From cad7fbb9f78456b60505e010eaeff50189d104fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morel=20Se=CC=81bastien?= Date: Mon, 2 Dec 2019 15:03:16 -0800 Subject: [PATCH] [DI] Autowire public typed properties --- .../DependencyInjection/CHANGELOG.md | 5 ++ .../AutowireRequiredPropertiesPass.php | 61 +++++++++++++++++++ .../Compiler/PassConfig.php | 1 + .../AutowireRequiredPropertiesPassTest.php | 46 ++++++++++++++ .../includes/autowiring_classes_74.php | 15 +++++ 5 files changed, 128 insertions(+) create mode 100644 src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php create mode 100644 src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index b0432200431cb..4c6e3671fa0fb 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -1,6 +1,11 @@ CHANGELOG ========= +5.1.0 +----- + + * added support to autowire public typed properties in php 7.4 + 5.0.0 ----- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php new file mode 100644 index 0000000000000..934bde8dd1fe8 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireRequiredPropertiesPass.php @@ -0,0 +1,61 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\TypedReference; + +/** + * Looks for definitions with autowiring enabled and registers their corresponding "@required" properties. + * + * @author Sebastien Morel (Plopix) + * @author Nicolas Grekas + */ +class AutowireRequiredPropertiesPass extends AbstractRecursivePass +{ + /** + * {@inheritdoc} + */ + protected function processValue($value, bool $isRoot = false) + { + if (\PHP_VERSION_ID < 70400) { + return $value; + } + $value = parent::processValue($value, $isRoot); + + if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { + return $value; + } + if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) { + return $value; + } + + $properties = $value->getProperties(); + foreach ($reflectionClass->getProperties() as $reflectionProperty) { + if (false === $doc = $reflectionProperty->getDocComment()) { + continue; + } + if (false === stripos($doc, '@required') || !preg_match('#(?:^/\*\*|\n\s*+\*)\s*+@required(?:\s|\*/$)#i', $doc)) { + continue; + } + if (\array_key_exists($name = $reflectionProperty->getName(), $properties)) { + continue; + } + + $type = $reflectionProperty->getType()->getName(); + $value->setProperty($name, new TypedReference($type, $type, ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, $name)); + } + + return $value; + } +} diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 8b5cfb683d882..5bbac05d530f9 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -57,6 +57,7 @@ public function __construct() new ResolveFactoryClassPass(), new ResolveNamedArgumentsPass(), new AutowireRequiredMethodsPass(), + new AutowireRequiredPropertiesPass(), new ResolveBindingsPass(), new ServiceLocatorTagPass(), new CheckDefinitionValidityPass(), diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php new file mode 100644 index 0000000000000..241daaaff3358 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireRequiredPropertiesPassTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Tests\Compiler; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Compiler\AutowireRequiredPropertiesPass; +use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php'; + +if (\PHP_VERSION_ID >= 70400) { + require_once __DIR__.'/../Fixtures/includes/autowiring_classes_74.php'; +} + +/** + * @requires PHP 7.4 + */ +class AutowireRequiredPropertiesPassTest extends TestCase +{ + public function testInjection() + { + $container = new ContainerBuilder(); + $container->register(Bar::class); + $container->register(A::class); + $container->register(B::class); + $container->register(PropertiesInjection::class)->setAutowired(true); + + (new ResolveClassPass())->process($container); + (new AutowireRequiredPropertiesPass())->process($container); + + $properties = $container->getDefinition(PropertiesInjection::class)->getProperties(); + + $this->assertArrayHasKey('plop', $properties); + $this->assertEquals(Bar::class, (string) $properties['plop']); + } +} diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php new file mode 100644 index 0000000000000..f1d76f2f0c788 --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_74.php @@ -0,0 +1,15 @@ +