10000 [DI] Service decoration: autowire the inner service · symfony/symfony@8b057e1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8b057e1

Browse files
committed
[DI] Service decoration: autowire the inner service
1 parent 1364089 commit 8b057e1

File tree

5 files changed

+145
-0
lines changed

5 files changed

+145
-0
lines changed

src/Symfony/Component/DependencyInjection/Compiler/DecoratorServicePass.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313

1414
use Symfony\Component\DependencyInjection\ContainerBuilder;
1515
use Symfony\Component\DependencyInjection\Alias;
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
use Symfony\Component\DependencyInjection\TypedReference;
1620

1721
/**
1822
* Overwrites a service but keeps the overridden one.
1923
*
2024
* @author Christophe Coevoet <stof@notk.org>
2125
* @author Fabien Potencier <fabien@symfony.com>
2226
* @author Diego Saint Esteben <diego@saintesteben.me>
27+
* @author Kévin Dunglas <dunglas@gmail.com>
2328
*/
2429
class DecoratorServicePass implements CompilerPassInterface
2530
{
@@ -62,6 +67,38 @@ public function process(ContainerBuilder $container)
6267
}
6368

6469
$container->setAlias($inner, $id)->setPublic($public)->setPrivate($private);
70+
$this->autowire($container, $definition, $renamedId);
71+
}
72+
}
73+
74+
private function autowire(ContainerBuilder $container, Definition $definition, string $renamedId): void
75+
{
76+
if (!$definition->isAutowired() ||
77+
null === ($innerClass = $container->findDefinition($renamedId)->getClass()) ||
78+
!($reflectionClass = $container->getReflectionClass($definition->getClass())) ||
79+
!$constructor = $reflectionClass->getConstructor()
80+
) {
81+
return;
82+
}
83+
84+
$innerIndex = null;
85+
foreach ($constructor->getParameters() as $index => $parameter) {
86+
if (null === ($type = ProxyHelper::getTypeHint($constructor, $parameter, true)) ||
87+
!is_a($innerClass, $type, true)
88+
) {
89+
continue;
90+
}
91+
92+
if (null !== $innerIndex) {
93+
// There is more than one argument of the type of the decorated class
94+
return;
95+
}
96+
97+
$innerIndex = $index;
98+
}
99+
100+
if (null !== $innerIndex) {
101+
$definition->setArgument($innerIndex, new TypedReference($renamedId, $innerClass));
65102
}
66103
}
67104
}

src/Symfony/Component/DependencyInjection/Tests/Compiler/DecoratorServicePassTest.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Symfony\Component\DependencyInjection\Alias;
1616
use Symfony\Component\DependencyInjection\ContainerBuilder;
1717
use Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass;
18+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar;
19+
use Symfony\Component\DependencyInjection\Tests\Fixtures\BarDecorator;
20+
use Symfony\Component\DependencyInjection\Tests\Fixtures\NonAutowirableBarDecorator;
1821

1922
class DecoratorServicePassTest extends TestCase
2023
{
@@ -144,6 +147,53 @@ public function testProcessMovesTagsFromDecoratedDefinitionToDecoratingDefinitio
144147
$this->assertEquals(array('bar' => array('attr' => 'baz'), 'foobar' => array('attr' => 'bar')), $container->getDefinition('baz')->getTags());
145148
}
146149

150+
public function testAutowire()
151+
{
152+
$container = new ContainerBuilder();
153+
$container->register(Bar::class, Bar::class);
154+
$container
155+
->register(BarDecorator::class, BarDecorator::class)
156+
->setDecoratedService(Bar::class)
157+
->setAutowired(true)
158+
;
159+
160+
$this->process($container);
161+
162+
$definition = $container->getDefinition(BarDecorator::class);
163+
$this->assertCount(1, $definition->getArguments(), 'The "$logger" argument must not be autowired.');
164+
$this->assertSame('Symfony\Component\DependencyInjection\Tests\Fixtures\BarDecorator.inner', (string) $definition->getArgument(1));
165+
}
166+
167+
public function testDoNotAutowireWhenSeveralArgumentOfTheType()
168+
{
169+
$container = new ContainerBuilder();
170+
$container->register(Bar::class, Bar::class);
171+
$container
172+
->register(NonAutowirableBarDecorator::class, NonAutowirableBarDecorator::class)
173+
->setDecoratedService(Bar::class)
174+
->setAutowired(true)
175+
;
176+
177+
$this->process($container);
178+
179+
$this->assertEmpty($container->getDefinition(NonAutowirableBarDecorator::class)->getArguments());
180+
}
181+
182+
public function testDoNotAutowireWhenNoConstructor()
183+
{
184+
$container = new ContainerBuilder();
185+
$container->register(Bar::class, Bar::class);
186+
$container
187+
->register(NoConstructor::class, NoConstructor::class)
188+
->setDecoratedService(Bar::class)
189+
->setAutowired(true)
190+
;
191+
192+
$this->process($container);
193+
194+
$this->assertEmpty($container->getDefinition(NoConstructor::class)->getArguments());
195+
}
196+
147197
protected function process(ContainerBuilder $container)
148198
{
149199
$repeatedPass = new DecoratorServicePass();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
13+
14+
use Psr\Log\LoggerInterface;
15+
16+
class BarDecorator implements BarInterface
17+
{
18+
public function __construct(LoggerInterface $logger, BarInterface $decorated)
19+
{
20+
}
21+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
13+
14+
class NoConstructor
15+
{
16+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Tests\Fixtures;
13+
14+
use Psr\Log\LoggerInterface;
15+
16+
class NonAutowirableBarDecorator implements BarInterface
17+
{
18+
public function __construct(LoggerInterface $logger, BarInterface $decorated1, BarInterface $decorated2)
19+
{
20+
}
21+
}

0 commit comments

Comments
 (0)
0