From 02bf53b76d70efa6d5eca4f100bd3f077f67f00e Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Fri, 23 Feb 2024 11:03:28 -0500 Subject: [PATCH] [DependencyInjection] Make AutowirePass reentrant --- .../Compiler/AutowirePass.php | 86 +++++++++---------- 1 file changed, 42 insertions(+), 44 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index 41fa5052393f..155d1680da96 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -44,11 +44,8 @@ class AutowirePass extends AbstractRecursivePass private bool $throwOnAutowiringException; private ?string $decoratedClass = null; private ?string $decoratedId = null; - private ?array $methodCalls = null; private object $defaultArgument; - private ?\Closure $getPreviousValue = null; - private ?int $decoratedMethodIndex = null; - private ?int $decoratedMethodArgumentIndex = null; + private ?\Closure $restorePreviousValue = null; private ?self $typesClone = null; public function __construct(bool $throwOnAutowireException = true) @@ -79,12 +76,9 @@ public function process(ContainerBuilder $container): void } finally { $this->decoratedClass = null; $this->decoratedId = null; - $this->methodCalls = null; $this->defaultArgument->bag = null; $this->defaultArgument->names = null; - $this->getPreviousValue = null; - $this->decoratedMethodIndex = null; - $this->decoratedMethodArgumentIndex = null; + $this->restorePreviousValue = null; $this->typesClone = null; } } @@ -155,7 +149,7 @@ private function doProcessValue(mixed $value, bool $isRoot = false): mixed return $value; } - $this->methodCalls = $value->getMethodCalls(); + $methodCalls = $value->getMethodCalls(); try { $constructor = $this->getConstructor($value, false); @@ -164,40 +158,42 @@ private function doProcessValue(mixed $value, bool $isRoot = false): mixed } if ($constructor) { - array_unshift($this->methodCalls, [$constructor, $value->getArguments()]); + array_unshift($methodCalls, [$constructor, $value->getArguments()]); } $checkAttributes = !$value->hasTag('container.ignore_attributes'); - $this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot, $checkAttributes); + $methodCalls = $this->autowireCalls($methodCalls, $reflectionClass, $isRoot, $checkAttributes); if ($constructor) { - [, $arguments] = array_shift($this->methodCalls); + [, $arguments] = array_shift($methodCalls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); } } - if ($this->methodCalls !== $value->getMethodCalls()) { - $value->setMethodCalls($this->methodCalls); + if ($methodCalls !== $value->getMethodCalls()) { + $value->setMethodCalls($methodCalls); } return $value; } - private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array + private function autowireCalls(array $methodCalls, \ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes): array { - $this->decoratedId = null; - $this->decoratedClass = null; - $this->getPreviousValue = null; + if ($isRoot) { + $this->decoratedId = null; + $this->decoratedClass = null; + $this->restorePreviousValue = null; - 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(); + if (($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 = []; - foreach ($this->methodCalls as $i => $call) { + foreach ($methodCalls as $i => $call) { [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { @@ -214,10 +210,10 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, } } - $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i); + $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes); if ($arguments !== $call[1]) { - $this->methodCalls[$i][1] = $arguments; + $methodCalls[$i][1] = $arguments; $patchedIndexes[] = $i; } } @@ -225,7 +221,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, // use named arguments to skip complex default values foreach ($patchedIndexes as $i) { $namedArguments = null; - $arguments = $this->methodCalls[$i][1]; + $arguments = $methodCalls[$i][1]; foreach ($arguments as $j => $value) { if ($namedArguments && !$value instanceof $this->defaultArgument) { @@ -248,10 +244,10 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, } } - $this->methodCalls[$i][1] = $arguments; + $methodCalls[$i][1] = $arguments; } - return $this->methodCalls; + return $methodCalls; } /** @@ -259,7 +255,7 @@ private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, * * @throws AutowiringFailedException */ - private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex): array + private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes): array { $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; $method = $reflectionMethod->name; @@ -267,10 +263,11 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if ($reflectionMethod->isVariadic()) { array_pop($parameters); } - $this->defaultArgument->names = new \ArrayObject(); + $defaultArgument = clone $this->defaultArgument; + $defaultArgument->names = new \ArrayObject(); foreach ($parameters as $index => $parameter) { - $this->defaultArgument->names[$index] = $parameter->name; + $defaultArgument->names[$index] = $parameter->name; if (\array_key_exists($parameter->name, $arguments)) { $arguments[$index] = $arguments[$parameter->name]; @@ -284,15 +281,16 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a $target = null; $name = Target::parseName($parameter, $target); $target = $target ? [$target] : []; + $currentId = $this->currentId; - $getValue = function () use ($type, $parameter, $class, $method, $name, $target) { + $getValue = function () use ($type, $parameter, $class, $method, $name, $target, $defaultArgument, $currentId) { if (!$value = $this->getAutowiredReference($ref = new TypedReference($type, $type, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, $name, $target), false)) { - $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class.'::'.$method : $method)); + $failureMessage = $this->createTypeNotFoundMessageCallback($ref, sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $currentId ? $class.'::'.$method : $method)); if ($parameter->isDefaultValueAvailable()) { - $value = $this->defaultArgument->withValue($parameter); + $value = $defaultArgument->withValue($parameter); } elseif (!$parameter->allowsNull()) { - throw new AutowiringFailedException($this->currentId, $failureMessage); + throw new AutowiringFailedException($currentId, $failureMessage); } } @@ -316,7 +314,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if (!$parameter->isDefaultValueAvailable()) { throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); } - $arguments[$index] = clone $this->defaultArgument; + $arguments[$index] = clone $defaultArgument; $arguments[$index]->value = $parameter->getDefaultValue(); continue 2; @@ -324,6 +322,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if ($attribute instanceof AutowireCallable) { $value = $attribute->buildDefinition($value, $type, $parameter); + $value = $this->doProcessValue($value); } elseif ($lazy = $attribute->lazy) { $definition = (new Definition($type)) ->setFactory('current') @@ -385,25 +384,24 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a } // specifically pass the default value - $arguments[$index] = $this->defaultArgument->withValue($parameter); + $arguments[$index] = $defaultArgument->withValue($parameter); continue; } if ($this->decoratedClass && is_a($this->decoratedClass, $type, true)) { - if ($this->getPreviousValue) { + if ($this->restorePreviousValue) { // The inner service is injected only if there is only 1 argument matching the type of the decorated class // across all arguments of all autowired methods. // If a second matching argument is found, the default behavior is restored. - - $getPreviousValue = $this->getPreviousValue; - $this->methodCalls[$this->decoratedMethodIndex][1][$this->decoratedMethodArgumentIndex] = $getPreviousValue(); - $this->decoratedClass = null; // Prevent further checks + ($this->restorePreviousValue)(); + $this->decoratedClass = $this->restorePreviousValue = null; // Prevent further checks } else { $arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass); - $this->getPreviousValue = $getValue; - $this->decoratedMethodIndex = $methodIndex; - $this->decoratedMethodArgumentIndex = $index; + $argumentAtIndex = &$arguments[$index]; + $this->restorePreviousValue = static function () use (&$argumentAtIndex, $getValue) { + $argumentAtIndex = $getValue(); + }; continue; } @@ -414,7 +412,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a if ($parameters && !isset($arguments[++$index])) { while (0 <= --$index) { - if (!$arguments[$index] instanceof $this->defaultArgument) { + if (!$arguments[$index] instanceof $defaultArgument) { break; } unset($arguments[$index]);