Skip to content

Commit 999cfcf

Browse files
committed
Fix circular reference in autowired decorators
1 parent a665946 commit 999cfcf

File tree

3 files changed

+31
-7
lines changed

3 files changed

+31
-7
lines changed

Compiler/AutowirePass.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
153153
$this->decoratedClass = null;
154154
$this->getPreviousValue = null;
155155

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

160162
foreach ($this->methodCalls as $i => $call) {

Tests/Compiler/AutowirePassTest.php

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -985,8 +985,8 @@ public function testAutowireDecorator()
985985
->setAutowired(true)
986986
;
987987

988-
(new DecoratorServicePass())->process($container);
989988
(new AutowirePass())->process($container);
989+
(new DecoratorServicePass())->process($container);
990990

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

1011-
(new DecoratorServicePass())->process($container);
10121011
(new AutowirePass())->process($container);
1012+
(new DecoratorServicePass())->process($container);
10131013

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

1029-
(new DecoratorServicePass())->process($container);
10301029
(new AutowirePass())->process($container);
1030+
(new DecoratorServicePass())->process($container);
10311031

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

1047-
(new DecoratorServicePass())->process($container);
10481047
try {
10491048
(new AutowirePass())->process($container);
10501049
$this->fail('AutowirePass should have thrown an exception');
10511050
} catch (AutowiringFailedException $e) {
1052-
$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());
1051+
$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());
10531052
}
10541053
}
10551054

@@ -1106,4 +1105,23 @@ public function testArgumentWithTarget()
11061105

11071106
$this->assertSame(BarInterface::class.' $imageStorage', (string) $container->getDefinition('with_target')->getArgument(0));
11081107
}
1108+
1109+
public function testDecorationWithServiceAndAliasedInterface()
1110+
{
1111+
$container = new ContainerBuilder();
1112+
1113+
$container->register(DecoratorImpl::class, DecoratorImpl::class)
1114+
->setAutowired(true)
1115+
->setPublic(true);
1116+
$container->setAlias(DecoratorInterface::class, DecoratorImpl::class)->setPublic(true);
1117+
$container->register(DecoratedDecorator::class, DecoratedDecorator::class)
1118+
->setAutowired(true)
1119+
->setPublic(true)
1120+
->setDecoratedService(DecoratorImpl::class);
1121+
1122+
$container->compile();
1123+
1124+
static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorInterface::class));
1125+
static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorImpl::class));
1126+
}
11091127
}

Tests/Fixtures/includes/autowiring_classes.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ interface DecoratorInterface
382382
{
383383
}
384384

385+
class DecoratorImpl implements DecoratorInterface
386+
{
387+
}
388+
385389
class Decorated implements DecoratorInterface
386390
{
387391
public function __construct($quz = null, \NonExistent $nonExistent = null, DecoratorInterface $decorated = null, array $foo = [])

0 commit comments

Comments
 (0)