Skip to content

Commit d5936a3

Browse files
[DependencyInjection] Make AutowirePass reentrant
1 parent 30f1c60 commit d5936a3

File tree

1 file changed

+42
-44
lines changed

1 file changed

+42
-44
lines changed

Compiler/AutowirePass.php

Lines changed: 42 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,8 @@ class AutowirePass extends AbstractRecursivePass
4444
private bool $throwOnAutowiringException;
4545
private ?string $decoratedClass = null;
4646
private ?string $decoratedId = null;
47-
private ?array $methodCalls = null;
4847
private object $defaultArgument;
49-
private ?\Closure $getPreviousValue = null;
50-
private ?int $decoratedMethodIndex = null;
51-
private ?int $decoratedMethodArgumentIndex = null;
48+
private ?\Closure $restorePreviousValue = null;
5249
private ?self $typesClone = null;
5350

5451
public function __construct(bool $throwOnAutowireException = true)
@@ -79,12 +76,9 @@ public function process(ContainerBuilder $container): void
7976
} finally {
8077
$this->decoratedClass = null;
8178
$this->decoratedId = null;
82-
$this->methodCalls = null;
8379
$this->defaultArgument->bag = null;
8480
$this->defaultArgument->names = null;
85-
$this->getPreviousValue = null;
86-
$this->decoratedMethodIndex = null;
87-
$this->decoratedMethodArgumentIndex = null;
81+
$this->restorePreviousValue = null;
8882
$this->typesClone = null;
8983
}
9084
}
@@ -155,7 +149,7 @@ private function doProcessValue(mixed $value, bool $isRoot = false): mixed
155149
return $value;
156150
}
157151

158-
$this->methodCalls = $value->getMethodCalls();
152+
$methodCalls = $value->getMethodCalls();
159153

160154
try {
161155
$constructor = $this->getConstructor($value, false);
@@ -164,40 +158,42 @@ private function doProcessValue(mixed $value, bool $isRoot = false): mixed
164158
}
165159

166160
if ($constructor) {
167-
array_unshift($this->methodCalls, [$constructor, $value->getArguments()]);
161+
array_unshift($methodCalls, [$constructor, $value->getArguments()]);
168162
}
169163

170164
$checkAttributes = !$value->hasTag('container.ignore_attributes');
171-
$this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot, $checkAttributes);
165+
$methodCalls = $this->autowireCalls($methodCalls, $reflectionClass, $isRoot, $checkAttributes);
172166

173167
if ($constructor) {
174-
[, $arguments] = array_shift($this->methodCalls);
168+
[, $arguments] = array_shift($methodCalls);
175169

176170
if ($arguments !== $value->getArguments()) {
177171
$value->setArguments($arguments);
178172
}
179173
}
180174

181-
if ($this->methodCalls !== $value->getMethodCalls()) {
182-
$value->setMethodCalls($this->methodCalls);
175+
if ($methodCalls !== $value->getMethodCalls()) {
176+
$value->setMethodCalls($methodCalls);
183177
}
184178

185179
return $value;
186180
}
187181

188-
private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array
182+
private function autowireCalls(array $methodCalls, \ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array
189183
{
190-
$this->decoratedId = null;
191-
$this->decoratedClass = null;
192-
$this->getPreviousValue = null;
184+
if ($isRoot) {
185+
$this->decoratedId = null;
186+
$this->decoratedClass = null;
187+
$this->restorePreviousValue = null;
193188

194-
if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) {
195-
$this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass();
189+
if (($definition = $this->container->getDefinition($this->currentId)) && null !== ($this->decoratedId = $definition->innerServiceId) && $this->container->has($this->decoratedId)) {
190+
$this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass();
191+
}
196192
}
197193

198194
$patchedIndexes = [];
199195

200-
foreach ($this->methodCalls as $i => $call) {
196+
foreach ($methodCalls as $i => $call) {
201197
[$method, $arguments] = $call;
202198

203199
if ($method instanceof \ReflectionFunctionAbstract) {
@@ -214,18 +210,18 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
214210
}
215211
}
216212

217-
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i);
213+
$arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes);
218214

219215
if ($arguments !== $call[1]) {
220-
$this->methodCalls[$i][1] = $arguments;
216+
$methodCalls[$i][1] = $arguments;
221217
$patchedIndexes[] = $i;
222218
}
223219
}
224220

225221
// use named arguments to skip complex default values
226222
foreach ($patchedIndexes as $i) {
227223
$namedArguments = null;
228-
$arguments = $this->methodCalls[$i][1];
224+
$arguments = $methodCalls[$i][1];
229225

230226
foreach ($arguments as $j => $value) {
231227
if ($namedArguments && !$value instanceof $this->defaultArgument) {
@@ -248,29 +244,30 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot,
248244
}
249245
}
250246

251-
$this->methodCalls[$i][1] = $arguments;
247+
$methodCalls[$i][1] = $arguments;
252248
}
253249

