Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 55a39c0

Browse filesBrowse files
Okhoshinicolas-grekas
authored andcommitted
[DependencyInjection] Fix autowiring tagged arguments from attributes
1 parent 67f43b0 commit 55a39c0
Copy full SHA for 55a39c0

File tree

7 files changed

+173
-41
lines changed
Filter options

7 files changed

+173
-41
lines changed

‎src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php
+6-33Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212
namespace Symfony\Component\DependencyInjection\Compiler;
1313

1414
use Symfony\Component\Config\Resource\ClassExistenceResource;
15-
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
16-
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
17-
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
18-
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
1915
use Symfony\Component\DependencyInjection\Attribute\Target;
2016
use Symfony\Component\DependencyInjection\ContainerBuilder;
2117
use Symfony\Component\DependencyInjection\Definition;
@@ -135,8 +131,7 @@ private function doProcessValue($value, bool $isRoot = false)
135131
array_unshift($this->methodCalls, [$constructor, $value->getArguments()]);
136132
}
137133

138-
$checkAttributes = 80000 <= \PHP_VERSION_ID && !$value->hasTag('container.ignore_attributes');
139-
$this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot, $checkAttributes);
134+
$this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot);
140135

141136
if ($constructor) {
142137
[, $arguments] = array_shift($this->methodCalls);
@@ -153,16 +148,14 @@ private function doProcessValue($value, bool $isRoot = false)
153148
return $value;
154149
}
155150

156-
private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array
151+
private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): array
157152
{
158153
$this->decoratedId = null;
159154
$this->decoratedClass = null;
160155
$this->getPreviousValue = null;
161156

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

168161
$patchedIndexes = [];
@@ -184,7 +177,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
184177
}
185178
}
186179

187-
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i);
180+
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $i);
188181

189182
if ($arguments !== $call[1]) {
190183
$this->methodCalls[$i][1] = $arguments;
@@ -227,7 +220,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
227220
*
228221
* @throws AutowiringFailedException
229222
*/
230-
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array
223+
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, int $methodIndex): array
231224
{
232225
$class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
233226
$method = $reflectionMethod->name;
@@ -246,26 +239,6 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
246239

247240
$type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, true);
248241

249-
if ($checkAttributes) {
250-
foreach ($parameter->getAttributes() as $attribute) {
251-
if (TaggedIterator::class === $attribute->getName()) {
252-
$attribute = $attribute->newInstance();
253-
$arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute);
254-
break;
255-
}
256-
257-
if (TaggedLocator::class === $attribute->getName()) {
258-
$attribute = $attribute->newInstance();
259-
$arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, null, true));
260-
break;
261-
}
262-
}
263-
264-
if ('' !== ($arguments[$index] ?? '')) {
265-
continue;
266-
}
267-
}
268-
269242
if (!$type) {
270243
if (isset($arguments[$index])) {
271244
continue;
+132Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
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\Compiler;
13+
14+
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
15+
use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
16+
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
17+
use Symfony\Component\DependencyInjection\Attribute\TaggedLocator;
18+
use Symfony\Component\DependencyInjection\ContainerBuilder;
19+
use Symfony\Component\DependencyInjection\Definition;
20+
use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException;
21+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
22+
23+
final class AutowireTaggedArgumentsPass extends AbstractRecursivePass
24+
{
25+
public function process(ContainerBuilder $container): void
26+
{
27+
if (\PHP_VERSION_ID < 80000) {
28+
return;
29+
}
30+
31+
parent::process($container);
32+
}
33+
34+
protected function processValue($value, bool $isRoot = false)
35+
{
36+
$value = parent::processValue($value, $isRoot);
37+
38+
if (!$value instanceof Definition
39+
|| !$value->isAutowired()
40+
|| $value->isAbstract()
41+
|| $value->hasTag('container.ignore_attributes')
42+
|| !($reflectionClass = $this->container->getReflectionClass($value->getClass(), false))
43+
) {
44+
return $value;
45+
}
46+
47+
$methodCalls = $value->getMethodCalls();
48+
49+
try {
50+
if ($constructor = $this->getConstructor($value, false)) {
51+
array_unshift($methodCalls, [$constructor, $value->getArguments()]);
52+
}
53+
} catch (RuntimeException $e) {
54+
throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e);
55+
}
56+
57+
$methodCalls = $this->autowireCalls($methodCalls, $reflectionClass, $isRoot);
58+
59+
if ($constructor) {
60+
[, $arguments] = array_shift($methodCalls);
61+
62+
if ($arguments !== $value->getArguments()) {
63+
$value->setArguments($arguments);
64+
}
65+
}
66+
67+
if ($methodCalls !== $value->getMethodCalls()) {
68+
$value->setMethodCalls($methodCalls);
69+
}
70+
71+
return $value;
72+
}
73+
74+
private function autowireCalls(array $methodCalls, \ReflectionClass $reflectionClass, bool $isRoot): array
75+
{
76+
foreach ($methodCalls as $i => $call) {
77+
[$method, $arguments] = $call;
78+
79+
if ($method instanceof \ReflectionFunctionAbstract) {
80+
$reflectionMethod = $method;
81+
} else {
82+
$definition = new Definition($reflectionClass->name);
83+
try {
84+
$reflectionMethod = $this->getReflectionMethod($definition, $method);
85+
} catch (RuntimeException $e) {
86+
if ($definition->getFactory()) {
87+
continue;
88+
}
89+
throw $e;
90+
}
91+
}
92+
93+
$arguments = $this->autowireMethod($reflectionMethod, $arguments);
94+
95+
if ($arguments !== $call[1]) {
96+
$methodCalls[$i][1] = $arguments;
97+
}
98+
}
99+
100+
return $methodCalls;
101+
}
102+
103+
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments): array
104+
{
105+
$parameters = $reflectionMethod->getParameters();
106+
if ($reflectionMethod->isVariadic()) {
107+
array_pop($parameters);
108+
}
109+
110+
foreach ($parameters as $index => $parameter) {
111+
if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) {
112+
continue;
113+
}
114+
115+
foreach ($parameter->getAttributes() as $attribute) {
116+
if (TaggedIterator::class === $attribute->getName()) {
117+
$attribute = $attribute->newInstance();
118+
$arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute);
119+
break;
120+
}
121+
122+
if (TaggedLocator::class === $attribute->getName()) {
123+
$attribute = $attribute->newInstance();
124+
$arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, null, true));
125+
break;
126+
}
127+
}
128+
}
129+
130+
return $arguments;
131+
}
132+
}

