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\ContainerBuilder;
  13. use Symfony\Component\DependencyInjection\Definition;
  14. use Symfony\Component\DependencyInjection\Exception\LogicException;
  15. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  16. use Symfony\Component\DependencyInjection\ExpressionLanguage;
  17. use Symfony\Component\DependencyInjection\Reference;
  18. use Symfony\Component\ExpressionLanguage\Expression;
  19. /**
  20.  * @author Nicolas Grekas <p@tchwork.com>
  21.  */
  22. abstract class AbstractRecursivePass implements CompilerPassInterface
  23. {
  24.     /**
  25.      * @var ContainerBuilder
  26.      */
  27.     protected $container;
  28.     protected $currentId;
  29.     private $processExpressions false;
  30.     private $expressionLanguage;
  31.     private $inExpression false;
  32.     /**
  33.      * {@inheritdoc}
  34.      */
  35.     public function process(ContainerBuilder $container)
  36.     {
  37.         $this->container $container;
  38.         try {
  39.             $this->processValue($container->getDefinitions(), true);
  40.         } finally {
  41.             $this->container null;
  42.         }
  43.     }
  44.     protected function enableExpressionProcessing()
  45.     {
  46.         $this->processExpressions true;
  47.     }
  48.     protected function inExpression(bool $reset true): bool
  49.     {
  50.         $inExpression $this->inExpression;
  51.         if ($reset) {
  52.             $this->inExpression false;
  53.         }
  54.         return $inExpression;
  55.     }
  56.     /**
  57.      * Processes a value found in a definition tree.
  58.      *
  59.      * @param mixed $value
  60.      * @param bool  $isRoot
  61.      *
  62.      * @return mixed The processed value
  63.      */
  64.     protected function processValue($value$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.      * @param bool $required
  95.      *
  96.      * @return \ReflectionFunctionAbstract|null
  97.      *
  98.      * @throws RuntimeException
  99.      */
  100.     protected function getConstructor(Definition $definition$required)
  101.     {
  102.         if ($definition->isSynthetic()) {
  103.             return null;
  104.         }
  105.         if (\is_string($factory $definition->getFactory())) {
  106.             if (!\function_exists($factory)) {
  107.                 throw new RuntimeException(sprintf('Invalid service "%s": function "%s" does not exist.'$this->currentId$factory));
  108.             }
  109.             $r = new \ReflectionFunction($factory);
  110.             if (false !== $r->getFileName() && file_exists($r->getFileName())) {
  111.                 $this->container->fileExists($r->getFileName());
  112.             }
  113.             return $r;
  114.         }
  115.         if ($factory) {
  116.             list($class$method) = $factory;
  117.             if ($class instanceof Reference) {
  118.                 $class $this->container->findDefinition((string) $class)->getClass();
  119.             } elseif ($class instanceof Definition) {
  120.                 $class $class->getClass();
  121.             } elseif (null === $class) {
  122.                 $class $definition->getClass();
  123.             }
  124.             if ('__construct' === $method) {
  125.                 throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.'$this->currentId));
  126.             }
  127.             return $this->getReflectionMethod(new Definition($class), $method);
  128.         }
  129.         $class $definition->getClass();
  130.         try {
  131.             if (!$r $this->container->getReflectionClass($class)) {
  132.                 throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  133.             }
  134.         } catch (\ReflectionException $e) {
  135.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).lcfirst($e->getMessage()));
  136.         }
  137.         if (!$r $r->getConstructor()) {
  138.             if ($required) {
  139.                 throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.'$this->currentIdsprintf($class !== $this->currentId ' "%s"' ''$class)));
  140.             }
  141.         } elseif (!$r->isPublic()) {
  142.             throw new RuntimeException(sprintf('Invalid service "%s": '$this->currentId).sprintf($class !== $this->currentId 'constructor of class "%s"' 'its constructor'$class).' must be public.');
  143.         }
  144.         return $r;
  145.     }
  146.     /**
  147.      * @param string $method
  148.      *
  149.      * @throws RuntimeException
  150.      *
  151.      * @return \ReflectionFunctionAbstract
  152.      */
  153.     protected function getReflectionMethod(Definition $definition$method)
  154.     {
  155.         if ('__construct' === $method) {
  156.             return $this->getConstructor($definitiontrue);
  157.         }
  158.         if (!$class $definition->getClass()) {
  159.             throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  160.         }
  161.         if (!$r $this->container->getReflectionClass($class)) {
  162.             throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  163.         }
  164.         if (!$r->hasMethod($method)) {
  165.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  166.         }
  167.         $r $r->getMethod($method);
  168.         if (!$r->isPublic()) {
  169.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  170.         }
  171.         return $r;
  172.     }
  173.     private function getExpressionLanguage(): ExpressionLanguage
  174.     {
  175.         if (null === $this->expressionLanguage) {
  176.             if (!class_exists(ExpressionLanguage::class)) {
  177.                 throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
  178.             }
  179.             $providers $this->container->getExpressionLanguageProviders();
  180.             $this->expressionLanguage = new ExpressionLanguage(null$providers, function (string $arg): string {
  181.                 if ('""' === substr_replace($arg''1, -1)) {
  182.                     $id stripcslashes(substr($arg1, -1));
  183.                     $this->inExpression true;
  184.                     $arg $this->processValue(new Reference($id));
  185.                     $this->inExpression false;
  186.                     if (!$arg instanceof Reference) {
  187.                         throw new RuntimeException(sprintf('"%s::processValue()" must return a Reference when processing an expression, "%s" returned for service("%s").', static::class, \is_object($arg) ? \get_class($arg) : \gettype($arg), $id));
  188.                     }
  189.                     $arg sprintf('"%s"'$arg);
  190.                 }
  191.                 return sprintf('$this->get(%s)'$arg);
  192.             });
  193.         }
  194.         return $this->expressionLanguage;
  195.     }
  196. }