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

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