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 (null === $class) {
  120.                 $class $definition->getClass();
  121.             }
  122.             if ('__construct' === $method) {
  123.                 throw new RuntimeException(sprintf('Invalid service "%s": "__construct()" cannot be used as a factory method.'$this->currentId));
  124.             }
  125.             return $this->getReflectionMethod(new Definition($class), $method);
  126.         }
  127.         $class $definition->getClass();
  128.         try {
  129.             if (!$r $this->container->getReflectionClass($class)) {
  130.                 throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  131.             }
  132.         } catch (\ReflectionException $e) {
  133.             throw new RuntimeException(sprintf('Invalid service "%s": %s.'$this->currentIdlcfirst(rtrim($e->getMessage(), '.'))));
  134.         }
  135.         if (!$r $r->getConstructor()) {
  136.             if ($required) {
  137.                 throw new RuntimeException(sprintf('Invalid service "%s": class%s has no constructor.'$this->currentIdsprintf($class !== $this->currentId ' "%s"' ''$class)));
  138.             }
  139.         } elseif (!$r->isPublic()) {
  140.             throw new RuntimeException(sprintf('Invalid service "%s": %s must be public.'$this->currentIdsprintf($class !== $this->currentId 'constructor of class "%s"' 'its constructor'$class)));
  141.         }
  142.         return $r;
  143.     }
  144.     /**
  145.      * @param string $method
  146.      *
  147.      * @throws RuntimeException
  148.      *
  149.      * @return \ReflectionFunctionAbstract
  150.      */
  151.     protected function getReflectionMethod(Definition $definition$method)
  152.     {
  153.         if ('__construct' === $method) {
  154.             return $this->getConstructor($definitiontrue);
  155.         }
  156.         if (!$class $definition->getClass()) {
  157.             throw new RuntimeException(sprintf('Invalid service "%s": the class is not set.'$this->currentId));
  158.         }
  159.         if (!$r $this->container->getReflectionClass($class)) {
  160.             throw new RuntimeException(sprintf('Invalid service "%s": class "%s" does not exist.'$this->currentId$class));
  161.         }
  162.         if (!$r->hasMethod($method)) {
  163.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" does not exist.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  164.         }
  165.         $r $r->getMethod($method);
  166.         if (!$r->isPublic()) {
  167.             throw new RuntimeException(sprintf('Invalid service "%s": method "%s()" must be public.'$this->currentId$class !== $this->currentId $class.'::'.$method $method));
  168.         }
  169.         return $r;
  170.     }
  171.     private function getExpressionLanguage()
  172.     {
  173.         if (null === $this->expressionLanguage) {
  174.             if (!class_exists(ExpressionLanguage::class)) {
  175.                 throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
  176.             }
  177.             $providers $this->container->getExpressionLanguageProviders();
  178.             $this->expressionLanguage = new ExpressionLanguage(null$providers, function ($arg) {
  179.                 if ('""' === substr_replace($arg''1, -1)) {
  180.                     $id stripcslashes(substr($arg1, -1));
  181.                     $this->inExpression true;
  182.                     $arg $this->processValue(new Reference($id));
  183.                     $this->inExpression false;
  184.                     if (!$arg instanceof Reference) {
  185.                         throw new RuntimeException(sprintf('"%s::processValue()" must return a Reference when processing an expression, %s returned for service("%s").', \get_class($this), \is_object($arg) ? \get_class($arg) : \gettype($arg), $id));
  186.                     }
  187.                     $arg sprintf('"%s"'$arg);
  188.                 }
  189.                 return sprintf('$this->get(%s)'$arg);
  190.             });
  191.         }
  192.         return $this->expressionLanguage;
  193.     }
  194. }