From c463133308e532d604c4c4ee522ab3fa5d551199 Mon Sep 17 00:00:00 2001 From: Kevin Bond Date: Fri, 17 Feb 2023 15:59:43 -0500 Subject: [PATCH] [DI] allow extending `Autowire` attribute --- .../Component/DependencyInjection/CHANGELOG.md | 1 + .../DependencyInjection/Compiler/AutowirePass.php | 6 +++--- .../Tests/Compiler/AutowirePassTest.php | 6 ++++-- .../Fixtures/includes/autowiring_classes_80.php | 11 +++++++++++ src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../RegisterControllerArgumentLocatorsPass.php | 2 +- .../RegisterControllerArgumentLocatorsPassTest.php | 14 +++++++++++++- 7 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 9bcc904541389..7a6ef6b65836f 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * Enable deprecating parameters with `ContainerBuilder::deprecateParameter()` * Add `#[AsAlias]` attribute to tell under which alias a service should be registered or to use the implemented interface if no parameter is given * Allow to trim XML service parameters value by using `trim="true"` attribute + * Allow extending the `Autowire` attribute 6.2 --- diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 762f22d9dcf51..3bcaa812cf485 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -292,11 +292,11 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } if ($checkAttributes) { - foreach ($parameter->getAttributes() as $attribute) { - if (\in_array($attribute->getName(), [TaggedIterator::class, TaggedLocator::class, Autowire::class, MapDecorated::class], true)) { + foreach ([TaggedIterator::class, TaggedLocator::class, Autowire::class, MapDecorated::class] as $attributeClass) { + foreach ($parameter->getAttributes($attributeClass, Autowire::class === $attributeClass ? \ReflectionAttribute::IS_INSTANCEOF : 0) as $attribute) { $arguments[$index] = $this->processAttribute($attribute->newInstance(), $parameter->allowsNull()); - continue 2; + continue 3; } } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index ed7316fd707d1..bdf07b10ff077 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1235,7 +1235,7 @@ public function testAutowireAttribute() $definition = $container->getDefinition(AutowireAttribute::class); - $this->assertCount(9, $definition->getArguments()); + $this->assertCount(10, $definition->getArguments()); $this->assertEquals(new Reference('some.id'), $definition->getArgument(0)); $this->assertEquals(new Expression("parameter('some.parameter')"), $definition->getArgument(1)); $this->assertSame('foo/bar', $definition->getArgument(2)); @@ -1244,7 +1244,8 @@ public function testAutowireAttribute() $this->assertEquals(new Expression("parameter('some.parameter')"), $definition->getArgument(5)); $this->assertSame('bar', $definition->getArgument(6)); $this->assertSame('@bar', $definition->getArgument(7)); - $this->assertEquals(new Reference('invalid.id', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(8)); + $this->assertSame('foo', $definition->getArgument(8)); + $this->assertEquals(new Reference('invalid.id', ContainerInterface::NULL_ON_INVALID_REFERENCE), $definition->getArgument(9)); $container->compile(); @@ -1257,6 +1258,7 @@ public function testAutowireAttribute() $this->assertSame('foo', $service->expressionAsValue); $this->assertSame('bar', $service->rawValue); $this->assertSame('@bar', $service->escapedRawValue); + $this->assertSame('foo', $service->customAutowire); $this->assertNull($service->invalid); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php index c1f56eb1cffb4..863b33e7ce4b5 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Fixtures/includes/autowiring_classes_80.php @@ -33,6 +33,15 @@ class AutowireProperty public Foo $foo; } +#[\Attribute(\Attribute::TARGET_PARAMETER)] +class CustomAutowire extends Autowire +{ + public function __construct(string $parameter) + { + parent::__construct(param: $parameter); + } +} + class AutowireAttribute { public function __construct( @@ -52,6 +61,8 @@ public function __construct( public string $rawValue, #[Autowire('@@bar')] public string $escapedRawValue, + #[CustomAutowire('some.parameter')] + public string $customAutowire, #[Autowire(service: 'invalid.id')] public ?\stdClass $invalid = null, ) { diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 650bd9bc75b49..c70f632a5249c 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Add `#[WithHttpStatus]` for defining status codes for exceptions * Use an instance of `Psr\Clock\ClockInterface` to generate the current date time in `DateTimeValueResolver` * Add `#[WithLogLevel]` for defining log levels for exceptions + * Allow extending the `Autowire` attribute 6.2 --- diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index b294036d4eb98..d0e05340d8c6a 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -146,7 +146,7 @@ public function process(ContainerBuilder $container) $args[$p->name] = $bindingValue; continue; - } elseif (!$autowire || (!($autowireAttributes ??= $p->getAttributes(Autowire::class)) && (!$type || '\\' !== $target[0]))) { + } elseif (!$autowire || (!($autowireAttributes ??= $p->getAttributes(Autowire::class, \ReflectionAttribute::IS_INSTANCEOF)) && (!$type || '\\' !== $target[0]))) { continue; } elseif (is_subclass_of($type, \UnitEnum::class)) { // do not attempt to register enum typed arguments if not already present in bindings diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index 8420040366a79..13523d1d65e6d 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -482,7 +482,7 @@ public function testAutowireAttribute() $locator = $container->get($locatorId)->get('foo::fooAction'); - $this->assertCount(7, $locator->getProvidedServices()); + $this->assertCount(8, $locator->getProvidedServices()); $this->assertInstanceOf(\stdClass::class, $locator->get('service1')); $this->assertSame('foo/bar', $locator->get('value')); $this->assertSame('foo', $locator->get('expression')); @@ -490,6 +490,7 @@ public function testAutowireAttribute() $this->assertInstanceOf(\stdClass::class, $locator->get('expressionAsValue')); $this->assertSame('bar', $locator->get('rawValue')); $this->assertSame('@bar', $locator->get('escapedRawValue')); + $this->assertSame('foo', $locator->get('customAutowire')); $this->assertFalse($locator->has('service2')); } } @@ -580,6 +581,15 @@ public function fooAction(Response $response, ?Response $nullableResponse) } } +#[\Attribute(\Attribute::TARGET_PARAMETER)] +class CustomAutowire extends Autowire +{ + public function __construct(string $parameter) + { + parent::__construct(param: $parameter); + } +} + class WithAutowireAttribute { public function fooAction( @@ -597,6 +607,8 @@ public function fooAction( string $rawValue, #[Autowire('@@bar')] string $escapedRawValue, + #[CustomAutowire('some.parameter')] + string $customAutowire, #[Autowire(service: 'invalid.id')] \stdClass $service2 = null, ) {