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

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