![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/old/vendor/rector/rector/vendor/symfony/dependency-injection/Compiler/ |
<?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <[email protected]> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace RectorPrefix202308\Symfony\Component\DependencyInjection\Compiler; use RectorPrefix202308\Symfony\Component\Config\Resource\ClassExistenceResource; use RectorPrefix202308\Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; use RectorPrefix202308\Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument; use RectorPrefix202308\Symfony\Component\DependencyInjection\Attribute\Autowire; use RectorPrefix202308\Symfony\Component\DependencyInjection\Attribute\MapDecorated; use RectorPrefix202308\Symfony\Component\DependencyInjection\Attribute\TaggedIterator; use RectorPrefix202308\Symfony\Component\DependencyInjection\Attribute\TaggedLocator; use RectorPrefix202308\Symfony\Component\DependencyInjection\Attribute\Target; use RectorPrefix202308\Symfony\Component\DependencyInjection\ContainerBuilder; use RectorPrefix202308\Symfony\Component\DependencyInjection\ContainerInterface; use RectorPrefix202308\Symfony\Component\DependencyInjection\Definition; use RectorPrefix202308\Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; use RectorPrefix202308\Symfony\Component\DependencyInjection\Exception\RuntimeException; use RectorPrefix202308\Symfony\Component\DependencyInjection\LazyProxy\ProxyHelper; use RectorPrefix202308\Symfony\Component\DependencyInjection\Reference; use RectorPrefix202308\Symfony\Component\DependencyInjection\TypedReference; /** * Inspects existing service definitions and wires the autowired ones using the type hints of their classes. * * @author Kévin Dunglas <[email protected]> * @author Nicolas Grekas <[email protected]> */ class AutowirePass extends AbstractRecursivePass { /** * @var mixed[] */ private $types; /** * @var mixed[] */ private $ambiguousServiceTypes; /** * @var mixed[] */ private $autowiringAliases; /** * @var string|null */ private $lastFailure; /** * @var bool */ private $throwOnAutowiringException; /** * @var string|null */ private $decoratedClass; /** * @var string|null */ private $decoratedId; /** * @var mixed[]|null */ private $methodCalls; /** * @var object */ private $defaultArgument; /** * @var \Closure|null */ private $getPreviousValue; /** * @var int|null */ private $decoratedMethodIndex; /** * @var int|null */ private $decoratedMethodArgumentIndex; /** * @var $this|null */ private $typesClone; /** * @var mixed[] */ private $combinedAliases; public function __construct(bool $throwOnAutowireException = \true) { $this->throwOnAutowiringException = $throwOnAutowireException; $this->defaultArgument = new class { public $value; public $names; }; } /** * {@inheritdoc} */ public function process(ContainerBuilder $container) { $this->populateCombinedAliases($container); try { $this->typesClone = clone $this; parent::process($container); } finally { $this->decoratedClass = null; $this->decoratedId = null; $this->methodCalls = null; $this->defaultArgument->names = null; $this->getPreviousValue = null; $this->decoratedMethodIndex = null; $this->decoratedMethodArgumentIndex = null; $this->typesClone = null; $this->combinedAliases = []; } } /** * {@inheritdoc} * @param mixed $value * @return mixed */ protected function processValue($value, bool $isRoot = \false) { try { return $this->doProcessValue($value, $isRoot); } catch (AutowiringFailedException $e) { if ($this->throwOnAutowiringException) { throw $e; } $this->container->getDefinition($this->currentId)->addError($e->getMessageCallback() ?? $e->getMessage()); return parent::processValue($value, $isRoot); } } /** * @param mixed $value * @return mixed */ private function doProcessValue($value, bool $isRoot = \false) { if ($value instanceof TypedReference) { if ($ref = $this->getAutowiredReference($value, \true)) { return $ref; } if (ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) { $message = $this->createTypeNotFoundMessageCallback($value, 'it'); // since the error message varies by referenced id and $this->currentId, so should the id of the dummy errored definition $this->container->register($id = \sprintf('.errored.%s.%s', $this->currentId, (string) $value), $value->getType())->addError($message); return new TypedReference($id, $value->getType(), $value->getInvalidBehavior(), $value->getName()); } } $value = parent::processValue($value, $isRoot); if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) { return $value; } if (!($reflectionClass = $this->container->getReflectionClass($value->getClass(), \false))) { $this->container->log($this, \sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass())); return $value; } $this->methodCalls = $value->getMethodCalls(); try { $constructor = $this->getConstructor($value, \false); } catch (RuntimeException $e) { throw new AutowiringFailedException($this->currentId, $e->getMessage(), 0, $e); } if ($constructor) { \array_unshift($this->methodCalls, [$constructor, $value->getArguments()]); } $checkAttributes = !$value->hasTag('container.ignore_attributes'); $this->methodCalls = $this->autowireCalls($reflectionClass, $isRoot, $checkAttributes); if ($constructor) { [, $arguments] = \array_shift($this->methodCalls); if ($arguments !== $value->getArguments()) { $value->setArguments($arguments); } } if ($this->methodCalls !== $value->getMethodCalls()) { $value->setMethodCalls($this->methodCalls); } return $value; } private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot, bool $checkAttributes) : array { $this->decoratedId = null; $this->decoratedClass = null; $this->getPreviousValue = 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(); } $patchedIndexes = []; foreach ($this->methodCalls as $i => $call) { [$method, $arguments] = $call; if ($method instanceof \ReflectionFunctionAbstract) { $reflectionMethod = $method; } else { $definition = new Definition($reflectionClass->name); try { $reflectionMethod = $this->getReflectionMethod($definition, $method); } catch (RuntimeException $e) { if ($definition->getFactory()) { continue; } throw $e; } } $arguments = $this->autowireMethod($reflectionMethod, $arguments, $checkAttributes, $i); if ($arguments !== $call[1]) { $this->methodCalls[$i][1] = $arguments; $patchedIndexes[] = $i; } } // use named arguments to skip complex default values foreach ($patchedIndexes as $i) { $namedArguments = null; $arguments = $this->methodCalls[$i][1]; foreach ($arguments as $j => $value) { if ($namedArguments && !$value instanceof $this->defaultArgument) { unset($arguments[$j]); $arguments[$namedArguments[$j]] = $value; } if ($namedArguments || !$value instanceof $this->defaultArgument) { continue; } if (\is_array($value->value) ? $value->value : \is_object($value->value)) { unset($arguments[$j]); $namedArguments = $value->names; } else { $arguments[$j] = $value->value; } } $this->methodCalls[$i][1] = $arguments; } return $this->methodCalls; } /** * Autowires the constructor or a method. * * @throws AutowiringFailedException */ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, array $arguments, bool $checkAttributes, int $methodIndex) : array { $class = $reflectionMethod instanceof \ReflectionMethod ? $reflectionMethod->class : $this->currentId; $method = $reflectionMethod->name; $parameters = $reflectionMethod->getParameters(); if ($reflectionMethod->isVariadic()) { \array_pop($parameters); } $this->defaultArgument->names = new \ArrayObject(); foreach ($parameters as $index => $parameter) { $this->defaultArgument->names[$index] = $parameter->name; if (\array_key_exists($parameter->name, $arguments)) { $arguments[$index] = $arguments[$parameter->name]; unset($arguments[$parameter->name]); } if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, \true); if ($checkAttributes) { foreach (\method_exists($parameter, 'getAttributes') ? $parameter->getAttributes() : [] as $attribute) { if (TaggedIterator::class === $attribute->getName()) { $attribute = $attribute->newInstance(); $arguments[$index] = new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, \false, $attribute->defaultPriorityMethod, (array) $attribute->exclude); break; } if (TaggedLocator::class === $attribute->getName()) { $attribute = $attribute->newInstance(); $arguments[$index] = new ServiceLocatorArgument(new TaggedIteratorArgument($attribute->tag, $attribute->indexAttribute, $attribute->defaultIndexMethod, \true, $attribute->defaultPriorityMethod, (array) $attribute->exclude)); break; } if (Autowire::class === $attribute->getName()) { $value = $attribute->newInstance()->value; $value = $this->container->getParameterBag()->resolveValue($value); if ($value instanceof Reference && $parameter->allowsNull()) { $value = new Reference($value, ContainerInterface::NULL_ON_INVALID_REFERENCE); } $arguments[$index] = $value; break; } if (MapDecorated::class === $attribute->getName()) { $definition = $this->container->getDefinition($this->currentId); $arguments[$index] = new Reference($definition->innerServiceId ?? $this->currentId . '.inner', $definition->decorationOnInvalid ?? ContainerInterface::NULL_ON_INVALID_REFERENCE); break; } } if ('' !== ($arguments[$index] ?? '')) { continue; } } if (!$type) { if (isset($arguments[$index])) { continue; } // no default value? Then fail if (!$parameter->isDefaultValueAvailable()) { // For core classes, isDefaultValueAvailable() can // be false when isOptional() returns true. If the // argument *is* optional, allow it to be missing if ($parameter->isOptional()) { --$index; break; } $type = ProxyHelper::getTypeHint($reflectionMethod, $parameter, \false); $type = $type ? \sprintf('is type-hinted "%s"', \ltrim($type, '\\')) : 'has no type-hint'; throw new AutowiringFailedException($this->currentId, \sprintf('Cannot autowire service "%s": argument "$%s" of method "%s()" %s, you should configure its value explicitly.', $this->currentId, $parameter->name, $class !== $this->currentId ? $class . '::' . $method : $method, $type)); } // specifically pass the default value $arguments[$index] = clone $this->defaultArgument; $arguments[$index]->value = $parameter->getDefaultValue(); continue; } $getValue = function () use($type, $parameter, $class, $method) { if (!($value = $this->getAutowiredReference($ref = new TypedReference($type, $type, ContainerBuilder::EXCEPTION_ON_INVALID_REFERENCE, Target::parseName($parameter)), \true))) { $failureMessage = $this->createTypeNotFoundMessageCallback($ref, \sprintf('argument "$%s" of method "%s()"', $parameter->name, $class !== $this->currentId ? $class . '::' . $method : $method)); if ($parameter->isDefaultValueAvailable()) { $value = clone $this->defaultArgument; $value->value = $parameter->getDefaultValue(); } elseif (!$parameter->allowsNull()) { throw new AutowiringFailedException($this->currentId, $failureMessage); } } return $value; }; if ($this->decoratedClass && ($isDecorated = \is_a($this->decoratedClass, $type, \true))) { if ($this->getPreviousValue) { // 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 } else { $arguments[$index] = new TypedReference($this->decoratedId, $this->decoratedClass); $this->getPreviousValue = $getValue; $this->decoratedMethodIndex = $methodIndex; $this->decoratedMethodArgumentIndex = $index; continue; } } $arguments[$index] = $getValue(); } if ($parameters && !isset($arguments[++$index])) { while (0 <= --$index) { if (!$arguments[$index] instanceof $this->defaultArgument) { break; } unset($arguments[$index]); } } // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected \ksort($arguments, \SORT_NATURAL); return $arguments; } /** * Returns a reference to the service matching the given type, if any. */ private function getAutowiredReference(TypedReference $reference, bool $filterType) : ?TypedReference { $this->lastFailure = null; $type = $reference->getType(); if ($type !== (string) $reference) { return $reference; } if ($filterType && \false !== ($m = \strpbrk($type, '&|'))) { $types = \array_diff(\explode($m[0], $type), ['int', 'string', 'array', 'bool', 'float', 'iterable', 'object', 'callable', 'null']); \sort($types); $type = \implode($m[0], $types); } if (null !== ($name = $reference->getName())) { if ($this->container->has($alias = $type . ' $' . $name) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } if (null !== ($alias = $this->combinedAliases[$alias] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } if ($this->container->has($name) && !$this->container->findDefinition($name)->isAbstract()) { foreach ($this->container->getAliases() + $this->combinedAliases as $id => $alias) { if ($name === (string) $alias && \strncmp($id, $type . ' $', \strlen($type . ' $')) === 0) { return new TypedReference($name, $type, $reference->getInvalidBehavior()); } } } } if ($this->container->has($type) && !$this->container->findDefinition($type)->isAbstract()) { return new TypedReference($type, $type, $reference->getInvalidBehavior()); } if (null !== ($alias = $this->combinedAliases[$type] ?? null) && !$this->container->findDefinition($alias)->isAbstract()) { return new TypedReference($alias, $type, $reference->getInvalidBehavior()); } return null; } /** * Populates the list of available types. */ private function populateAvailableTypes(ContainerBuilder $container) { $this->types = []; $this->ambiguousServiceTypes = []; $this->autowiringAliases = []; foreach ($container->getDefinitions() as $id => $definition) { $this->populateAvailableType($container, $id, $definition); } foreach ($container->getAliases() as $id => $alias) { $this->populateAutowiringAlias($id); } } /** * Populates the list of available types for a given definition. */ private function populateAvailableType(ContainerBuilder $container, string $id, Definition $definition) { // Never use abstract services if ($definition->isAbstract()) { return; } if ('' === $id || '.' === $id[0] || $definition->isDeprecated() || !($reflectionClass = $container->getReflectionClass($definition->getClass(), \false))) { return; } foreach ($reflectionClass->getInterfaces() as $reflectionInterface) { $this->set($reflectionInterface->name, $id); } do { $this->set($reflectionClass->name, $id); } while ($reflectionClass = $reflectionClass->getParentClass()); $this->populateAutowiringAlias($id); } /** * Associates a type and a service id if applicable. */ private function set(string $type, string $id) { // is this already a type/class that is known to match multiple services? if (isset($this->ambiguousServiceTypes[$type])) { $this->ambiguousServiceTypes[$type][] = $id; return; } // check to make sure the type doesn't match multiple services if (!isset($this->types[$type]) || $this->types[$type] === $id) { $this->types[$type] = $id; return; } // keep an array of all services matching this type if (!isset($this->ambiguousServiceTypes[$type])) { $this->ambiguousServiceTypes[$type] = [$this->types[$type]]; unset($this->types[$type]); } $this->ambiguousServiceTypes[$type][] = $id; } private function createTypeNotFoundMessageCallback(TypedReference $reference, string $label) : \Closure { if (null === $this->typesClone->container) { $this->typesClone->container = new ContainerBuilder($this->container->getParameterBag()); $this->typesClone->container->setAliases($this->container->getAliases()); $this->typesClone->container->setDefinitions($this->container->getDefinitions()); $this->typesClone->container->setResourceTracking(\false); } $currentId = $this->currentId; return (function () use($reference, $label, $currentId) { return $this->createTypeNotFoundMessage($reference, $label, $currentId); })->bindTo($this->typesClone); } private function createTypeNotFoundMessage(TypedReference $reference, string $label, string $currentId) : string { if (!($r = $this->container->getReflectionClass($type = $reference->getType(), \false))) { // either $type does not exist or a parent class does not exist try { $resource = new ClassExistenceResource($type, \false); // isFresh() will explode ONLY if a parent class/trait does not exist $resource->isFresh(0); $parentMsg = \false; } catch (\ReflectionException $e) { $parentMsg = $e->getMessage(); } $message = \sprintf('has type "%s" but this class %s.', $type, $parentMsg ? \sprintf('is missing a parent class (%s)', $parentMsg) : 'was not found'); } else { $alternatives = $this->createTypeAlternatives($this->container, $reference); $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; $message = \sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $alternatives); if ($r->isInterface() && !$alternatives) { $message .= ' Did you create a class that implements this interface?'; } } $message = \sprintf('Cannot autowire service "%s": %s %s', $currentId, $label, $message); if (null !== $this->lastFailure) { $message = $this->lastFailure . "\n" . $message; $this->lastFailure = null; } return $message; } private function createTypeAlternatives(ContainerBuilder $container, TypedReference $reference) : string { // try suggesting available aliases first if ($message = $this->getAliasesSuggestionForType($container, $type = $reference->getType())) { return ' ' . $message; } if (!isset($this->ambiguousServiceTypes)) { $this->populateAvailableTypes($container); } $servicesAndAliases = $container->getServiceIds(); if (null !== ($autowiringAliases = $this->autowiringAliases[$type] ?? null) && !isset($autowiringAliases[''])) { return \sprintf(' Available autowiring aliases for this %s are: "$%s".', \class_exists($type, \false) ? 'class' : 'interface', \implode('", "$', $autowiringAliases)); } if (!$container->has($type) && \false !== ($key = \array_search(\strtolower($type), \array_map('strtolower', $servicesAndAliases)))) { return \sprintf(' Did you mean "%s"?', $servicesAndAliases[$key]); } elseif (isset($this->ambiguousServiceTypes[$type])) { $message = \sprintf('one of these existing services: "%s"', \implode('", "', $this->ambiguousServiceTypes[$type])); } elseif (isset($this->types[$type])) { $message = \sprintf('the existing "%s" service', $this->types[$type]); } else { return ''; } return \sprintf(' You should maybe alias this %s to %s.', \class_exists($type, \false) ? 'class' : 'interface', $message); } private function getAliasesSuggestionForType(ContainerBuilder $container, string $type) : ?string { $aliases = []; foreach (\class_parents($type) + \class_implements($type) as $parent) { if ($container->has($parent) && !$container->findDefinition($parent)->isAbstract()) { $aliases[] = $parent; } } if (1 < ($len = \count($aliases))) { $message = 'Try changing the type-hint to one of its parents: '; for ($i = 0, --$len; $i < $len; ++$i) { $message .= \sprintf('%s "%s", ', \class_exists($aliases[$i], \false) ? 'class' : 'interface', $aliases[$i]); } $message .= \sprintf('or %s "%s".', \class_exists($aliases[$i], \false) ? 'class' : 'interface', $aliases[$i]); return $message; } if ($aliases) { return \sprintf('Try changing the type-hint to "%s" instead.', $aliases[0]); } return null; } private function populateAutowiringAlias(string $id) : void { if (!\preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \\$((?&V)))?$/', $id, $m)) { return; } $type = $m[2]; $name = $m[3] ?? ''; if (\class_exists($type, \false) || \interface_exists($type, \false)) { $this->autowiringAliases[$type][$name] = $name; } } private function populateCombinedAliases(ContainerBuilder $container) : void { $this->combinedAliases = []; $reverseAliases = []; foreach ($container->getAliases() as $id => $alias) { if (!\preg_match('/(?(DEFINE)(?<V>[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*+))^((?&V)(?:\\\\(?&V))*+)(?: \\$((?&V)))?$/', $id, $m)) { continue; } $type = $m[2]; $name = $m[3] ?? ''; $reverseAliases[(string) $alias][$name][] = $type; } foreach ($reverseAliases as $alias => $names) { foreach ($names as $name => $types) { if (2 > ($count = \count($types))) { continue; } \sort($types); $i = 1 << $count; // compute the powerset of the list of types while ($i--) { $set = []; for ($j = 0; $j < $count; ++$j) { if ($i & 1 << $j) { $set[] = $types[$j]; } } if (2 <= \count($set)) { $this->combinedAliases[\implode('&', $set) . ('' === $name ? '' : ' $' . $name)] = $alias; $this->combinedAliases[\implode('|', $set) . ('' === $name ? '' : ' $' . $name)] = $alias; } } } } } }