Skip to content

Commit

Permalink
[DependencyInjection] Fix autowiring tagged arguments from attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
Okhoshi authored and nicolas-grekas committed Oct 22, 2021
1 parent dd50a90 commit be833dd
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 9 deletions.
6 changes: 2 additions & 4 deletions Compiler/AutowirePass.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,8 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
$this->decoratedClass = null;
$this->getPreviousValue = null;

if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && ($decoratedDefinition = $definition->getDecoratedService()) && null !== ($innerId = $decoratedDefinition[0]) && $this->container->has($innerId)) {
// If the class references to itself and is decorated, provide the inner service id and class to not get a circular reference
$this->decoratedClass = $this->container->findDefinition($innerId)->getClass();
$this->decoratedId = $decoratedDefinition[1] ?? $this->currentId.'.inner';
if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) {
$this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass();
}

$patchedIndexes = [];
Expand Down
3 changes: 2 additions & 1 deletion Compiler/PassConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,11 @@ public function __construct()
new AutowireRequiredMethodsPass(),
new AutowireRequiredPropertiesPass(),
new ResolveBindingsPass(),
new ServiceLocatorTagPass(),
new DecoratorServicePass(),
new CheckDefinitionValidityPass(),
new AutowirePass(false),
new ServiceLocatorTagPass(),
new DecoratorServicePass(),
new ResolveTaggedIteratorArgumentPass(),
new ResolveServiceSubscribersPass(),
new ResolveReferencesToAliasesPass(),
Expand Down
9 changes: 5 additions & 4 deletions Tests/Compiler/AutowirePassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -985,8 +985,8 @@ public function testAutowireDecorator()
->setAutowired(true)
;

(new AutowirePass())->process($container);
(new DecoratorServicePass())->process($container);
(new AutowirePass())->process($container);

$definition = $container->getDefinition(Decorator::class);
$this->assertSame(Decorator::class.'.inner', (string) $definition->getArgument(1));
Expand All @@ -1008,8 +1008,8 @@ public function testAutowireDecoratorChain()
->setAutowired(true)
;

(new AutowirePass())->process($container);
(new DecoratorServicePass())->process($container);
(new AutowirePass())->process($container);

$definition = $container->getDefinition(DecoratedDecorator::class);
$this->assertSame(DecoratedDecorator::class.'.inner', (string) $definition->getArgument(0));
Expand All @@ -1026,8 +1026,8 @@ public function testAutowireDecoratorRenamedId()
->setAutowired(true)
;

(new AutowirePass())->process($container);
(new DecoratorServicePass())->process($container);
(new AutowirePass())->process($container);

$definition = $container->getDefinition(Decorator::class);
$this->assertSame('renamed', (string) $definition->getArgument(1));
Expand All @@ -1044,11 +1044,12 @@ public function testDoNotAutowireDecoratorWhenSeveralArgumentOfTheType()
->setAutowired(true)
;

(new DecoratorServicePass())->process($container);
try {
(new AutowirePass())->process($container);
$this->fail('AutowirePass should have thrown an exception');
} catch (AutowiringFailedException $e) {
$this->assertSame('Cannot autowire service "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator": argument "$decorated1" of method "__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DecoratorInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "Symfony\Component\DependencyInjection\Tests\Compiler\Decorated", "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator".', (string) $e->getMessage());
$this->assertSame('Cannot autowire service "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator": argument "$decorated1" of method "__construct()" references interface "Symfony\Component\DependencyInjection\Tests\Compiler\DecoratorInterface" but no such service exists. You should maybe alias this interface to one of these existing services: "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator", "Symfony\Component\DependencyInjection\Tests\Compiler\NonAutowirableDecorator.inner".', (string) $e->getMessage());
}
}

Expand Down
27 changes: 27 additions & 0 deletions Tests/Compiler/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,33 @@ public function testCanDecorateServiceLocator()
$this->assertSame($container->get('foo'), $container->get(DecoratedServiceLocator::class)->get('foo'));
}

public function testAliasDecoratedService()
{
$container = new ContainerBuilder();

$container->register('service', ServiceLocator::class)
->setPublic(true)
->setArguments([[]])
;
$container->register('decorator', DecoratedServiceLocator::class)
->setDecoratedService('service')
->setAutowired(true)
->setPublic(true)
;
$container->setAlias(ServiceLocator::class, 'decorator.inner')
->setPublic(true)
;
$container->register('user_service', DecoratedServiceLocator::class)
->setAutowired(true)
;

$container->compile();

$this->assertInstanceOf(DecoratedServiceLocator::class, $container->get('service'));
$this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceLocator::class));
$this->assertSame($container->get('service'), $container->get('decorator'));
}

/**
* @dataProvider getYamlCompileTests
*/
Expand Down

0 comments on commit be833dd

Please sign in to comment.