vendor/symfony/dependency-injection/Compiler/AbstractRecursivePass.php line 226

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\DependencyInjection\Compiler;
  11. use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
  12. use Symfony\Component\DependencyInjection\ChildDefinition;
  13. use Symfony\Component\DependencyInjection\ContainerBuilder;
  14. use Symfony\Component\DependencyInjection\Definition;
  15. use Symfony\Component\DependencyInjection\Exception\LogicException;
  16. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  17. use Symfony\Component\DependencyInjection\ExpressionLanguage;
  18. use Symfony\Component\DependencyInjection\Reference;
  19. use Symfony\Component\ExpressionLanguage\Expression;
  20. /**
  21.  * @author Nicolas Grekas <p@tchwork.com>
  22.  */
  23. abstract class AbstractRecursivePass implements CompilerPassInterface
  24. {
  25.     /**
  26.      * @var ContainerBuilder
  27.      */
  28.     protected $container;
  29.     protected $currentId;
  30.     private bool $processExpressions false;
  31.     private ExpressionLanguage $expressionLanguage;
  32.     private bool $inExpression false;
  33.     /**
  34.      * {@inheritdoc}
  35.      */
  36.     public function process(ContainerBuilder $container)
  37.     {
  38.         $this->container $container;
  39.         try {
  40.             $this->processValue($container->getDefinitions(), true);
  41.         } finally {
  42.             $this->container null;
  43.         }
  44.     }
  45.     protected function enableExpressionProcessing()
  46.     {
  47.         $this->processExpressions true;
  48.     }
  49.     protected function inExpression(bool $reset true): bool
  50.     {
  51.         $inExpression $this->inExpression;
  52.         if ($reset) {
  53.             $this->inExpression false;
  54.         }
  55.         return $inExpression;
  56.     }
  57.     /**
  58.      * Processes a value found in a definition tree.
  59.      *
  60.      * @return mixed
  61.      */
  62.     protected function processValue(mixed $valuebool $isRoot false)
  63.     {
  64.         if (\is_array($value)) {
  65.             foreach ($value as $k => $v) {
  66.                 if ($isRoot) {
  67.                     $this->currentId $k;
  68.                 }
  69.                 if ($v !== $processedValue $this->processValue($v$isRoot)) {
  70.                     $value[$k] = $processedValue;
  71.                 }
  72.             }
  73.         } elseif ($value instanceof ArgumentInterface) {
  74.             $value->setValues($this->processValue($value->getValues()));
  75.         } elseif ($value instanceof Expression && $this->processExpressions) {
  76.             $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container''args' => 'args']);
  77.         } elseif ($value instanceof Definition) {
  78.             $value->setArguments($this->processValue($value->getArguments()));
  79.             $value->setProperties($this->processValue($value->getProperties()));
  80.             $value->setMethodCalls($this->processValue($value->getMethodCalls()));
  81.             $changes $value->getChanges();
  82.             if (isset($changes['factory'])) {
  83.                 if (\is_string($factory $value->getFactory()) && str_starts_with($factory'@=')) {
  84.                     if (!class_exists(Expression::class)) {
  85.                         throw new LogicException('Expressions cannot be used in service factories without the ExpressionLanguage component. Try running "composer require symfony/expression-language".');
  86.                     }
  87.                     $factory = new Expression(substr($factory2));
  88.                 }
  89.                 if (($factory $this->processValue($factory)) instanceof Expression) {
  90.                     $factory '@='.$factory;
  91.                 }
  92.                 $value->setFactory($factory);
  93.             }
  94.             if (isset($changes['configurator'])) {
  95.                 $value->setConfigurator($this->processValue($value->getConfigurator()));
  96.             }
  97.         }
  98.         return $value;
  99.     }
  100.     /**
  101.      * @throws RuntimeException
  102.      */
  103.     protected function getConstructor(Definition $definitionbool $required): ?\ReflectionFunctionAbstract
  104.     {
  105.         if ($definition->isSynthetic()) {
  106.             return null;
  107.         }
  108.         if (\is_string($factory $definition->getFactory())) {
  109.             if (str_starts_with($factory'@=')) {
  110.                 return new \ReflectionFunction(static function (...$args) {});
  111.             }
  112.             if (!\function_exists($factory)) {
  113.                 throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.'$this->currentId$factory));
  114.             }
  115.             $r = new \ReflectionFunction($factory);
  116.             if (false !== $r->getFileName() && file_exists($r->getFileName())) {
  117.                 $this->container->fileExists($r->getFileName());
  118.             }
  119.             return $r;
  120.         }
  121.         if ($factory) {
  122.             [$class$method] = $factory;
  123.             if ('__construct' === $method) {
  124.                 throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.'$this->currentId));
  125.             }
  126.             if ($class instanceof Reference) {
  127.                 $factoryDefinition $this->container->findDefinition((string) $class);
  128.                 while ((null === $class $factoryDefinition->getClass()) && $factoryDefinition instanceof ChildDefinition) {
  129.                     $factoryDefinition $this->container->findDefinition($factoryDefinition->getParent());
  130.                 }
  131.             } elseif ($class instanceof Definition) {
  132.                 $class $class->getClass();
  133.             } elseif (null === $class) {
  134.                 $class $definition->getClass();
  135.             }
  136.             return $this->getReflectionMethod(new Definition($class), $method);
  137.         }
  138.         while ((null === $class $definition->getClass()) && $definition instanceof ChildDefinition) {
  139.             $definition $this->container->findDefinition($definition->getParent());
  140.         }
  141.         try {
  142.             if (!$r $this->container->getReflectionClass($class)) {
  143.                 if (null === $class) {
  144.                     throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  145.                 }
  146.                 throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  147.             }
  148.         } catch (\ReflectionException $e) {
  149.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).lcfirst($e->getMessage()));
  150.         }
  151.         if (!$r $r->getConstructor()) {
  152.             if ($required) {
  153.                 throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.'$this->currentIdsprintf($class !== $this->currentId ' "%s"' ''$class)));
  154.             }
  155.         } elseif (!$r->isPublic()) {
  156.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).sprintf($class !== $this->currentId 'constructor of class "%s"' 'its constructor'$class).' must be public.');
  157.         }
  158.         return $r;
  159.     }
  160.     /**
  161.      * @throws RuntimeException
  162.      */
  163.     protected function getReflectionMethod(Definition $definitionstring $method): \ReflectionFunctionAbstract
  164.     {
  165.         if ('__construct' === $method) {
  166.             return $this->getConstructor($definitiontrue);
  167.         }
  168.         while ((null === $class $definition->getClass()) && $definition instanceof ChildDefinition) {
  169.             $definition $this->container->findDefinition($definition->getParent());
  170.         }
  171.         if (null === $class) {
  172.             throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  173.         }
  174.         if (!$r $this->container->getReflectionClass($class)) {
  175.             throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  176.         }
  177.         if (!$r->hasMethod($method)) {
  178.             if ($r->hasMethod('__call') && ($r $r->getMethod('__call')) && $r->isPublic()) {
  179.                 return new \ReflectionMethod(static function (...$arguments) {}, '__invoke');
  180.             }
  181.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  182.         }
  183.         $r $r->getMethod($method);
  184.         if (!$r->isPublic()) {
  185.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  186.         }
  187.         return $r;
  188.     }
  189.     private function getExpressionLanguage(): ExpressionLanguage
  190.     {
  191.         if (!isset($this->expressionLanguage)) {
  192.             if (!class_exists(ExpressionLanguage::class)) {
  193.                 throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
  194.             }
  195.             $providers $this->container->getExpressionLanguageProviders();
  196.             $this->expressionLanguage = new ExpressionLanguage(null$providers, function (string $arg): string {
  197.                 if ('""' === substr_replace($arg''1, -1)) {
  198.                     $id stripcslashes(substr($arg1, -1));
  199.                     $this->inExpression true;
  200.                     $arg $this->processValue(new Reference($id));
  201.                     $this->inExpression false;
  202.                     if (!$arg instanceof Reference) {
  203.                         throw new RuntimeException(sprintf('"%s::processValue()" must return a Reference when processing an expression, "%s" returned for service("%s").', static::class, get_debug_type($arg), $id));
  204.                     }
  205.                     $arg sprintf('"%s"'$arg);
  206.                 }
  207.                 return sprintf('$this->get(%s)'$arg);
  208.             });
  209.         }
  210.         return $this->expressionLanguage;
  211.     }
  212. }