254-
return $this->methodCalls;
250+
return $methodCalls;
255251
}
256252

257253
/**
258254
* Autowires the constructor or a method.
259255
*
260256
* @throws AutowiringFailedException
261257
*/
262-
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array
258+
private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes): array
263259
{
264260
$class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId;
265261
$method = $reflectionMethod->name;
266262
$parameters = $reflectionMethod->getParameters();
267263
if ($reflectionMethod->isVariadic()) {
268264
array_pop($parameters);
269265
}
270-
$this->defaultArgument->names = new \ArrayObject();
266+
$defaultArgument = clone $this->defaultArgument;
267+
$defaultArgument->names = new \ArrayObject();
271268

272269
foreach ($parameters as $index => $parameter) {
273-
$this->defaultArgument->names[$index] = $parameter->name;
270+
$defaultArgument->names[$index] = $parameter->name;
274271

275272
if (\array_key_exists($parameter->name, $arguments)) {
276273
$arguments[$index] = $arguments[$parameter->name];
@@ -284,15 +281,16 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
284281
$target = null;
285282
$name = Target::parseName($parameter, $target);
286283
$target = $target ? [$target] : [];
284+
$currentId = $this->currentId;
287285

288-
$getValue = function () use ($type, $parameter, $class, $method, $name, $target) {
286+
$getValue = function () use ($type, $parameter, $class, $method, $name, $target, $defaultArgument, $currentId) {
289287
if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $name, $target), false)) {
290-
$failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method));
288+
$failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $currentId ? $class.'::'.$method : $method));
291289

292290
if ($parameter->isDefaultValueAvailable()) {
293-
$value = $this->defaultArgument->withValue($parameter);
291+
$value = $defaultArgument->withValue($parameter);
294292
} elseif (!$parameter->allowsNull()) {
295-
throw new AutowiringFailedException($this->currentId, $failureMessage);
293+
throw new AutowiringFailedException($currentId, $failureMessage);
296294
}
297295
}
298296

@@ -316,14 +314,15 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
316314
if (!$parameter->isDefaultValueAvailable()) {
317315
throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e);
318316
}
319-
$arguments[$index] = clone $this->defaultArgument;
317+
$arguments[$index] = clone $defaultArgument;
320318
$arguments[$index]->value = $parameter->getDefaultValue();
321319

322320
continue 2;
323321
}
324322

325323
if ($attribute instanceof AutowireCallable) {
326324
$value = $attribute->buildDefinition($value, $type, $parameter);
325+
$value = $this->doProcessValue($value);
327326
} elseif ($lazy = $attribute->lazy) {
328327
$definition = (new Definition($type))
329328
->setFactory('current')
@@ -385,25 +384,24 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
385384
}
386385

387386
// specifically pass the default value
388-
$arguments[$index] = $this->defaultArgument->withValue($parameter);
387+
$arguments[$index] = $defaultArgument->withValue($parameter);
389388

390389
continue;
391390
}
392391

393392
if ($this->decoratedClass && is_a($this->decoratedClass, $type, true)) {
394-
if ($this->getPreviousValue) {
393+
if ($this->restorePreviousValue) {
395394
// The inner service is injected only if there is only 1 argument matching the type of the decorated class
396395
// across all arguments of all autowired methods.
397396
// If a second matching argument is found, the default behavior is restored.
398-
399-
$getPreviousValue = $this->getPreviousValue;
400-
$this->methodCalls[$this->decoratedMethodIndex][1][$this->decoratedMethodArgumentIndex] = $getPreviousValue();
401-
$this->decoratedClass = null; // Prevent further checks
397+
($this->restorePreviousValue)();
398+
$this->decoratedClass = $this->restorePreviousValue = null; // Prevent further checks
402399
} else {
403400
$arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass);
404-
$this->getPreviousValue = $getValue;
405-
$this->decoratedMethodIndex = $methodIndex;
406-
$this->decoratedMethodArgumentIndex = $index;
401+
$argumentAtIndex = &$arguments[$index];
402+
$this->restorePreviousValue = static function () use (&$argumentAtIndex, $getValue) {
403+
$argumentAtIndex = $getValue();
404+
};
407405

408406
continue;
409407
}
@@ -414,7 +412,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a
414412

415413
if ($parameters && !isset($arguments[++$index])) {
416414
while (0 <= --$index) {
417-
if (!$arguments[$index] instanceof $this->defaultArgument) {
415+
if (!$arguments[$index] instanceof $defaultArgument) {
418416
break;
419417
}
420418
unset($arguments[$index]);

0 commit comments

Comments
 (0)