‎src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ public function __construct()
6262
new AutowireRequiredMethodsPass(),
6363
new AutowireRequiredPropertiesPass(),
6464
new ResolveBindingsPass(),
65-
new CheckDefinitionValidityPass(),
66-
new AutowirePass(false),
65+
new AutowireTaggedArgumentsPass(),
6766
new ServiceLocatorTagPass(),
6867
new DecoratorServicePass(),
68+
new CheckDefinitionValidityPass(),
69+
new AutowirePass(false),
6970
new ResolveTaggedIteratorArgumentPass(),
7071
new ResolveServiceSubscribersPass(),
7172
new ResolveReferencesToAliasesPass(),

‎src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -985,8 +985,8 @@ public function testAutowireDecorator()
985985
->setAutowired(true)
986986
;
987987

988-
(new AutowirePass())->process($container);
989988
(new DecoratorServicePass())->process($container);
989+
(new AutowirePass())->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 AutowirePass())->process($container);
10121011
(new DecoratorServicePass())->process($container);
1012+
(new AutowirePass())->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 AutowirePass())->process($container);
10301029
(new DecoratorServicePass())->process($container);
1030+
(new AutowirePass())->process($container);
10311031

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

1047+
(new DecoratorServicePass())->process($container);
10471048
try {
10481049
(new AutowirePass())->process($container);
10491050
$this->fail('AutowirePass should have thrown an exception');
10501051
} catch (AutowiringFailedException $e) {
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());
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());
10521053
}
10531054
}
10541055

‎src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Compiler/IntegrationTest.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,33 @@ public function testCanDecorateServiceLocator()
175175
$this->assertSame($container->get('foo'), $container->get(DecoratedServiceLocator::class)->get('foo'));
176176
}
177177

178+
public function testAliasDecoratedService()
179+
{
180+
$container = new ContainerBuilder();
181+
182+
$container->register('service', ServiceLocator::class)
183+
->setPublic(true)
184+
->setArguments([[]])
185+
;
186+
$container->register('decorator', DecoratedServiceLocator::class)
187+
->setDecoratedService('service')
188+
->setAutowired(true)
189+
->setPublic(true)
190+
;
191+
$container->setAlias(ServiceLocator::class, 'decorator.inner')
192+
->setPublic(true)
193+
;
194+
$container->register('user_service', DecoratedServiceLocator::class)
195+
->setAutowired(true)
196+
;
197+
198+
$container->compile();
199+
200+
$this->assertInstanceOf(DecoratedServiceLocator::class, $container->get('service'));
201+
$this->assertInstanceOf(ServiceLocator::class, $container->get(ServiceLocator::class));
202+
$this->assertSame($container->get('service'), $container->get('decorator'));
203+
}
204+
178205
/**
179206
* @dataProvider getYamlCompileTests
180207
*/

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber.php
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public function getRemovedIds(): array
4646
return [
4747
'.service_locator.DlIAmAe' => true,
4848
'.service_locator.DlIAmAe.foo_service' => true,
49-
'.service_locator.t5IGRMW' => true,
5049
'Psr\\Container\\ContainerInterface' => true,
5150
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
5251
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true,

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber_php81.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services_subscriber_php81.php
-1Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ public function getRemovedIds(): array
4646
return [
4747
'.service_locator.JmEob1b' => true,
4848
'.service_locator.JmEob1b.foo_service' => true,
49-
'.service_locator.KIgkoLM' => true,
5049
'Psr\\Container\\ContainerInterface' => true,
5150
'Symfony\\Component\\DependencyInjection\\ContainerInterface' => true,
5251
'Symfony\\Component\\DependencyInjection\\Tests\\Fixtures\\CustomDefinition' => true,

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.