vendor/symfony/dependency-injection/ContainerBuilder.php line 1147

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;
  11. use Psr\Container\ContainerInterface as PsrContainerInterface;
  12. use Symfony\Component\Config\Resource\ClassExistenceResource;
  13. use Symfony\Component\Config\Resource\ComposerResource;
  14. use Symfony\Component\Config\Resource\DirectoryResource;
  15. use Symfony\Component\Config\Resource\FileExistenceResource;
  16. use Symfony\Component\Config\Resource\FileResource;
  17. use Symfony\Component\Config\Resource\GlobResource;
  18. use Symfony\Component\Config\Resource\ReflectionClassResource;
  19. use Symfony\Component\Config\Resource\ResourceInterface;
  20. use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  21. use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
  22. use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  23. use Symfony\Component\DependencyInjection\Argument\ServiceLocator;
  24. use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
  25. use Symfony\Component\DependencyInjection\Compiler\Compiler;
  26. use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  27. use Symfony\Component\DependencyInjection\Compiler\PassConfig;
  28. use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
  29. use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
  30. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  31. use Symfony\Component\DependencyInjection\Exception\LogicException;
  32. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  33. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  34. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  35. use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
  36. use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
  37. use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
  38. use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
  39. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  40. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  41. use Symfony\Component\ExpressionLanguage\Expression;
  42. use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
  43. /**
  44.  * ContainerBuilder is a DI container that provides an API to easily describe services.
  45.  *
  46.  * @author Fabien Potencier <fabien@symfony.com>
  47.  */
  48. class ContainerBuilder extends Container implements TaggedContainerInterface
  49. {
  50.     /**
  51.      * @var ExtensionInterface[]
  52.      */
  53.     private $extensions = [];
  54.     /**
  55.      * @var ExtensionInterface[]
  56.      */
  57.     private $extensionsByNs = [];
  58.     /**
  59.      * @var Definition[]
  60.      */
  61.     private $definitions = [];
  62.     /**
  63.      * @var Alias[]
  64.      */
  65.     private $aliasDefinitions = [];
  66.     /**
  67.      * @var ResourceInterface[]
  68.      */
  69.     private $resources = [];
  70.     private $extensionConfigs = [];
  71.     /**
  72.      * @var Compiler
  73.      */
  74.     private $compiler;
  75.     private $trackResources;
  76.     /**
  77.      * @var InstantiatorInterface|null
  78.      */
  79.     private $proxyInstantiator;
  80.     /**
  81.      * @var ExpressionLanguage|null
  82.      */
  83.     private $expressionLanguage;
  84.     /**
  85.      * @var ExpressionFunctionProviderInterface[]
  86.      */
  87.     private $expressionLanguageProviders = [];
  88.     /**
  89.      * @var string[] with tag names used by findTaggedServiceIds
  90.      */
  91.     private $usedTags = [];
  92.     /**
  93.      * @var string[][] a map of env var names to their placeholders
  94.      */
  95.     private $envPlaceholders = [];
  96.     /**
  97.      * @var int[] a map of env vars to their resolution counter
  98.      */
  99.     private $envCounters = [];
  100.     /**
  101.      * @var string[] the list of vendor directories
  102.      */
  103.     private $vendors;
  104.     private $autoconfiguredInstanceof = [];
  105.     private $removedIds = [];
  106.     private $removedBindingIds = [];
  107.     private const INTERNAL_TYPES = [
  108.         'int' => true,
  109.         'float' => true,
  110.         'string' => true,
  111.         'bool' => true,
  112.         'resource' => true,
  113.         'object' => true,
  114.         'array' => true,
  115.         'null' => true,
  116.         'callable' => true,
  117.         'iterable' => true,
  118.         'mixed' => true,
  119.     ];
  120.     public function __construct(ParameterBagInterface $parameterBag null)
  121.     {
  122.         parent::__construct($parameterBag);
  123.         $this->trackResources interface_exists(ResourceInterface::class);
  124.         $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
  125.         $this->setAlias(PsrContainerInterface::class, new Alias('service_container'false));
  126.         $this->setAlias(ContainerInterface::class, new Alias('service_container'false));
  127.     }
  128.     /**
  129.      * @var \ReflectionClass[] a list of class reflectors
  130.      */
  131.     private $classReflectors;
  132.     /**
  133.      * Sets the track resources flag.
  134.      *
  135.      * If you are not using the loaders and therefore don't want
  136.      * to depend on the Config component, set this flag to false.
  137.      *
  138.      * @param bool $track True if you want to track resources, false otherwise
  139.      */
  140.     public function setResourceTracking($track)
  141.     {
  142.         $this->trackResources = (bool) $track;
  143.     }
  144.     /**
  145.      * Checks if resources are tracked.
  146.      *
  147.      * @return bool true If resources are tracked, false otherwise
  148.      */
  149.     public function isTrackingResources()
  150.     {
  151.         return $this->trackResources;
  152.     }
  153.     /**
  154.      * Sets the instantiator to be used when fetching proxies.
  155.      */
  156.     public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
  157.     {
  158.         $this->proxyInstantiator $proxyInstantiator;
  159.     }
  160.     public function registerExtension(ExtensionInterface $extension)
  161.     {
  162.         $this->extensions[$extension->getAlias()] = $extension;
  163.         if (false !== $extension->getNamespace()) {
  164.             $this->extensionsByNs[$extension->getNamespace()] = $extension;
  165.         }
  166.     }
  167.     /**
  168.      * Returns an extension by alias or namespace.
  169.      *
  170.      * @param string $name An alias or a namespace
  171.      *
  172.      * @return ExtensionInterface An extension instance
  173.      *
  174.      * @throws LogicException if the extension is not registered
  175.      */
  176.     public function getExtension($name)
  177.     {
  178.         if (isset($this->extensions[$name])) {
  179.             return $this->extensions[$name];
  180.         }
  181.         if (isset($this->extensionsByNs[$name])) {
  182.             return $this->extensionsByNs[$name];
  183.         }
  184.         throw new LogicException(sprintf('Container extension "%s" is not registered.'$name));
  185.     }
  186.     /**
  187.      * Returns all registered extensions.
  188.      *
  189.      * @return ExtensionInterface[] An array of ExtensionInterface
  190.      */
  191.     public function getExtensions()
  192.     {
  193.         return $this->extensions;
  194.     }
  195.     /**
  196.      * Checks if we have an extension.
  197.      *
  198.      * @param string $name The name of the extension
  199.      *
  200.      * @return bool If the extension exists
  201.      */
  202.     public function hasExtension($name)
  203.     {
  204.         return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
  205.     }
  206.     /**
  207.      * Returns an array of resources loaded to build this configuration.
  208.      *
  209.      * @return ResourceInterface[] An array of resources
  210.      */
  211.     public function getResources()
  212.     {
  213.         return array_values($this->resources);
  214.     }
  215.     /**
  216.      * @return $this
  217.      */
  218.     public function addResource(ResourceInterface $resource)
  219.     {
  220.         if (!$this->trackResources) {
  221.             return $this;
  222.         }
  223.         if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
  224.             return $this;
  225.         }
  226.         $this->resources[(string) $resource] = $resource;
  227.         return $this;
  228.     }
  229.     /**
  230.      * Sets the resources for this configuration.
  231.      *
  232.      * @param ResourceInterface[] $resources An array of resources
  233.      *
  234.      * @return $this
  235.      */
  236.     public function setResources(array $resources)
  237.     {
  238.         if (!$this->trackResources) {
  239.             return $this;
  240.         }
  241.         $this->resources $resources;
  242.         return $this;
  243.     }
  244.     /**
  245.      * Adds the object class hierarchy as resources.
  246.      *
  247.      * @param object|string $object An object instance or class name
  248.      *
  249.      * @return $this
  250.      */
  251.     public function addObjectResource($object)
  252.     {
  253.         if ($this->trackResources) {
  254.             if (\is_object($object)) {
  255.                 $object = \get_class($object);
  256.             }
  257.             if (!isset($this->classReflectors[$object])) {
  258.                 $this->classReflectors[$object] = new \ReflectionClass($object);
  259.             }
  260.             $class $this->classReflectors[$object];
  261.             foreach ($class->getInterfaceNames() as $name) {
  262.                 if (null === $interface = &$this->classReflectors[$name]) {
  263.                     $interface = new \ReflectionClass($name);
  264.                 }
  265.                 $file $interface->getFileName();
  266.                 if (false !== $file && file_exists($file)) {
  267.                     $this->fileExists($file);
  268.                 }
  269.             }
  270.             do {
  271.                 $file $class->getFileName();
  272.                 if (false !== $file && file_exists($file)) {
  273.                     $this->fileExists($file);
  274.                 }
  275.                 foreach ($class->getTraitNames() as $name) {
  276.                     $this->addObjectResource($name);
  277.                 }
  278.             } while ($class $class->getParentClass());
  279.         }
  280.         return $this;
  281.     }
  282.     /**
  283.      * Retrieves the requested reflection class and registers it for resource tracking.
  284.      *
  285.      * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
  286.      *
  287.      * @final
  288.      */
  289.     public function getReflectionClass(?string $classbool $throw true): ?\ReflectionClass
  290.     {
  291.         if (!$class $this->getParameterBag()->resolveValue($class)) {
  292.             return null;
  293.         }
  294.         if (isset(self::INTERNAL_TYPES[$class])) {
  295.             return null;
  296.         }
  297.         $resource $classReflector null;
  298.         try {
  299.             if (isset($this->classReflectors[$class])) {
  300.                 $classReflector $this->classReflectors[$class];
  301.             } elseif (class_exists(ClassExistenceResource::class)) {
  302.                 $resource = new ClassExistenceResource($classfalse);
  303.                 $classReflector $resource->isFresh(0) ? false : new \ReflectionClass($class);
  304.             } else {
  305.                 $classReflector class_exists($class) ? new \ReflectionClass($class) : false;
  306.             }
  307.         } catch (\ReflectionException $e) {
  308.             if ($throw) {
  309.                 throw $e;
  310.             }
  311.         }
  312.         if ($this->trackResources) {
  313.             if (!$classReflector) {
  314.                 $this->addResource($resource ?: new ClassExistenceResource($classfalse));
  315.             } elseif (!$classReflector->isInternal()) {
  316.                 $path $classReflector->getFileName();
  317.                 if (!$this->inVendors($path)) {
  318.                     $this->addResource(new ReflectionClassResource($classReflector$this->vendors));
  319.                 }
  320.             }
  321.             $this->classReflectors[$class] = $classReflector;
  322.         }
  323.         return $classReflector ?: null;
  324.     }
  325.     /**
  326.      * Checks whether the requested file or directory exists and registers the result for resource tracking.
  327.      *
  328.      * @param string      $path          The file or directory path for which to check the existence
  329.      * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
  330.      *                                   it will be used as pattern for tracking contents of the requested directory
  331.      *
  332.      * @final
  333.      */
  334.     public function fileExists(string $path$trackContents true): bool
  335.     {
  336.         $exists file_exists($path);
  337.         if (!$this->trackResources || $this->inVendors($path)) {
  338.             return $exists;
  339.         }
  340.         if (!$exists) {
  341.             $this->addResource(new FileExistenceResource($path));
  342.             return $exists;
  343.         }
  344.         if (is_dir($path)) {
  345.             if ($trackContents) {
  346.                 $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents null));
  347.             } else {
  348.                 $this->addResource(new GlobResource($path'/*'false));
  349.             }
  350.         } elseif ($trackContents) {
  351.             $this->addResource(new FileResource($path));
  352.         }
  353.         return $exists;
  354.     }
  355.     /**
  356.      * Loads the configuration for an extension.
  357.      *
  358.      * @param string $extension The extension alias or namespace
  359.      * @param array  $values    An array of values that customizes the extension
  360.      *
  361.      * @return $this
  362.      *
  363.      * @throws BadMethodCallException When this ContainerBuilder is compiled
  364.      * @throws \LogicException        if the extension is not registered
  365.      */
  366.     public function loadFromExtension($extension, array $values null)
  367.     {
  368.         if ($this->isCompiled()) {
  369.             throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
  370.         }
  371.         if (\func_num_args() < 2) {
  372.             $values = [];
  373.         }
  374.         $namespace $this->getExtension($extension)->getAlias();
  375.         $this->extensionConfigs[$namespace][] = $values;
  376.         return $this;
  377.     }
  378.     /**
  379.      * Adds a compiler pass.
  380.      *
  381.      * @param string $type     The type of compiler pass
  382.      * @param int    $priority Used to sort the passes
  383.      *
  384.      * @return $this
  385.      */
  386.     public function addCompilerPass(CompilerPassInterface $pass$type PassConfig::TYPE_BEFORE_OPTIMIZATIONint $priority 0)
  387.     {
  388.         $this->getCompiler()->addPass($pass$type$priority);
  389.         $this->addObjectResource($pass);
  390.         return $this;
  391.     }
  392.     /**
  393.      * Returns the compiler pass config which can then be modified.
  394.      *
  395.      * @return PassConfig The compiler pass config
  396.      */
  397.     public function getCompilerPassConfig()
  398.     {
  399.         return $this->getCompiler()->getPassConfig();
  400.     }
  401.     /**
  402.      * Returns the compiler.
  403.      *
  404.      * @return Compiler The compiler
  405.      */
  406.     public function getCompiler()
  407.     {
  408.         if (null === $this->compiler) {
  409.             $this->compiler = new Compiler();
  410.         }
  411.         return $this->compiler;
  412.     }
  413.     /**
  414.      * Sets a service.
  415.      *
  416.      * @param string      $id      The service identifier
  417.      * @param object|null $service The service instance
  418.      *
  419.      * @throws BadMethodCallException When this ContainerBuilder is compiled
  420.      */
  421.     public function set($id$service)
  422.     {
  423.         if (!\is_object($service) && null !== $service) {
  424.             @trigger_error(sprintf('Non-object services are deprecated since Symfony 4.4, setting the "%s" service to a value of type "%s" should be avoided.'$id, \gettype($service)), \E_USER_DEPRECATED);
  425.         }
  426.         $id = (string) $id;
  427.         if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
  428.             // setting a synthetic service on a compiled container is alright
  429.             throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.'$id));
  430.         }
  431.         unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
  432.         parent::set($id$service);
  433.     }
  434.     /**
  435.      * Removes a service definition.
  436.      *
  437.      * @param string $id The service identifier
  438.      */
  439.     public function removeDefinition($id)
  440.     {
  441.         if (isset($this->definitions[$id = (string) $id])) {
  442.             unset($this->definitions[$id]);
  443.             $this->removedIds[$id] = true;
  444.         }
  445.     }
  446.     /**
  447.      * Returns true if the given service is defined.
  448.      *
  449.      * @param string $id The service identifier
  450.      *
  451.      * @return bool true if the service is defined, false otherwise
  452.      */
  453.     public function has($id)
  454.     {
  455.         $id = (string) $id;
  456.         return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
  457.     }
  458.     /**
  459.      * Gets a service.
  460.      *
  461.      * @param string $id              The service identifier
  462.      * @param int    $invalidBehavior The behavior when the service does not exist
  463.      *
  464.      * @return object|null The associated service
  465.      *
  466.      * @throws InvalidArgumentException          when no definitions are available
  467.      * @throws ServiceCircularReferenceException When a circular reference is detected
  468.      * @throws ServiceNotFoundException          When the service is not defined
  469.      * @throws \Exception
  470.      *
  471.      * @see Reference
  472.      */
  473.     public function get($id$invalidBehavior ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
  474.     {
  475.         if ($this->isCompiled() && isset($this->removedIds[$id = (string) $id]) && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $invalidBehavior) {
  476.             return parent::get($id);
  477.         }
  478.         $service $this->doGet($id$invalidBehavior);
  479.         if (!\is_object($service) && null !== $service) {
  480.             @trigger_error(sprintf('Non-object services are deprecated since Symfony 4.4, please fix the "%s" service which is of type "%s" right now.'$id, \gettype($service)), \E_USER_DEPRECATED);
  481.         }
  482.         return $service;
  483.     }
  484.     private function doGet(string $idint $invalidBehavior ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices nullbool $isConstructorArgument false)
  485.     {
  486.         if (isset($inlineServices[$id])) {
  487.             return $inlineServices[$id];
  488.         }
  489.         if (null === $inlineServices) {
  490.             $isConstructorArgument true;
  491.             $inlineServices = [];
  492.         }
  493.         try {
  494.             if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
  495.                 return parent::get($id$invalidBehavior);
  496.             }
  497.             if ($service parent::get($idContainerInterface::NULL_ON_INVALID_REFERENCE)) {
  498.                 return $service;
  499.             }
  500.         } catch (ServiceCircularReferenceException $e) {
  501.             if ($isConstructorArgument) {
  502.                 throw $e;
  503.             }
  504.         }
  505.         if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
  506.             $alias $this->aliasDefinitions[$id];
  507.             if ($alias->isDeprecated()) {
  508.                 @trigger_error($alias->getDeprecationMessage($id), \E_USER_DEPRECATED);
  509.             }
  510.             return $this->doGet((string) $alias$invalidBehavior$inlineServices$isConstructorArgument);
  511.         }
  512.         try {
  513.             $definition $this->getDefinition($id);
  514.         } catch (ServiceNotFoundException $e) {
  515.             if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE $invalidBehavior) {
  516.                 return null;
  517.             }
  518.             throw $e;
  519.         }
  520.         if ($definition->hasErrors() && $e $definition->getErrors()) {
  521.             throw new RuntimeException(reset($e));
  522.         }
  523.         if ($isConstructorArgument) {
  524.             $this->loading[$id] = true;
  525.         }
  526.         try {
  527.             return $this->createService($definition$inlineServices$isConstructorArgument$id);
  528.         } finally {
  529.             if ($isConstructorArgument) {
  530.                 unset($this->loading[$id]);
  531.             }
  532.         }
  533.     }
  534.     /**
  535.      * Merges a ContainerBuilder with the current ContainerBuilder configuration.
  536.      *
  537.      * Service definitions overrides the current defined ones.
  538.      *
  539.      * But for parameters, they are overridden by the current ones. It allows
  540.      * the parameters passed to the container constructor to have precedence
  541.      * over the loaded ones.
  542.      *
  543.      *     $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar']));
  544.      *     $loader = new LoaderXXX($container);
  545.      *     $loader->load('resource_name');
  546.      *     $container->register('foo', 'stdClass');
  547.      *
  548.      * In the above example, even if the loaded resource defines a foo
  549.      * parameter, the value will still be 'bar' as defined in the ContainerBuilder
  550.      * constructor.
  551.      *
  552.      * @throws BadMethodCallException When this ContainerBuilder is compiled
  553.      */
  554.     public function merge(self $container)
  555.     {
  556.         if ($this->isCompiled()) {
  557.             throw new BadMethodCallException('Cannot merge on a compiled container.');
  558.         }
  559.         $this->addDefinitions($container->getDefinitions());
  560.         $this->addAliases($container->getAliases());
  561.         $this->getParameterBag()->add($container->getParameterBag()->all());
  562.         if ($this->trackResources) {
  563.             foreach ($container->getResources() as $resource) {
  564.                 $this->addResource($resource);
  565.             }
  566.         }
  567.         foreach ($this->extensions as $name => $extension) {
  568.             if (!isset($this->extensionConfigs[$name])) {
  569.                 $this->extensionConfigs[$name] = [];
  570.             }
  571.             $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
  572.         }
  573.         if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
  574.             $envPlaceholders $container->getParameterBag()->getEnvPlaceholders();
  575.             $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
  576.         } else {
  577.             $envPlaceholders = [];
  578.         }
  579.         foreach ($container->envCounters as $env => $count) {
  580.             if (!$count && !isset($envPlaceholders[$env])) {
  581.                 continue;
  582.             }
  583.             if (!isset($this->envCounters[$env])) {
  584.                 $this->envCounters[$env] = $count;
  585.             } else {
  586.                 $this->envCounters[$env] += $count;
  587.             }
  588.         }
  589.         foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
  590.             if (isset($this->autoconfiguredInstanceof[$interface])) {
  591.                 throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.'$interface));
  592.             }
  593.             $this->autoconfiguredInstanceof[$interface] = $childDefinition;
  594.         }
  595.     }
  596.     /**
  597.      * Returns the configuration array for the given extension.
  598.      *
  599.      * @param string $name The name of the extension
  600.      *
  601.      * @return array An array of configuration
  602.      */
  603.     public function getExtensionConfig($name)
  604.     {
  605.         if (!isset($this->extensionConfigs[$name])) {
  606.             $this->extensionConfigs[$name] = [];
  607.         }
  608.         return $this->extensionConfigs[$name];
  609.     }
  610.     /**
  611.      * Prepends a config array to the configs of the given extension.
  612.      *
  613.      * @param string $name   The name of the extension
  614.      * @param array  $config The config to set
  615.      */
  616.     public function prependExtensionConfig($name, array $config)
  617.     {
  618.         if (!isset($this->extensionConfigs[$name])) {
  619.             $this->extensionConfigs[$name] = [];
  620.         }
  621.         array_unshift($this->extensionConfigs[$name], $config);
  622.     }
  623.     /**
  624.      * Compiles the container.
  625.      *
  626.      * This method passes the container to compiler
  627.      * passes whose job is to manipulate and optimize
  628.      * the container.
  629.      *
  630.      * The main compiler passes roughly do four things:
  631.      *
  632.      *  * The extension configurations are merged;
  633.      *  * Parameter values are resolved;
  634.      *  * The parameter bag is frozen;
  635.      *  * Extension loading is disabled.
  636.      *
  637.      * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
  638.      *                                     env vars or be replaced by uniquely identifiable placeholders.
  639.      *                                     Set to "true" when you want to use the current ContainerBuilder
  640.      *                                     directly, keep to "false" when the container is dumped instead.
  641.      */
  642.     public function compile(bool $resolveEnvPlaceholders false)
  643.     {
  644.         $compiler $this->getCompiler();
  645.         if ($this->trackResources) {
  646.             foreach ($compiler->getPassConfig()->getPasses() as $pass) {
  647.                 $this->addObjectResource($pass);
  648.             }
  649.         }
  650.         $bag $this->getParameterBag();
  651.         if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
  652.             $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
  653.         }
  654.         $compiler->compile($this);
  655.         foreach ($this->definitions as $id => $definition) {
  656.             if ($this->trackResources && $definition->isLazy()) {
  657.                 $this->getReflectionClass($definition->getClass());
  658.             }
  659.         }
  660.         $this->extensionConfigs = [];
  661.         if ($bag instanceof EnvPlaceholderParameterBag) {
  662.             if ($resolveEnvPlaceholders) {
  663.                 $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
  664.             }
  665.             $this->envPlaceholders $bag->getEnvPlaceholders();
  666.         }
  667.         parent::compile();
  668.         foreach ($this->definitions $this->aliasDefinitions as $id => $definition) {
  669.             if (!$definition->isPublic() || $definition->isPrivate()) {
  670.                 $this->removedIds[$id] = true;
  671.             }
  672.         }
  673.     }
  674.     /**
  675.      * {@inheritdoc}
  676.      */
  677.     public function getServiceIds()
  678.     {
  679.         return array_map('strval'array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())));
  680.     }
  681.     /**
  682.      * Gets removed service or alias ids.
  683.      *
  684.      * @return array
  685.      */
  686.     public function getRemovedIds()
  687.     {
  688.         return $this->removedIds;
  689.     }
  690.     /**
  691.      * Adds the service aliases.
  692.      */
  693.     public function addAliases(array $aliases)
  694.     {
  695.         foreach ($aliases as $alias => $id) {
  696.             $this->setAlias($alias$id);
  697.         }
  698.     }
  699.     /**
  700.      * Sets the service aliases.
  701.      */
  702.     public function setAliases(array $aliases)
  703.     {
  704.         $this->aliasDefinitions = [];
  705.         $this->addAliases($aliases);
  706.     }
  707.     /**
  708.      * Sets an alias for an existing service.
  709.      *
  710.      * @param string       $alias The alias to create
  711.      * @param string|Alias $id    The service to alias
  712.      *
  713.      * @return Alias
  714.      *
  715.      * @throws InvalidArgumentException if the id is not a string or an Alias
  716.      * @throws InvalidArgumentException if the alias is for itself
  717.      */
  718.     public function setAlias($alias$id)
  719.     {
  720.         $alias = (string) $alias;
  721.         if ('' === $alias || '\\' === $alias[-1] || \strlen($alias) !== strcspn($alias"\0\r\n'")) {
  722.             throw new InvalidArgumentException(sprintf('Invalid alias id: "%s".'$alias));
  723.         }
  724.         if (\is_string($id)) {
  725.             $id = new Alias($id);
  726.         } elseif (!$id instanceof Alias) {
  727.             throw new InvalidArgumentException('$id must be a string, or an Alias object.');
  728.         }
  729.         if ($alias === (string) $id) {
  730.             throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".'$alias));
  731.         }
  732.         unset($this->definitions[$alias], $this->removedIds[$alias]);
  733.         return $this->aliasDefinitions[$alias] = $id;
  734.     }
  735.     /**
  736.      * Removes an alias.
  737.      *
  738.      * @param string $alias The alias to remove
  739.      */
  740.     public function removeAlias($alias)
  741.     {
  742.         if (isset($this->aliasDefinitions[$alias = (string) $alias])) {
  743.             unset($this->aliasDefinitions[$alias]);
  744.             $this->removedIds[$alias] = true;
  745.         }
  746.     }
  747.     /**
  748.      * Returns true if an alias exists under the given identifier.
  749.      *
  750.      * @param string $id The service identifier
  751.      *
  752.      * @return bool true if the alias exists, false otherwise
  753.      */
  754.     public function hasAlias($id)
  755.     {
  756.         return isset($this->aliasDefinitions[$id = (string) $id]);
  757.     }
  758.     /**
  759.      * Gets all defined aliases.
  760.      *
  761.      * @return Alias[] An array of aliases
  762.      */
  763.     public function getAliases()
  764.     {
  765.         return $this->aliasDefinitions;
  766.     }
  767.     /**
  768.      * Gets an alias.
  769.      *
  770.      * @param string $id The service identifier
  771.      *
  772.      * @return Alias An Alias instance
  773.      *
  774.      * @throws InvalidArgumentException if the alias does not exist
  775.      */
  776.     public function getAlias($id)
  777.     {
  778.         $id = (string) $id;
  779.         if (!isset($this->aliasDefinitions[$id])) {
  780.             throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.'$id));
  781.         }
  782.         return $this->aliasDefinitions[$id];
  783.     }
  784.     /**
  785.      * Registers a service definition.
  786.      *
  787.      * This methods allows for simple registration of service definition
  788.      * with a fluid interface.
  789.      *
  790.      * @param string      $id    The service identifier
  791.      * @param string|null $class The service class
  792.      *
  793.      * @return Definition A Definition instance
  794.      */
  795.     public function register($id$class null)
  796.     {
  797.         return $this->setDefinition($id, new Definition($class));
  798.     }
  799.     /**
  800.      * Registers an autowired service definition.
  801.      *
  802.      * This method implements a shortcut for using setDefinition() with
  803.      * an autowired definition.
  804.      *
  805.      * @param string      $id    The service identifier
  806.      * @param string|null $class The service class
  807.      *
  808.      * @return Definition The created definition
  809.      */
  810.     public function autowire($id$class null)
  811.     {
  812.         return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
  813.     }
  814.     /**
  815.      * Adds the service definitions.
  816.      *
  817.      * @param Definition[] $definitions An array of service definitions
  818.      */
  819.     public function addDefinitions(array $definitions)
  820.     {
  821.         foreach ($definitions as $id => $definition) {
  822.             $this->setDefinition($id$definition);
  823.         }
  824.     }
  825.     /**
  826.      * Sets the service definitions.
  827.      *
  828.      * @param Definition[] $definitions An array of service definitions
  829.      */
  830.     public function setDefinitions(array $definitions)
  831.     {
  832.         $this->definitions = [];
  833.         $this->addDefinitions($definitions);
  834.     }
  835.     /**
  836.      * Gets all service definitions.
  837.      *
  838.      * @return Definition[] An array of Definition instances
  839.      */
  840.     public function getDefinitions()
  841.     {
  842.         return $this->definitions;
  843.     }
  844.     /**
  845.      * Sets a service definition.
  846.      *
  847.      * @param string $id The service identifier
  848.      *
  849.      * @return Definition the service definition
  850.      *
  851.      * @throws BadMethodCallException When this ContainerBuilder is compiled
  852.      */
  853.     public function setDefinition($idDefinition $definition)
  854.     {
  855.         if ($this->isCompiled()) {
  856.             throw new BadMethodCallException('Adding definition to a compiled container is not allowed.');
  857.         }
  858.         $id = (string) $id;
  859.         if ('' === $id || '\\' === $id[-1] || \strlen($id) !== strcspn($id"\0\r\n'")) {
  860.             throw new InvalidArgumentException(sprintf('Invalid service id: "%s".'$id));
  861.         }
  862.         unset($this->aliasDefinitions[$id], $this->removedIds[$id]);
  863.         return $this->definitions[$id] = $definition;
  864.     }
  865.     /**
  866.      * Returns true if a service definition exists under the given identifier.
  867.      *
  868.      * @param string $id The service identifier
  869.      *
  870.      * @return bool true if the service definition exists, false otherwise
  871.      */
  872.     public function hasDefinition($id)
  873.     {
  874.         return isset($this->definitions[(string) $id]);
  875.     }
  876.     /**
  877.      * Gets a service definition.
  878.      *
  879.      * @param string $id The service identifier
  880.      *
  881.      * @return Definition A Definition instance
  882.      *
  883.      * @throws ServiceNotFoundException if the service definition does not exist
  884.      */
  885.     public function getDefinition($id)
  886.     {
  887.         $id = (string) $id;
  888.         if (!isset($this->definitions[$id])) {
  889.             throw new ServiceNotFoundException($id);
  890.         }
  891.         return $this->definitions[$id];
  892.     }
  893.     /**
  894.      * Gets a service definition by id or alias.
  895.      *
  896.      * The method "unaliases" recursively to return a Definition instance.
  897.      *
  898.      * @param string $id The service identifier or alias
  899.      *
  900.      * @return Definition A Definition instance
  901.      *
  902.      * @throws ServiceNotFoundException if the service definition does not exist
  903.      */
  904.     public function findDefinition($id)
  905.     {
  906.         $id = (string) $id;
  907.         $seen = [];
  908.         while (isset($this->aliasDefinitions[$id])) {
  909.             $id = (string) $this->aliasDefinitions[$id];
  910.             if (isset($seen[$id])) {
  911.                 $seen array_values($seen);
  912.                 $seen = \array_slice($seenarray_search($id$seen));
  913.                 $seen[] = $id;
  914.                 throw new ServiceCircularReferenceException($id$seen);
  915.             }
  916.             $seen[$id] = $id;
  917.         }
  918.         return $this->getDefinition($id);
  919.     }
  920.     /**
  921.      * Creates a service for a service definition.
  922.      *
  923.      * @return mixed The service described by the service definition
  924.      *
  925.      * @throws RuntimeException         When the factory definition is incomplete
  926.      * @throws RuntimeException         When the service is a synthetic service
  927.      * @throws InvalidArgumentException When configure callable is not callable
  928.      */
  929.     private function createService(Definition $definition, array &$inlineServicesbool $isConstructorArgument falsestring $id nullbool $tryProxy true)
  930.     {
  931.         if (null === $id && isset($inlineServices[$h spl_object_hash($definition)])) {
  932.             return $inlineServices[$h];
  933.         }
  934.         if ($definition instanceof ChildDefinition) {
  935.             throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.'$id));
  936.         }
  937.         if ($definition->isSynthetic()) {
  938.             throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.'$id));
  939.         }
  940.         if ($definition->isDeprecated()) {
  941.             @trigger_error($definition->getDeprecationMessage($id), \E_USER_DEPRECATED);
  942.         }
  943.         if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) {
  944.             $proxy $proxy->instantiateProxy(
  945.                 $this,
  946.                 $definition,
  947.                 $id, function () use ($definition, &$inlineServices$id) {
  948.                     return $this->createService($definition$inlineServicestrue$idfalse);
  949.                 }
  950.             );
  951.             $this->shareService($definition$proxy$id$inlineServices);
  952.             return $proxy;
  953.         }
  954.         $parameterBag $this->getParameterBag();
  955.         if (null !== $definition->getFile()) {
  956.             require_once $parameterBag->resolveValue($definition->getFile());
  957.         }
  958.         $arguments $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices$isConstructorArgument);
  959.         if (null !== $factory $definition->getFactory()) {
  960.             if (\is_array($factory)) {
  961.                 $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices$isConstructorArgument), $factory[1]];
  962.             } elseif (!\is_string($factory)) {
  963.                 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.'$id));
  964.             }
  965.         }
  966.         if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) {
  967.             return $this->services[$id];
  968.         }
  969.         if (null !== $factory) {
  970.             $service $factory(...$arguments);
  971.             if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
  972.                 $r = new \ReflectionClass($factory[0]);
  973.                 if (strpos($r->getDocComment(), "\n * @deprecated ")) {
  974.                     @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.'$id$r->name), \E_USER_DEPRECATED);
  975.                 }
  976.             }
  977.         } else {
  978.             $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
  979.             $service null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs(array_values($arguments));
  980.             if (!$definition->isDeprecated() && strpos($r->getDocComment(), "\n * @deprecated ")) {
  981.                 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.'$id$r->name), \E_USER_DEPRECATED);
  982.             }
  983.         }
  984.         $lastWitherIndex null;
  985.         foreach ($definition->getMethodCalls() as $k => $call) {
  986.             if ($call[2] ?? false) {
  987.                 $lastWitherIndex $k;
  988.             }
  989.         }
  990.         if (null === $lastWitherIndex && ($tryProxy || !$definition->isLazy())) {
  991.             // share only if proxying failed, or if not a proxy, and if no withers are found
  992.             $this->shareService($definition$service$id$inlineServices);
  993.         }
  994.         $properties $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices);
  995.         foreach ($properties as $name => $value) {
  996.             $service->$name $value;
  997.         }
  998.         foreach ($definition->getMethodCalls() as $k => $call) {
  999.             $service $this->callMethod($service$call$inlineServices);
  1000.             if ($lastWitherIndex === $k && ($tryProxy || !$definition->isLazy())) {
  1001.                 // share only if proxying failed, or if not a proxy, and this is the last wither
  1002.                 $this->shareService($definition$service$id$inlineServices);
  1003.             }
  1004.         }
  1005.         if ($callable $definition->getConfigurator()) {
  1006.             if (\is_array($callable)) {
  1007.                 $callable[0] = $parameterBag->resolveValue($callable[0]);
  1008.                 if ($callable[0] instanceof Reference) {
  1009.                     $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices);
  1010.                 } elseif ($callable[0] instanceof Definition) {
  1011.                     $callable[0] = $this->createService($callable[0], $inlineServices);
  1012.                 }
  1013.             }
  1014.             if (!\is_callable($callable)) {
  1015.                 throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service)));
  1016.             }
  1017.             $callable($service);
  1018.         }
  1019.         return $service;
  1020.     }
  1021.     /**
  1022.      * Replaces service references by the real service instance and evaluates expressions.
  1023.      *
  1024.      * @param mixed $value A value
  1025.      *
  1026.      * @return mixed The same value with all service references replaced by
  1027.      *               the real service instances and all expressions evaluated
  1028.      */
  1029.     public function resolveServices($value)
  1030.     {
  1031.         return $this->doResolveServices($value);
  1032.     }
  1033.     private function doResolveServices($value, array &$inlineServices = [], bool $isConstructorArgument false)
  1034.     {
  1035.         if (\is_array($value)) {
  1036.             foreach ($value as $k => $v) {
  1037.                 $value[$k] = $this->doResolveServices($v$inlineServices$isConstructorArgument);
  1038.             }
  1039.         } elseif ($value instanceof ServiceClosureArgument) {
  1040.             $reference $value->getValues()[0];
  1041.             $value = function () use ($reference) {
  1042.                 return $this->resolveServices($reference);
  1043.             };
  1044.         } elseif ($value instanceof IteratorArgument) {
  1045.             $value = new RewindableGenerator(function () use ($value, &$inlineServices) {
  1046.                 foreach ($value->getValues() as $k => $v) {
  1047.                     foreach (self::getServiceConditionals($v) as $s) {
  1048.                         if (!$this->has($s)) {
  1049.                             continue 2;
  1050.                         }
  1051.                     }
  1052.                     foreach (self::getInitializedConditionals($v) as $s) {
  1053.                         if (!$this->doGet($sContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE$inlineServices)) {
  1054.                             continue 2;
  1055.                         }
  1056.                     }
  1057.                     yield $k => $this->doResolveServices($v$inlineServices);
  1058.                 }
  1059.             }, function () use ($value): int {
  1060.                 $count 0;
  1061.                 foreach ($value->getValues() as $v) {
  1062.                     foreach (self::getServiceConditionals($v) as $s) {
  1063.                         if (!$this->has($s)) {
  1064.                             continue 2;
  1065.                         }
  1066.                     }
  1067.                     foreach (self::getInitializedConditionals($v) as $s) {
  1068.                         if (!$this->doGet($sContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
  1069.                             continue 2;
  1070.                         }
  1071.                     }
  1072.                     ++$count;
  1073.                 }
  1074.                 return $count;
  1075.             });
  1076.         } elseif ($value instanceof ServiceLocatorArgument) {
  1077.             $refs $types = [];
  1078.             foreach ($value->getValues() as $k => $v) {
  1079.                 if ($v) {
  1080.                     $refs[$k] = [$v];
  1081.                     $types[$k] = $v instanceof TypedReference $v->getType() : '?';
  1082.                 }
  1083.             }
  1084.             $value = new ServiceLocator(\Closure::fromCallable([$this'resolveServices']), $refs$types);
  1085.         } elseif ($value instanceof Reference) {
  1086.             $value $this->doGet((string) $value$value->getInvalidBehavior(), $inlineServices$isConstructorArgument);
  1087.         } elseif ($value instanceof Definition) {
  1088.             $value $this->createService($value$inlineServices$isConstructorArgument);
  1089.         } elseif ($value instanceof Parameter) {
  1090.             $value $this->getParameter((string) $value);
  1091.         } elseif ($value instanceof Expression) {
  1092.             $value $this->getExpressionLanguage()->evaluate($value, ['container' => $this]);
  1093.         }
  1094.         return $value;
  1095.     }
  1096.     /**
  1097.      * Returns service ids for a given tag.
  1098.      *
  1099.      * Example:
  1100.      *
  1101.      *     $container->register('foo')->addTag('my.tag', ['hello' => 'world']);
  1102.      *
  1103.      *     $serviceIds = $container->findTaggedServiceIds('my.tag');
  1104.      *     foreach ($serviceIds as $serviceId => $tags) {
  1105.      *         foreach ($tags as $tag) {
  1106.      *             echo $tag['hello'];
  1107.      *         }
  1108.      *     }
  1109.      *
  1110.      * @param string $name
  1111.      * @param bool   $throwOnAbstract
  1112.      *
  1113.      * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
  1114.      */
  1115.     public function findTaggedServiceIds($name$throwOnAbstract false)
  1116.     {
  1117.         $this->usedTags[] = $name;
  1118.         $tags = [];
  1119.         foreach ($this->getDefinitions() as $id => $definition) {
  1120.             if ($definition->hasTag($name)) {
  1121.                 if ($throwOnAbstract && $definition->isAbstract()) {
  1122.                     throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.'$id$name));
  1123.                 }
  1124.                 $tags[$id] = $definition->getTag($name);
  1125.             }
  1126.         }
  1127.         return $tags;
  1128.     }
  1129.     /**
  1130.      * Returns all tags the defined services use.
  1131.      *
  1132.      * @return array An array of tags
  1133.      */
  1134.     public function findTags()
  1135.     {
  1136.         $tags = [];
  1137.         foreach ($this->getDefinitions() as $id => $definition) {
  1138.             $tags array_merge(array_keys($definition->getTags()), $tags);
  1139.         }
  1140.         return array_unique($tags);
  1141.     }
  1142.     /**
  1143.      * Returns all tags not queried by findTaggedServiceIds.
  1144.      *
  1145.      * @return string[] An array of tags
  1146.      */
  1147.     public function findUnusedTags()
  1148.     {
  1149.         return array_values(array_diff($this->findTags(), $this->usedTags));
  1150.     }
  1151.     public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
  1152.     {
  1153.         $this->expressionLanguageProviders[] = $provider;
  1154.     }
  1155.     /**
  1156.      * @return ExpressionFunctionProviderInterface[]
  1157.      */
  1158.     public function getExpressionLanguageProviders()
  1159.     {
  1160.         return $this->expressionLanguageProviders;
  1161.     }
  1162.     /**
  1163.      * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
  1164.      *
  1165.      * @param string $interface The class or interface to match
  1166.      *
  1167.      * @return ChildDefinition
  1168.      */
  1169.     public function registerForAutoconfiguration($interface)
  1170.     {
  1171.         if (!isset($this->autoconfiguredInstanceof[$interface])) {
  1172.             $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
  1173.         }
  1174.         return $this->autoconfiguredInstanceof[$interface];
  1175.     }
  1176.     /**
  1177.      * Registers an autowiring alias that only binds to a specific argument name.
  1178.      *
  1179.      * The argument name is derived from $name if provided (from $id otherwise)
  1180.      * using camel case: "foo.bar" or "foo_bar" creates an alias bound to
  1181.      * "$fooBar"-named arguments with $type as type-hint. Such arguments will
  1182.      * receive the service $id when autowiring is used.
  1183.      */
  1184.     public function registerAliasForArgument(string $idstring $typestring $name null): Alias
  1185.     {
  1186.         $name lcfirst(str_replace(' '''ucwords(preg_replace('/[^a-zA-Z0-9\x7f-\xff]++/'' '$name ?? $id))));
  1187.         if (!preg_match('/^[a-zA-Z_\x7f-\xff]/'$name)) {
  1188.             throw new InvalidArgumentException(sprintf('Invalid argument name "%s" for service "%s": the first character must be a letter.'$name$id));
  1189.         }
  1190.         return $this->setAlias($type.' $'.$name$id);
  1191.     }
  1192.     /**
  1193.      * Returns an array of ChildDefinition[] keyed by interface.
  1194.      *
  1195.      * @return ChildDefinition[]
  1196.      */
  1197.     public function getAutoconfiguredInstanceof()
  1198.     {
  1199.         return $this->autoconfiguredInstanceof;
  1200.     }
  1201.     /**
  1202.      * Resolves env parameter placeholders in a string or an array.
  1203.      *
  1204.      * @param mixed            $value     The value to resolve
  1205.      * @param string|true|null $format    A sprintf() format returning the replacement for each env var name or
  1206.      *                                    null to resolve back to the original "%env(VAR)%" format or
  1207.      *                                    true to resolve to the actual values of the referenced env vars
  1208.      * @param array            &$usedEnvs Env vars found while resolving are added to this array
  1209.      *
  1210.      * @return mixed The value with env parameters resolved if a string or an array is passed
  1211.      */
  1212.     public function resolveEnvPlaceholders($value$format null, array &$usedEnvs null)
  1213.     {
  1214.         if (null === $format) {
  1215.             $format '%%env(%s)%%';
  1216.         }
  1217.         $bag $this->getParameterBag();
  1218.         if (true === $format) {
  1219.             $value $bag->resolveValue($value);
  1220.         }
  1221.         if ($value instanceof Definition) {
  1222.             $value = (array) $value;
  1223.         }
  1224.         if (\is_array($value)) {
  1225.             $result = [];
  1226.             foreach ($value as $k => $v) {
  1227.                 $result[\is_string($k) ? $this->resolveEnvPlaceholders($k$format$usedEnvs) : $k] = $this->resolveEnvPlaceholders($v$format$usedEnvs);
  1228.             }
  1229.             return $result;
  1230.         }
  1231.         if (!\is_string($value) || 38 > \strlen($value)) {
  1232.             return $value;
  1233.         }
  1234.         $envPlaceholders $bag instanceof EnvPlaceholderParameterBag $bag->getEnvPlaceholders() : $this->envPlaceholders;
  1235.         $completed false;
  1236.         foreach ($envPlaceholders as $env => $placeholders) {
  1237.             foreach ($placeholders as $placeholder) {
  1238.                 if (false !== stripos($value$placeholder)) {
  1239.                     if (true === $format) {
  1240.                         $resolved $bag->escapeValue($this->getEnv($env));
  1241.                     } else {
  1242.                         $resolved sprintf($format$env);
  1243.                     }
  1244.                     if ($placeholder === $value) {
  1245.                         $value $resolved;
  1246.                         $completed true;
  1247.                     } else {
  1248.                         if (!\is_string($resolved) && !is_numeric($resolved)) {
  1249.                             throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type "%s" inside string value "%s".'$env, \gettype($resolved), $this->resolveEnvPlaceholders($value)));
  1250.                         }
  1251.                         $value str_ireplace($placeholder$resolved$value);
  1252.                     }
  1253.                     $usedEnvs[$env] = $env;
  1254.                     $this->envCounters[$env] = isset($this->envCounters[$env]) ? $this->envCounters[$env] : 1;
  1255.                     if ($completed) {
  1256.                         break 2;
  1257.                     }
  1258.                 }
  1259.             }
  1260.         }
  1261.         return $value;
  1262.     }
  1263.     /**
  1264.      * Get statistics about env usage.
  1265.      *
  1266.      * @return int[] The number of time each env vars has been resolved
  1267.      */
  1268.     public function getEnvCounters()
  1269.     {
  1270.         $bag $this->getParameterBag();
  1271.         $envPlaceholders $bag instanceof EnvPlaceholderParameterBag $bag->getEnvPlaceholders() : $this->envPlaceholders;
  1272.         foreach ($envPlaceholders as $env => $placeholders) {
  1273.             if (!isset($this->envCounters[$env])) {
  1274.                 $this->envCounters[$env] = 0;
  1275.             }
  1276.         }
  1277.         return $this->envCounters;
  1278.     }
  1279.     /**
  1280.      * @final
  1281.      */
  1282.     public function log(CompilerPassInterface $passstring $message)
  1283.     {
  1284.         $this->getCompiler()->log($pass$this->resolveEnvPlaceholders($message));
  1285.     }
  1286.     /**
  1287.      * Gets removed binding ids.
  1288.      *
  1289.      * @internal
  1290.      */
  1291.     public function getRemovedBindingIds(): array
  1292.     {
  1293.         return $this->removedBindingIds;
  1294.     }
  1295.     /**
  1296.      * Removes bindings for a service.
  1297.      *
  1298.      * @internal
  1299.      */
  1300.     public function removeBindings(string $id)
  1301.     {
  1302.         if ($this->hasDefinition($id)) {
  1303.             foreach ($this->getDefinition($id)->getBindings() as $key => $binding) {
  1304.                 [, $bindingId] = $binding->getValues();
  1305.                 $this->removedBindingIds[(int) $bindingId] = true;
  1306.             }
  1307.         }
  1308.     }
  1309.     /**
  1310.      * Returns the Service Conditionals.
  1311.      *
  1312.      * @param mixed $value An array of conditionals to return
  1313.      *
  1314.      * @internal
  1315.      */
  1316.     public static function getServiceConditionals($value): array
  1317.     {
  1318.         $services = [];
  1319.         if (\is_array($value)) {
  1320.             foreach ($value as $v) {
  1321.                 $services array_unique(array_merge($servicesself::getServiceConditionals($v)));
  1322.             }
  1323.         } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
  1324.             $services[] = (string) $value;
  1325.         }
  1326.         return $services;
  1327.     }
  1328.     /**
  1329.      * Returns the initialized conditionals.
  1330.      *
  1331.      * @param mixed $value An array of conditionals to return
  1332.      *
  1333.      * @internal
  1334.      */
  1335.     public static function getInitializedConditionals($value): array
  1336.     {
  1337.         $services = [];
  1338.         if (\is_array($value)) {
  1339.             foreach ($value as $v) {
  1340.                 $services array_unique(array_merge($servicesself::getInitializedConditionals($v)));
  1341.             }
  1342.         } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) {
  1343.             $services[] = (string) $value;
  1344.         }
  1345.         return $services;
  1346.     }
  1347.     /**
  1348.      * Computes a reasonably unique hash of a value.
  1349.      *
  1350.      * @param mixed $value A serializable value
  1351.      *
  1352.      * @return string
  1353.      */
  1354.     public static function hash($value)
  1355.     {
  1356.         $hash substr(base64_encode(hash('sha256'serialize($value), true)), 07);
  1357.         return str_replace(['/''+'], ['.''_'], $hash);
  1358.     }
  1359.     /**
  1360.      * {@inheritdoc}
  1361.      */
  1362.     protected function getEnv($name)
  1363.     {
  1364.         $value parent::getEnv($name);
  1365.         $bag $this->getParameterBag();
  1366.         if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
  1367.             return $value;
  1368.         }
  1369.         $envPlaceholders $bag->getEnvPlaceholders();
  1370.         if (isset($envPlaceholders[$name][$value])) {
  1371.             $bag = new ParameterBag($bag->all());
  1372.             return $bag->unescapeValue($bag->get("env($name)"));
  1373.         }
  1374.         foreach ($envPlaceholders as $env => $placeholders) {
  1375.             if (isset($placeholders[$value])) {
  1376.                 return $this->getEnv($env);
  1377.             }
  1378.         }
  1379.         $this->resolving["env($name)"] = true;
  1380.         try {
  1381.             return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
  1382.         } finally {
  1383.             unset($this->resolving["env($name)"]);
  1384.         }
  1385.     }
  1386.     private function callMethod($service, array $call, array &$inlineServices)
  1387.     {
  1388.         foreach (self::getServiceConditionals($call[1]) as $s) {
  1389.             if (!$this->has($s)) {
  1390.                 return $service;
  1391.             }
  1392.         }
  1393.         foreach (self::getInitializedConditionals($call[1]) as $s) {
  1394.             if (!$this->doGet($sContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE$inlineServices)) {
  1395.                 return $service;
  1396.             }
  1397.         }
  1398.         $result $service->{$call[0]}(...$this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices));
  1399.         return empty($call[2]) ? $service $result;
  1400.     }
  1401.     /**
  1402.      * Shares a given service in the container.
  1403.      *
  1404.      * @param mixed $service
  1405.      */
  1406.     private function shareService(Definition $definition$service, ?string $id, array &$inlineServices)
  1407.     {
  1408.         $inlineServices[null !== $id $id spl_object_hash($definition)] = $service;
  1409.         if (null !== $id && $definition->isShared()) {
  1410.             $this->services[$id] = $service;
  1411.             unset($this->loading[$id]);
  1412.         }
  1413.     }
  1414.     private function getExpressionLanguage(): ExpressionLanguage
  1415.     {
  1416.         if (null === $this->expressionLanguage) {
  1417.             if (!class_exists(\Symfony\Component\ExpressionLanguage\ExpressionLanguage::class)) {
  1418.                 throw new LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
  1419.             }
  1420.             $this->expressionLanguage = new ExpressionLanguage(null$this->expressionLanguageProviders);
  1421.         }
  1422.         return $this->expressionLanguage;
  1423.     }
  1424.     private function inVendors(string $path): bool
  1425.     {
  1426.         if (null === $this->vendors) {
  1427.             $this->vendors = (new ComposerResource())->getVendors();
  1428.         }
  1429.         $path realpath($path) ?: $path;
  1430.         foreach ($this->vendors as $vendor) {
  1431.             if (=== strpos($path$vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
  1432.                 $this->addResource(new FileResource($vendor.'/composer/installed.json'));
  1433.                 return true;
  1434.             }
  1435.         }
  1436.         return false;
  1437.     }
  1438. }