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

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