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

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 $processExpressions false;
  31.     private $expressionLanguage;
  32.     private $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.      * @param mixed $value
  61.      *
  62.      * @return mixed
  63.      */
  64.     protected function processValue($valuebool $isRoot false)
  65.     {
  66.         if (\is_array($value)) {
  67.             foreach ($value as $k => $v) {
  68.                 if ($isRoot) {
  69.                     $this->currentId $k;
  70.                 }
  71.                 if ($v !== $processedValue $this->processValue($v$isRoot)) {
  72.                     $value[$k] = $processedValue;
  73.                 }
  74.             }
  75.         } elseif ($value instanceof ArgumentInterface) {
  76.             $value->setValues($this->processValue($value->getValues()));
  77.         } elseif ($value instanceof Expression && $this->processExpressions) {
  78.             $this->getExpressionLanguage()->compile((string) $value, ['this' => 'container']);
  79.         } elseif ($value instanceof Definition) {
  80.             $value->setArguments($this->processValue($value->getArguments()));
  81.             $value->setProperties($this->processValue($value->getProperties()));
  82.             $value->setMethodCalls($this->processValue($value->getMethodCalls()));
  83.             $changes $value->getChanges();
  84.             if (isset($changes['factory'])) {
  85.                 $value->setFactory($this->processValue($value->getFactory()));
  86.             }
  87.             if (isset($changes['configurator'])) {
  88.                 $value->setConfigurator($this->processValue($value->getConfigurator()));
  89.             }
  90.         }
  91.         return $value;
  92.     }
  93.     /**
  94.      * @return \ReflectionFunctionAbstract|null
  95.      *
  96.      * @throws RuntimeException
  97.      */
  98.     protected function getConstructor(Definition $definitionbool $required)
  99.     {
  100.         if ($definition->isSynthetic()) {
  101.             return null;
  102.         }
  103.         if (\is_string($factory $definition->getFactory())) {
  104.             if (!\function_exists($factory)) {
  105.                 throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.'$this->currentId$factory));
  106.             }
  107.             $r = new \ReflectionFunction($factory);
  108.             if (false !== $r->getFileName() && file_exists($r->getFileName())) {
  109.                 $this->container->fileExists($r->getFileName());
  110.             }
  111.             return $r;
  112.         }
  113.         if ($factory) {
  114.             [$class$method] = $factory;
  115.             if ('__construct' === $method) {
  116.                 throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.'$this->currentId));
  117.             }
  118.             if ($class instanceof Reference) {
  119.                 $factoryDefinition $this->container->findDefinition((string) $class);
  120.                 while ((null === $class $factoryDefinition->getClass()) && $factoryDefinition instanceof ChildDefinition) {
  121.                     $factoryDefinition $this->container->findDefinition($factoryDefinition->getParent());
  122.                 }
  123.             } elseif ($class instanceof Definition) {
  124.                 $class $class->getClass();
  125.             } elseif (null === $class) {
  126.                 $class $definition->getClass();
  127.             }
  128.             return $this->getReflectionMethod(new Definition($class), $method);
  129.         }
  130.         while ((null === $class $definition->getClass()) && $definition instanceof ChildDefinition) {
  131.             $definition $this->container->findDefinition($definition->getParent());
  132.         }
  133.         try {
  134.             if (!$r $this->container->getReflectionClass($class)) {
  135.                 if (null === $class) {
  136.                     throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  137.                 }
  138.                 throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  139.             }
  140.         } catch (\ReflectionException $e) {
  141.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).lcfirst($e->getMessage()));
  142.         }
  143.         if (!$r $r->getConstructor()) {
  144.             if ($required) {
  145.                 throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.'$this->currentIdsprintf($class !== $this->currentId ' "%s"' ''$class)));
  146.             }
  147.         } elseif (!$r->isPublic()) {
  148.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).sprintf($class !== $this->currentId 'constructor of class "%s"' 'its constructor'$class).' must be public.');
  149.         }
  150.         return $r;
  151.     }
  152.     /**
  153.      * @return \ReflectionFunctionAbstract
  154.      *
  155.      * @throws RuntimeException
  156.      */
  157.     protected function getReflectionMethod(Definition $definitionstring $method)
  158.     {
  159.         if ('__construct' === $method) {
  160.             return $this->getConstructor($definitiontrue);
  161.         }
  162.         while ((null === $class $definition->getClass()) && $definition instanceof ChildDefinition) {
  163.             $definition $this->container->findDefinition($definition->getParent());
  164.         }
  165.         if (null === $class) {
  166.             throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  167.         }
  168.         if (!$r $this->container->getReflectionClass($class)) {
  169.             throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  170.         }
  171.         if (!$r->hasMethod($method)) {
  172.             if ($r->hasMethod('__call') && ($r $r->getMethod('__call')) && $r->isPublic()) {
  173.                 return new \ReflectionMethod(static function (...$arguments) {}, '__invoke');
  174.             }
  175.             if ($r->hasMethod('__callStatic') && ($r $r->getMethod('__callStatic')) && $r->isPublic()) {
  176.                 return new \ReflectionMethod(static function (...$arguments) {}, '__invoke');
  177.             }
  178.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  179.         }
  180.         $r $r->getMethod($method);
  181.         if (!$r->isPublic()) {
  182.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  183.         }
  184.         return $r;
  185.     }
  186.     private function getExpressionLanguage(): ExpressionLanguage
  187.     {
  188.         if (null === $this->expressionLanguage) {
  189.             if (!class_exists(ExpressionLanguage::class)) {
  190.                 throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".');
  191.             }
  192.             $providers $this->container->getExpressionLanguageProviders();
  193.             $this->expressionLanguage = new ExpressionLanguage(null$providers, function (string $arg): string {
  194.                 if ('""' === substr_replace($arg''1, -1)) {
  195.                     $id stripcslashes(substr($arg1, -1));
  196.                     $this->inExpression true;
  197.                     $arg $this->processValue(new Reference($id));
  198.                     $this->inExpression false;
  199.                     if (!$arg instanceof Reference) {
  200.                         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));
  201.                     }
  202.                     $arg sprintf('"%s"'$arg);
  203.                 }
  204.                 return sprintf('$this->get(%s)'$arg);
  205.             });
  206.         }
  207.         return $this->expressionLanguage;
  208.     }
  209. }