From 64f788d825d6d0b8fe25ad3111ee1749d0d9f86b Mon Sep 17 00:00:00 2001 From: HypeMC Date: Tue, 1 Aug 2023 21:26:45 +0200 Subject: [PATCH] [DependencyInjection][HttpKernel] Fix using `#[AutowireCallable]` with controller arguments --- .../Attribute/AutowireCallable.php | 9 +++++++++ .../DependencyInjection/Compiler/AutowirePass.php | 5 +---- .../RegisterControllerArgumentLocatorsPass.php | 8 +++++++- .../RegisterControllerArgumentLocatorsPassTest.php | 14 +++++++++++++- src/Symfony/Component/HttpKernel/composer.json | 4 ++-- 5 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php b/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php index c472cb776e2bb..87e119746d84d 100644 --- a/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php +++ b/src/Symfony/Component/DependencyInjection/Attribute/AutowireCallable.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Attribute; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Exception\LogicException; use Symfony\Component\DependencyInjection\Reference; @@ -38,4 +39,12 @@ public function __construct( parent::__construct($callable ?? [new Reference($service), $method ?? '__invoke'], lazy: $lazy); } + + public function buildDefinition(mixed $value, ?string $type, \ReflectionParameter $parameter): Definition + { + return (new Definition($type = \is_string($this->lazy) ? $this->lazy : ($type ?: 'Closure'))) + ->setFactory(['Closure', 'fromCallable']) + ->setArguments([\is_array($value) ? $value + [1 => '__invoke'] : $value]) + ->setLazy($this->lazy || 'Closure' !== $type && 'callable' !== (string) $parameter->getType()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index f84a7faff07ec..f0a58c2b06197 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -314,10 +314,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } if ($attribute instanceof AutowireCallable) { - $value = (new Definition($type = \is_string($attribute->lazy) ? $attribute->lazy : ($type ?: 'Closure'))) - ->setFactory(['Closure', 'fromCallable']) - ->setArguments([\is_array($value) ? $value + [1 => '__invoke'] : $value]) - ->setLazy($attribute->lazy || 'Closure' !== $type && 'callable' !== (string) $parameter->getType()); + $value = $attribute->buildDefinition($value, $type, $parameter); } elseif ($lazy = $attribute->lazy) { $definition = (new Definition($type)) ->setFactory('current') diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php index d0e05340d8c6a..d43c6a3aef11f 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php +++ b/src/Symfony/Component/HttpKernel/DependencyInjection/RegisterControllerArgumentLocatorsPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\HttpKernel\DependencyInjection; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\Attribute\AutowireCallable; use Symfony\Component\DependencyInjection\Attribute\Target; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -160,7 +161,12 @@ public function process(ContainerBuilder $container) } if ($autowireAttributes) { - $value = $autowireAttributes[0]->newInstance()->value; + $attribute = $autowireAttributes[0]->newInstance(); + $value = $parameterBag->resolveValue($attribute->value); + + if ($attribute instanceof AutowireCallable) { + $value = $attribute->buildDefinition($value, $type, $p); + } if ($value instanceof Reference) { $args[$p->name] = $type ? new TypedReference($value, $type, $invalidBehavior, $p->name) : new Reference($value, $invalidBehavior); diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php index cc2f37b9711cb..82577d27570fe 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/RegisterControllerArgumentLocatorsPassTest.php @@ -12,9 +12,11 @@ namespace Symfony\Component\HttpKernel\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\Argument\LazyClosure; use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\Attribute\Autowire; +use Symfony\Component\DependencyInjection\Attribute\AutowireCallable; use Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use Symfony\Component\DependencyInjection\Attribute\TaggedLocator; use Symfony\Component\DependencyInjection\Attribute\Target; @@ -481,7 +483,7 @@ public function testAutowireAttribute() $locator = $container->get($locatorId)->get('foo::fooAction'); - $this->assertCount(8, $locator->getProvidedServices()); + $this->assertCount(9, $locator->getProvidedServices()); $this->assertInstanceOf(\stdClass::class, $locator->get('service1')); $this->assertSame('foo/bar', $locator->get('value')); $this->assertSame('foo', $locator->get('expression')); @@ -490,6 +492,9 @@ public function testAutowireAttribute() $this->assertSame('bar', $locator->get('rawValue')); $this->assertSame('@bar', $locator->get('escapedRawValue')); $this->assertSame('foo', $locator->get('customAutowire')); + $this->assertInstanceOf(FooInterface::class, $autowireCallable = $locator->get('autowireCallable')); + $this->assertInstanceOf(LazyClosure::class, $autowireCallable); + $this->assertInstanceOf(\stdClass::class, $autowireCallable->service); $this->assertFalse($locator->has('service2')); } @@ -625,6 +630,11 @@ public function __construct(string $parameter) } } +interface FooInterface +{ + public function foo(); +} + class WithAutowireAttribute { public function fooAction( @@ -644,6 +654,8 @@ public function fooAction( string $escapedRawValue, #[CustomAutowire('some.parameter')] string $customAutowire, + #[AutowireCallable(service: 'some.id', method: 'bar')] + FooInterface $autowireCallable, #[Autowire(service: 'invalid.id')] \stdClass $service2 = null, ) { diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index bf8be24d05ab4..7a989c40bd957 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -30,7 +30,7 @@ "symfony/config": "^6.1", "symfony/console": "^5.4|^6.0", "symfony/css-selector": "^5.4|^6.0", - "symfony/dependency-injection": "^6.3", + "symfony/dependency-injection": "^6.3.4", "symfony/dom-crawler": "^5.4|^6.0", "symfony/expression-language": "^5.4|^6.0", "symfony/finder": "^5.4|^6.0", @@ -57,7 +57,7 @@ "symfony/config": "<6.1", "symfony/console": "<5.4", "symfony/form": "<5.4", - "symfony/dependency-injection": "<6.3", + "symfony/dependency-injection": "<6.3.4", "symfony/doctrine-bridge": "<5.4", "symfony/http-client": "<5.4", "symfony/http-client-contracts": "<2.5",