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

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