Verzeichnisstruktur phpBB-3.3.15
- Veröffentlicht
- 28.08.2024
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
ContainerBuilder.php
0001 <?php
0002
0003 /*
0004 * This file is part of the Symfony package.
0005 *
0006 * (c) Fabien Potencier <fabien@symfony.com>
0007 *
0008 * For the full copyright and license information, please view the LICENSE
0009 * file that was distributed with this source code.
0010 */
0011
0012 namespace Symfony\Component\DependencyInjection;
0013
0014 use Psr\Container\ContainerInterface as PsrContainerInterface;
0015 use Symfony\Component\Config\Resource\ClassExistenceResource;
0016 use Symfony\Component\Config\Resource\ComposerResource;
0017 use Symfony\Component\Config\Resource\DirectoryResource;
0018 use Symfony\Component\Config\Resource\FileExistenceResource;
0019 use Symfony\Component\Config\Resource\FileResource;
0020 use Symfony\Component\Config\Resource\GlobResource;
0021 use Symfony\Component\Config\Resource\ReflectionClassResource;
0022 use Symfony\Component\Config\Resource\ResourceInterface;
0023 use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
0024 use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
0025 use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
0026 use Symfony\Component\DependencyInjection\Compiler\Compiler;
0027 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
0028 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
0029 use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
0030 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
0031 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
0032 use Symfony\Component\DependencyInjection\Exception\LogicException;
0033 use Symfony\Component\DependencyInjection\Exception\RuntimeException;
0034 use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
0035 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
0036 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
0037 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
0038 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
0039 use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
0040 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
0041 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
0042 use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
0043 use Symfony\Component\ExpressionLanguage\Expression;
0044 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
0045
0046 /**
0047 * ContainerBuilder is a DI container that provides an API to easily describe services.
0048 *
0049 * @author Fabien Potencier <fabien@symfony.com>
0050 */
0051 class ContainerBuilder extends Container implements TaggedContainerInterface
0052 {
0053 /**
0054 * @var ExtensionInterface[]
0055 */
0056 private $extensions = [];
0057
0058 /**
0059 * @var ExtensionInterface[]
0060 */
0061 private $extensionsByNs = [];
0062
0063 /**
0064 * @var Definition[]
0065 */
0066 private $definitions = [];
0067
0068 /**
0069 * @var Alias[]
0070 */
0071 private $aliasDefinitions = [];
0072
0073 /**
0074 * @var ResourceInterface[]
0075 */
0076 private $resources = [];
0077
0078 private $extensionConfigs = [];
0079
0080 /**
0081 * @var Compiler
0082 */
0083 private $compiler;
0084
0085 private $trackResources;
0086
0087 /**
0088 * @var InstantiatorInterface|null
0089 */
0090 private $proxyInstantiator;
0091
0092 /**
0093 * @var ExpressionLanguage|null
0094 */
0095 private $expressionLanguage;
0096
0097 /**
0098 * @var ExpressionFunctionProviderInterface[]
0099 */
0100 private $expressionLanguageProviders = [];
0101
0102 /**
0103 * @var string[] with tag names used by findTaggedServiceIds
0104 */
0105 private $usedTags = [];
0106
0107 /**
0108 * @var string[][] a map of env var names to their placeholders
0109 */
0110 private $envPlaceholders = [];
0111
0112 /**
0113 * @var int[] a map of env vars to their resolution counter
0114 */
0115 private $envCounters = [];
0116
0117 /**
0118 * @var string[] the list of vendor directories
0119 */
0120 private $vendors;
0121
0122 private $autoconfiguredInstanceof = [];
0123
0124 private $removedIds = [];
0125
0126 private $removedBindingIds = [];
0127
0128 private static $internalTypes = [
0129 'int' => true,
0130 'float' => true,
0131 'string' => true,
0132 'bool' => true,
0133 'resource' => true,
0134 'object' => true,
0135 'array' => true,
0136 'null' => true,
0137 'callable' => true,
0138 'iterable' => true,
0139 'mixed' => true,
0140 ];
0141
0142 public function __construct(ParameterBagInterface $parameterBag = null)
0143 {
0144 parent::__construct($parameterBag);
0145
0146 $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
0147 $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
0148 $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
0149 $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
0150 }
0151
0152 /**
0153 * @var \ReflectionClass[] a list of class reflectors
0154 */
0155 private $classReflectors;
0156
0157 /**
0158 * Sets the track resources flag.
0159 *
0160 * If you are not using the loaders and therefore don't want
0161 * to depend on the Config component, set this flag to false.
0162 *
0163 * @param bool $track True if you want to track resources, false otherwise
0164 */
0165 public function setResourceTracking($track)
0166 {
0167 $this->trackResources = (bool) $track;
0168 }
0169
0170 /**
0171 * Checks if resources are tracked.
0172 *
0173 * @return bool true If resources are tracked, false otherwise
0174 */
0175 public function isTrackingResources()
0176 {
0177 return $this->trackResources;
0178 }
0179
0180 /**
0181 * Sets the instantiator to be used when fetching proxies.
0182 */
0183 public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
0184 {
0185 $this->proxyInstantiator = $proxyInstantiator;
0186 }
0187
0188 public function registerExtension(ExtensionInterface $extension)
0189 {
0190 $this->extensions[$extension->getAlias()] = $extension;
0191
0192 if (false !== $extension->getNamespace()) {
0193 $this->extensionsByNs[$extension->getNamespace()] = $extension;
0194 }
0195 }
0196
0197 /**
0198 * Returns an extension by alias or namespace.
0199 *
0200 * @param string $name An alias or a namespace
0201 *
0202 * @return ExtensionInterface An extension instance
0203 *
0204 * @throws LogicException if the extension is not registered
0205 */
0206 public function getExtension($name)
0207 {
0208 if (isset($this->extensions[$name])) {
0209 return $this->extensions[$name];
0210 }
0211
0212 if (isset($this->extensionsByNs[$name])) {
0213 return $this->extensionsByNs[$name];
0214 }
0215
0216 throw new LogicException(sprintf('Container extension "%s" is not registered.', $name));
0217 }
0218
0219 /**
0220 * Returns all registered extensions.
0221 *
0222 * @return ExtensionInterface[] An array of ExtensionInterface
0223 */
0224 public function getExtensions()
0225 {
0226 return $this->extensions;
0227 }
0228
0229 /**
0230 * Checks if we have an extension.
0231 *
0232 * @param string $name The name of the extension
0233 *
0234 * @return bool If the extension exists
0235 */
0236 public function hasExtension($name)
0237 {
0238 return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
0239 }
0240
0241 /**
0242 * Returns an array of resources loaded to build this configuration.
0243 *
0244 * @return ResourceInterface[] An array of resources
0245 */
0246 public function getResources()
0247 {
0248 return array_values($this->resources);
0249 }
0250
0251 /**
0252 * @return $this
0253 */
0254 public function addResource(ResourceInterface $resource)
0255 {
0256 if (!$this->trackResources) {
0257 return $this;
0258 }
0259
0260 if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
0261 return $this;
0262 }
0263
0264 $this->resources[(string) $resource] = $resource;
0265
0266 return $this;
0267 }
0268
0269 /**
0270 * Sets the resources for this configuration.
0271 *
0272 * @param ResourceInterface[] $resources An array of resources
0273 *
0274 * @return $this
0275 */
0276 public function setResources(array $resources)
0277 {
0278 if (!$this->trackResources) {
0279 return $this;
0280 }
0281
0282 $this->resources = $resources;
0283
0284 return $this;
0285 }
0286
0287 /**
0288 * Adds the object class hierarchy as resources.
0289 *
0290 * @param object|string $object An object instance or class name
0291 *
0292 * @return $this
0293 */
0294 public function addObjectResource($object)
0295 {
0296 if ($this->trackResources) {
0297 if (\is_object($object)) {
0298 $object = \get_class($object);
0299 }
0300 if (!isset($this->classReflectors[$object])) {
0301 $this->classReflectors[$object] = new \ReflectionClass($object);
0302 }
0303 $class = $this->classReflectors[$object];
0304
0305 foreach ($class->getInterfaceNames() as $name) {
0306 if (null === $interface = &$this->classReflectors[$name]) {
0307 $interface = new \ReflectionClass($name);
0308 }
0309 $file = $interface->getFileName();
0310 if (false !== $file && file_exists($file)) {
0311 $this->fileExists($file);
0312 }
0313 }
0314 do {
0315 $file = $class->getFileName();
0316 if (false !== $file && file_exists($file)) {
0317 $this->fileExists($file);
0318 }
0319 foreach ($class->getTraitNames() as $name) {
0320 $this->addObjectResource($name);
0321 }
0322 } while ($class = $class->getParentClass());
0323 }
0324
0325 return $this;
0326 }
0327
0328 /**
0329 * Adds the given class hierarchy as resources.
0330 *
0331 * @return $this
0332 *
0333 * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
0334 */
0335 public function addClassResource(\ReflectionClass $class)
0336 {
0337 @trigger_error('The '.__METHOD__.'() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the addObjectResource() or the getReflectionClass() method instead.', \E_USER_DEPRECATED);
0338
0339 return $this->addObjectResource($class->name);
0340 }
0341
0342 /**
0343 * Retrieves the requested reflection class and registers it for resource tracking.
0344 *
0345 * @param string $class
0346 * @param bool $throw
0347 *
0348 * @return \ReflectionClass|null
0349 *
0350 * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
0351 *
0352 * @final
0353 */
0354 public function getReflectionClass($class, $throw = true)
0355 {
0356 if (!$class = $this->getParameterBag()->resolveValue($class)) {
0357 return null;
0358 }
0359
0360 if (isset(self::$internalTypes[$class])) {
0361 return null;
0362 }
0363
0364 $resource = $classReflector = null;
0365
0366 try {
0367 if (isset($this->classReflectors[$class])) {
0368 $classReflector = $this->classReflectors[$class];
0369 } elseif (class_exists(ClassExistenceResource::class)) {
0370 $resource = new ClassExistenceResource($class, false);
0371 $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
0372 } else {
0373 $classReflector = class_exists($class) ? new \ReflectionClass($class) : false;
0374 }
0375 } catch (\ReflectionException $e) {
0376 if ($throw) {
0377 throw $e;
0378 }
0379 }
0380
0381 if ($this->trackResources) {
0382 if (!$classReflector) {
0383 $this->addResource($resource ?: new ClassExistenceResource($class, false));
0384 } elseif (!$classReflector->isInternal()) {
0385 $path = $classReflector->getFileName();
0386
0387 if (!$this->inVendors($path)) {
0388 $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
0389 }
0390 }
0391 $this->classReflectors[$class] = $classReflector;
0392 }
0393
0394 return $classReflector ?: null;
0395 }
0396
0397 /**
0398 * Checks whether the requested file or directory exists and registers the result for resource tracking.
0399 *
0400 * @param string $path The file or directory path for which to check the existence
0401 * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
0402 * it will be used as pattern for tracking contents of the requested directory
0403 *
0404 * @return bool
0405 *
0406 * @final
0407 */
0408 public function fileExists($path, $trackContents = true)
0409 {
0410 $exists = file_exists($path);
0411
0412 if (!$this->trackResources || $this->inVendors($path)) {
0413 return $exists;
0414 }
0415
0416 if (!$exists) {
0417 $this->addResource(new FileExistenceResource($path));
0418
0419 return $exists;
0420 }
0421
0422 if (is_dir($path)) {
0423 if ($trackContents) {
0424 $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null));
0425 } else {
0426 $this->addResource(new GlobResource($path, '/*', false));
0427 }
0428 } elseif ($trackContents) {
0429 $this->addResource(new FileResource($path));
0430 }
0431
0432 return $exists;
0433 }
0434
0435 /**
0436 * Loads the configuration for an extension.
0437 *
0438 * @param string $extension The extension alias or namespace
0439 * @param array $values An array of values that customizes the extension
0440 *
0441 * @return $this
0442 *
0443 * @throws BadMethodCallException When this ContainerBuilder is compiled
0444 * @throws \LogicException if the extension is not registered
0445 */
0446 public function loadFromExtension($extension, array $values = null)
0447 {
0448 if ($this->isCompiled()) {
0449 throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
0450 }
0451
0452 if (\func_num_args() < 2) {
0453 $values = [];
0454 }
0455
0456 $namespace = $this->getExtension($extension)->getAlias();
0457
0458 $this->extensionConfigs[$namespace][] = $values;
0459
0460 return $this;
0461 }
0462
0463 /**
0464 * Adds a compiler pass.
0465 *
0466 * @param CompilerPassInterface $pass A compiler pass
0467 * @param string $type The type of compiler pass
0468 * @param int $priority Used to sort the passes
0469 *
0470 * @return $this
0471 */
0472 public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
0473 {
0474 if (\func_num_args() >= 3) {
0475 $priority = func_get_arg(2);
0476 } else {
0477 if (__CLASS__ !== static::class) {
0478 $r = new \ReflectionMethod($this, __FUNCTION__);
0479 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
0480 @trigger_error(sprintf('Method %s() will have a third `int $priority = 0` argument in version 4.0. Not defining it is deprecated since Symfony 3.2.', __METHOD__), \E_USER_DEPRECATED);
0481 }
0482 }
0483
0484 $priority = 0;
0485 }
0486
0487 $this->getCompiler()->addPass($pass, $type, $priority);
0488
0489 $this->addObjectResource($pass);
0490
0491 return $this;
0492 }
0493
0494 /**
0495 * Returns the compiler pass config which can then be modified.
0496 *
0497 * @return PassConfig The compiler pass config
0498 */
0499 public function getCompilerPassConfig()
0500 {
0501 return $this->getCompiler()->getPassConfig();
0502 }
0503
0504 /**
0505 * Returns the compiler.
0506 *
0507 * @return Compiler The compiler
0508 */
0509 public function getCompiler()
0510 {
0511 if (null === $this->compiler) {
0512 $this->compiler = new Compiler();
0513 }
0514
0515 return $this->compiler;
0516 }
0517
0518 /**
0519 * Sets a service.
0520 *
0521 * @param string $id The service identifier
0522 * @param object|null $service The service instance
0523 *
0524 * @throws BadMethodCallException When this ContainerBuilder is compiled
0525 */
0526 public function set($id, $service)
0527 {
0528 $id = $this->normalizeId($id);
0529
0530 if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
0531 // setting a synthetic service on a compiled container is alright
0532 throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
0533 }
0534
0535 unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
0536
0537 parent::set($id, $service);
0538 }
0539
0540 /**
0541 * Removes a service definition.
0542 *
0543 * @param string $id The service identifier
0544 */
0545 public function removeDefinition($id)
0546 {
0547 if (isset($this->definitions[$id = $this->normalizeId($id)])) {
0548 unset($this->definitions[$id]);
0549 $this->removedIds[$id] = true;
0550 }
0551 }
0552
0553 /**
0554 * Returns true if the given service is defined.
0555 *
0556 * @param string $id The service identifier
0557 *
0558 * @return bool true if the service is defined, false otherwise
0559 */
0560 public function has($id)
0561 {
0562 $id = $this->normalizeId($id);
0563
0564 return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
0565 }
0566
0567 /**
0568 * Gets a service.
0569 *
0570 * @param string $id The service identifier
0571 * @param int $invalidBehavior The behavior when the service does not exist
0572 *
0573 * @return object|null The associated service
0574 *
0575 * @throws InvalidArgumentException when no definitions are available
0576 * @throws ServiceCircularReferenceException When a circular reference is detected
0577 * @throws ServiceNotFoundException When the service is not defined
0578 * @throws \Exception
0579 *
0580 * @see Reference
0581 */
0582 public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
0583 {
0584 if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) {
0585 @trigger_error(sprintf('Fetching the "%s" private service or alias is deprecated since Symfony 3.4 and will fail in 4.0. Make it public instead.', $id), \E_USER_DEPRECATED);
0586 }
0587
0588 return $this->doGet($id, $invalidBehavior);
0589 }
0590
0591 private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, $isConstructorArgument = false)
0592 {
0593 $id = $this->normalizeId($id);
0594
0595 if (isset($inlineServices[$id])) {
0596 return $inlineServices[$id];
0597 }
0598 if (null === $inlineServices) {
0599 $isConstructorArgument = true;
0600 $inlineServices = [];
0601 }
0602 try {
0603 if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
0604 return parent::get($id, $invalidBehavior);
0605 }
0606 if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
0607 return $service;
0608 }
0609 } catch (ServiceCircularReferenceException $e) {
0610 if ($isConstructorArgument) {
0611 throw $e;
0612 }
0613 }
0614
0615 if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
0616 return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
0617 }
0618
0619 try {
0620 $definition = $this->getDefinition($id);
0621 } catch (ServiceNotFoundException $e) {
0622 if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
0623 return null;
0624 }
0625
0626 throw $e;
0627 }
0628
0629 if ($isConstructorArgument) {
0630 $this->loading[$id] = true;
0631 }
0632
0633 try {
0634 return $this->createService($definition, $inlineServices, $isConstructorArgument, $id);
0635 } finally {
0636 if ($isConstructorArgument) {
0637 unset($this->loading[$id]);
0638 }
0639 }
0640 }
0641
0642 /**
0643 * Merges a ContainerBuilder with the current ContainerBuilder configuration.
0644 *
0645 * Service definitions overrides the current defined ones.
0646 *
0647 * But for parameters, they are overridden by the current ones. It allows
0648 * the parameters passed to the container constructor to have precedence
0649 * over the loaded ones.
0650 *
0651 * $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar']));
0652 * $loader = new LoaderXXX($container);
0653 * $loader->load('resource_name');
0654 * $container->register('foo', 'stdClass');
0655 *
0656 * In the above example, even if the loaded resource defines a foo
0657 * parameter, the value will still be 'bar' as defined in the ContainerBuilder
0658 * constructor.
0659 *
0660 * @throws BadMethodCallException When this ContainerBuilder is compiled
0661 */
0662 public function merge(self $container)
0663 {
0664 if ($this->isCompiled()) {
0665 throw new BadMethodCallException('Cannot merge on a compiled container.');
0666 }
0667
0668 $this->addDefinitions($container->getDefinitions());
0669 $this->addAliases($container->getAliases());
0670 $this->getParameterBag()->add($container->getParameterBag()->all());
0671
0672 if ($this->trackResources) {
0673 foreach ($container->getResources() as $resource) {
0674 $this->addResource($resource);
0675 }
0676 }
0677
0678 foreach ($this->extensions as $name => $extension) {
0679 if (!isset($this->extensionConfigs[$name])) {
0680 $this->extensionConfigs[$name] = [];
0681 }
0682
0683 $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
0684 }
0685
0686 if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
0687 $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
0688 $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
0689 } else {
0690 $envPlaceholders = [];
0691 }
0692
0693 foreach ($container->envCounters as $env => $count) {
0694 if (!$count && !isset($envPlaceholders[$env])) {
0695 continue;
0696 }
0697 if (!isset($this->envCounters[$env])) {
0698 $this->envCounters[$env] = $count;
0699 } else {
0700 $this->envCounters[$env] += $count;
0701 }
0702 }
0703
0704 foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
0705 if (isset($this->autoconfiguredInstanceof[$interface])) {
0706 throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
0707 }
0708
0709 $this->autoconfiguredInstanceof[$interface] = $childDefinition;
0710 }
0711 }
0712
0713 /**
0714 * Returns the configuration array for the given extension.
0715 *
0716 * @param string $name The name of the extension
0717 *
0718 * @return array An array of configuration
0719 */
0720 public function getExtensionConfig($name)
0721 {
0722 if (!isset($this->extensionConfigs[$name])) {
0723 $this->extensionConfigs[$name] = [];
0724 }
0725
0726 return $this->extensionConfigs[$name];
0727 }
0728
0729 /**
0730 * Prepends a config array to the configs of the given extension.
0731 *
0732 * @param string $name The name of the extension
0733 * @param array $config The config to set
0734 */
0735 public function prependExtensionConfig($name, array $config)
0736 {
0737 if (!isset($this->extensionConfigs[$name])) {
0738 $this->extensionConfigs[$name] = [];
0739 }
0740
0741 array_unshift($this->extensionConfigs[$name], $config);
0742 }
0743
0744 /**
0745 * Compiles the container.
0746 *
0747 * This method passes the container to compiler
0748 * passes whose job is to manipulate and optimize
0749 * the container.
0750 *
0751 * The main compiler passes roughly do four things:
0752 *
0753 * * The extension configurations are merged;
0754 * * Parameter values are resolved;
0755 * * The parameter bag is frozen;
0756 * * Extension loading is disabled.
0757 *
0758 * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
0759 * env vars or be replaced by uniquely identifiable placeholders.
0760 * Set to "true" when you want to use the current ContainerBuilder
0761 * directly, keep to "false" when the container is dumped instead.
0762 */
0763 public function compile(/*$resolveEnvPlaceholders = false*/)
0764 {
0765 if (1 <= \func_num_args()) {
0766 $resolveEnvPlaceholders = func_get_arg(0);
0767 } else {
0768 if (__CLASS__ !== static::class) {
0769 $r = new \ReflectionMethod($this, __FUNCTION__);
0770 if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
0771 @trigger_error(sprintf('The %s::compile() method expects a first "$resolveEnvPlaceholders" argument since Symfony 3.3. It will be made mandatory in 4.0.', static::class), \E_USER_DEPRECATED);
0772 }
0773 }
0774 $resolveEnvPlaceholders = false;
0775 }
0776 $compiler = $this->getCompiler();
0777
0778 if ($this->trackResources) {
0779 foreach ($compiler->getPassConfig()->getPasses() as $pass) {
0780 $this->addObjectResource($pass);
0781 }
0782 }
0783 $bag = $this->getParameterBag();
0784
0785 if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
0786 $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
0787 }
0788
0789 $compiler->compile($this);
0790
0791 foreach ($this->definitions as $id => $definition) {
0792 if ($this->trackResources && $definition->isLazy()) {
0793 $this->getReflectionClass($definition->getClass());
0794 }
0795 }
0796
0797 $this->extensionConfigs = [];
0798
0799 if ($bag instanceof EnvPlaceholderParameterBag) {
0800 if ($resolveEnvPlaceholders) {
0801 $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
0802 }
0803
0804 $this->envPlaceholders = $bag->getEnvPlaceholders();
0805 }
0806
0807 parent::compile();
0808
0809 foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) {
0810 if (!$definition->isPublic() || $definition->isPrivate()) {
0811 $this->removedIds[$id] = true;
0812 }
0813 }
0814 }
0815
0816 /**
0817 * {@inheritdoc}
0818 */
0819 public function getServiceIds()
0820 {
0821 return array_map('strval', array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())));
0822 }
0823
0824 /**
0825 * Gets removed service or alias ids.
0826 *
0827 * @return array
0828 */
0829 public function getRemovedIds()
0830 {
0831 return $this->removedIds;
0832 }
0833
0834 /**
0835 * Adds the service aliases.
0836 */
0837 public function addAliases(array $aliases)
0838 {
0839 foreach ($aliases as $alias => $id) {
0840 $this->setAlias($alias, $id);
0841 }
0842 }
0843
0844 /**
0845 * Sets the service aliases.
0846 */
0847 public function setAliases(array $aliases)
0848 {
0849 $this->aliasDefinitions = [];
0850 $this->addAliases($aliases);
0851 }
0852
0853 /**
0854 * Sets an alias for an existing service.
0855 *
0856 * @param string $alias The alias to create
0857 * @param string|Alias $id The service to alias
0858 *
0859 * @return Alias
0860 *
0861 * @throws InvalidArgumentException if the id is not a string or an Alias
0862 * @throws InvalidArgumentException if the alias is for itself
0863 */
0864 public function setAlias($alias, $id)
0865 {
0866 $alias = $this->normalizeId($alias);
0867
0868 if ('' === $alias || '\\' === substr($alias, -1) || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
0869 throw new InvalidArgumentException(sprintf('Invalid alias id: "%s".', $alias));
0870 }
0871
0872 if (\is_string($id)) {
0873 $id = new Alias($this->normalizeId($id));
0874 } elseif (!$id instanceof Alias) {
0875 throw new InvalidArgumentException('$id must be a string, or an Alias object.');
0876 }
0877
0878 if ($alias === (string) $id) {
0879 throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
0880 }
0881
0882 unset($this->definitions[$alias], $this->removedIds[$alias]);
0883
0884 return $this->aliasDefinitions[$alias] = $id;
0885 }
0886
0887 /**
0888 * Removes an alias.
0889 *
0890 * @param string $alias The alias to remove
0891 */
0892 public function removeAlias($alias)
0893 {
0894 if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) {
0895 unset($this->aliasDefinitions[$alias]);
0896 $this->removedIds[$alias] = true;
0897 }
0898 }
0899
0900 /**
0901 * Returns true if an alias exists under the given identifier.
0902 *
0903 * @param string $id The service identifier
0904 *
0905 * @return bool true if the alias exists, false otherwise
0906 */
0907 public function hasAlias($id)
0908 {
0909 return isset($this->aliasDefinitions[$this->normalizeId($id)]);
0910 }
0911
0912 /**
0913 * Gets all defined aliases.
0914 *
0915 * @return Alias[] An array of aliases
0916 */
0917 public function getAliases()
0918 {
0919 return $this->aliasDefinitions;
0920 }
0921
0922 /**
0923 * Gets an alias.
0924 *
0925 * @param string $id The service identifier
0926 *
0927 * @return Alias An Alias instance
0928 *
0929 * @throws InvalidArgumentException if the alias does not exist
0930 */
0931 public function getAlias($id)
0932 {
0933 $id = $this->normalizeId($id);
0934
0935 if (!isset($this->aliasDefinitions[$id])) {
0936 throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
0937 }
0938
0939 return $this->aliasDefinitions[$id];
0940 }
0941
0942 /**
0943 * Registers a service definition.
0944 *
0945 * This methods allows for simple registration of service definition
0946 * with a fluid interface.
0947 *
0948 * @param string $id The service identifier
0949 * @param string|null $class The service class
0950 *
0951 * @return Definition A Definition instance
0952 */
0953 public function register($id, $class = null)
0954 {
0955 return $this->setDefinition($id, new Definition($class));
0956 }
0957
0958 /**
0959 * Registers an autowired service definition.
0960 *
0961 * This method implements a shortcut for using setDefinition() with
0962 * an autowired definition.
0963 *
0964 * @param string $id The service identifier
0965 * @param string|null $class The service class
0966 *
0967 * @return Definition The created definition
0968 */
0969 public function autowire($id, $class = null)
0970 {
0971 return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
0972 }
0973
0974 /**
0975 * Adds the service definitions.
0976 *
0977 * @param Definition[] $definitions An array of service definitions
0978 */
0979 public function addDefinitions(array $definitions)
0980 {
0981 foreach ($definitions as $id => $definition) {
0982 $this->setDefinition($id, $definition);
0983 }
0984 }
0985
0986 /**
0987 * Sets the service definitions.
0988 *
0989 * @param Definition[] $definitions An array of service definitions
0990 */
0991 public function setDefinitions(array $definitions)
0992 {
0993 $this->definitions = [];
0994 $this->addDefinitions($definitions);
0995 }
0996
0997 /**
0998 * Gets all service definitions.
0999 *
1000 * @return Definition[] An array of Definition instances
1001 */
1002 public function getDefinitions()
1003 {
1004 return $this->definitions;
1005 }
1006
1007 /**
1008 * Sets a service definition.
1009 *
1010 * @param string $id The service identifier
1011 * @param Definition $definition A Definition instance
1012 *
1013 * @return Definition the service definition
1014 *
1015 * @throws BadMethodCallException When this ContainerBuilder is compiled
1016 */
1017 public function setDefinition($id, Definition $definition)
1018 {
1019 if ($this->isCompiled()) {
1020 throw new BadMethodCallException('Adding definition to a compiled container is not allowed.');
1021 }
1022
1023 $id = $this->normalizeId($id);
1024
1025 if ('' === $id || '\\' === substr($id, -1) || \strlen($id) !== strcspn($id, "\0\r\n'")) {
1026 throw new InvalidArgumentException(sprintf('Invalid service id: "%s".', $id));
1027 }
1028
1029 unset($this->aliasDefinitions[$id], $this->removedIds[$id]);
1030
1031 return $this->definitions[$id] = $definition;
1032 }
1033
1034 /**
1035 * Returns true if a service definition exists under the given identifier.
1036 *
1037 * @param string $id The service identifier
1038 *
1039 * @return bool true if the service definition exists, false otherwise
1040 */
1041 public function hasDefinition($id)
1042 {
1043 return isset($this->definitions[$this->normalizeId($id)]);
1044 }
1045
1046 /**
1047 * Gets a service definition.
1048 *
1049 * @param string $id The service identifier
1050 *
1051 * @return Definition A Definition instance
1052 *
1053 * @throws ServiceNotFoundException if the service definition does not exist
1054 */
1055 public function getDefinition($id)
1056 {
1057 $id = $this->normalizeId($id);
1058
1059 if (!isset($this->definitions[$id])) {
1060 throw new ServiceNotFoundException($id);
1061 }
1062
1063 return $this->definitions[$id];
1064 }
1065
1066 /**
1067 * Gets a service definition by id or alias.
1068 *
1069 * The method "unaliases" recursively to return a Definition instance.
1070 *
1071 * @param string $id The service identifier or alias
1072 *
1073 * @return Definition A Definition instance
1074 *
1075 * @throws ServiceNotFoundException if the service definition does not exist
1076 */
1077 public function findDefinition($id)
1078 {
1079 $id = $this->normalizeId($id);
1080
1081 $seen = [];
1082 while (isset($this->aliasDefinitions[$id])) {
1083 $id = (string) $this->aliasDefinitions[$id];
1084
1085 if (isset($seen[$id])) {
1086 $seen = array_values($seen);
1087 $seen = \array_slice($seen, array_search($id, $seen));
1088 $seen[] = $id;
1089
1090 throw new ServiceCircularReferenceException($id, $seen);
1091 }
1092
1093 $seen[$id] = $id;
1094 }
1095
1096 return $this->getDefinition($id);
1097 }
1098
1099 /**
1100 * Creates a service for a service definition.
1101 *
1102 * @param Definition $definition A service definition instance
1103 * @param string $id The service identifier
1104 * @param bool $tryProxy Whether to try proxying the service with a lazy proxy
1105 *
1106 * @return mixed The service described by the service definition
1107 *
1108 * @throws RuntimeException When the factory definition is incomplete
1109 * @throws RuntimeException When the service is a synthetic service
1110 * @throws InvalidArgumentException When configure callable is not callable
1111 */
1112 private function createService(Definition $definition, array &$inlineServices, $isConstructorArgument = false, $id = null, $tryProxy = true)
1113 {
1114 if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) {
1115 return $inlineServices[$h];
1116 }
1117
1118 if ($definition instanceof ChildDefinition) {
1119 throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id));
1120 }
1121
1122 if ($definition->isSynthetic()) {
1123 throw new RuntimeException(sprintf('You have requested a synthetic service ("%s"). The DIC does not know how to construct this service.', $id));
1124 }
1125
1126 if ($definition->isDeprecated()) {
1127 @trigger_error($definition->getDeprecationMessage($id), \E_USER_DEPRECATED);
1128 }
1129
1130 if ($tryProxy && $definition->isLazy() && !$tryProxy = !($proxy = $this->proxyInstantiator) || $proxy instanceof RealServiceInstantiator) {
1131 $proxy = $proxy->instantiateProxy(
1132 $this,
1133 $definition,
1134 $id, function () use ($definition, &$inlineServices, $id) {
1135 return $this->createService($definition, $inlineServices, true, $id, false);
1136 }
1137 );
1138 $this->shareService($definition, $proxy, $id, $inlineServices);
1139
1140 return $proxy;
1141 }
1142
1143 $parameterBag = $this->getParameterBag();
1144
1145 if (null !== $definition->getFile()) {
1146 require_once $parameterBag->resolveValue($definition->getFile());
1147 }
1148
1149 $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices, $isConstructorArgument);
1150
1151 if (null !== $factory = $definition->getFactory()) {
1152 if (\is_array($factory)) {
1153 $factory = [$this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices, $isConstructorArgument), $factory[1]];
1154 } elseif (!\is_string($factory)) {
1155 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory.', $id));
1156 }
1157 }
1158
1159 if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) {
1160 return $this->services[$id];
1161 }
1162
1163 if (null !== $factory) {
1164 $service = \call_user_func_array($factory, $arguments);
1165
1166 if (!$definition->isDeprecated() && \is_array($factory) && \is_string($factory[0])) {
1167 $r = new \ReflectionClass($factory[0]);
1168
1169 if (0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
1170 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" factory class. It should either be deprecated or its factory upgraded.', $id, $r->name), \E_USER_DEPRECATED);
1171 }
1172 }
1173 } else {
1174 $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));
1175
1176 $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs(array_values($arguments));
1177 // don't trigger deprecations for internal uses
1178 // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class
1179 $deprecationAllowlist = ['event_dispatcher' => ContainerAwareEventDispatcher::class];
1180
1181 if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationAllowlist[$id]) || $deprecationAllowlist[$id] !== $class)) {
1182 @trigger_error(sprintf('The "%s" service relies on the deprecated "%s" class. It should either be deprecated or its implementation upgraded.', $id, $r->name), \E_USER_DEPRECATED);
1183 }
1184 }
1185
1186 if ($tryProxy || !$definition->isLazy()) {
1187 // share only if proxying failed, or if not a proxy
1188 $this->shareService($definition, $service, $id, $inlineServices);
1189 }
1190
1191 $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices);
1192 foreach ($properties as $name => $value) {
1193 $service->$name = $value;
1194 }
1195
1196 foreach ($definition->getMethodCalls() as $call) {
1197 $this->callMethod($service, $call, $inlineServices);
1198 }
1199
1200 if ($callable = $definition->getConfigurator()) {
1201 if (\is_array($callable)) {
1202 $callable[0] = $parameterBag->resolveValue($callable[0]);
1203
1204 if ($callable[0] instanceof Reference) {
1205 $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices);
1206 } elseif ($callable[0] instanceof Definition) {
1207 $callable[0] = $this->createService($callable[0], $inlineServices);
1208 }
1209 }
1210
1211 if (!\is_callable($callable)) {
1212 throw new InvalidArgumentException(sprintf('The configure callable for class "%s" is not a callable.', \get_class($service)));
1213 }
1214
1215 \call_user_func($callable, $service);
1216 }
1217
1218 return $service;
1219 }
1220
1221 /**
1222 * Replaces service references by the real service instance and evaluates expressions.
1223 *
1224 * @param mixed $value A value
1225 *
1226 * @return mixed The same value with all service references replaced by
1227 * the real service instances and all expressions evaluated
1228 */
1229 public function resolveServices($value)
1230 {
1231 return $this->doResolveServices($value);
1232 }
1233
1234 private function doResolveServices($value, array &$inlineServices = [], $isConstructorArgument = false)
1235 {
1236 if (\is_array($value)) {
1237 foreach ($value as $k => $v) {
1238 $value[$k] = $this->doResolveServices($v, $inlineServices, $isConstructorArgument);
1239 }
1240 } elseif ($value instanceof ServiceClosureArgument) {
1241 $reference = $value->getValues()[0];
1242 $value = function () use ($reference) {
1243 return $this->resolveServices($reference);
1244 };
1245 } elseif ($value instanceof IteratorArgument) {
1246 $value = new RewindableGenerator(function () use ($value) {
1247 foreach ($value->getValues() as $k => $v) {
1248 foreach (self::getServiceConditionals($v) as $s) {
1249 if (!$this->has($s)) {
1250 continue 2;
1251 }
1252 }
1253 foreach (self::getInitializedConditionals($v) as $s) {
1254 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
1255 continue 2;
1256 }
1257 }
1258
1259 yield $k => $this->resolveServices($v);
1260 }
1261 }, function () use ($value) {
1262 $count = 0;
1263 foreach ($value->getValues() as $v) {
1264 foreach (self::getServiceConditionals($v) as $s) {
1265 if (!$this->has($s)) {
1266 continue 2;
1267 }
1268 }
1269 foreach (self::getInitializedConditionals($v) as $s) {
1270 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
1271 continue 2;
1272 }
1273 }
1274
1275 ++$count;
1276 }
1277
1278 return $count;
1279 });
1280 } elseif ($value instanceof Reference) {
1281 $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices, $isConstructorArgument);
1282 } elseif ($value instanceof Definition) {
1283 $value = $this->createService($value, $inlineServices, $isConstructorArgument);
1284 } elseif ($value instanceof Parameter) {
1285 $value = $this->getParameter((string) $value);
1286 } elseif ($value instanceof Expression) {
1287 $value = $this->getExpressionLanguage()->evaluate($value, ['container' => $this]);
1288 }
1289
1290 return $value;
1291 }
1292
1293 /**
1294 * Returns service ids for a given tag.
1295 *
1296 * Example:
1297 *
1298 * $container->register('foo')->addTag('my.tag', ['hello' => 'world']);
1299 *
1300 * $serviceIds = $container->findTaggedServiceIds('my.tag');
1301 * foreach ($serviceIds as $serviceId => $tags) {
1302 * foreach ($tags as $tag) {
1303 * echo $tag['hello'];
1304 * }
1305 * }
1306 *
1307 * @param string $name
1308 * @param bool $throwOnAbstract
1309 *
1310 * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
1311 */
1312 public function findTaggedServiceIds($name, $throwOnAbstract = false)
1313 {
1314 $this->usedTags[] = $name;
1315 $tags = [];
1316 foreach ($this->getDefinitions() as $id => $definition) {
1317 if ($definition->hasTag($name)) {
1318 if ($throwOnAbstract && $definition->isAbstract()) {
1319 throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
1320 }
1321 $tags[$id] = $definition->getTag($name);
1322 }
1323 }
1324
1325 return $tags;
1326 }
1327
1328 /**
1329 * Returns all tags the defined services use.
1330 *
1331 * @return array An array of tags
1332 */
1333 public function findTags()
1334 {
1335 $tags = [];
1336 foreach ($this->getDefinitions() as $id => $definition) {
1337 $tags = array_merge(array_keys($definition->getTags()), $tags);
1338 }
1339
1340 return array_unique($tags);
1341 }
1342
1343 /**
1344 * Returns all tags not queried by findTaggedServiceIds.
1345 *
1346 * @return string[] An array of tags
1347 */
1348 public function findUnusedTags()
1349 {
1350 return array_values(array_diff($this->findTags(), $this->usedTags));
1351 }
1352
1353 public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
1354 {
1355 $this->expressionLanguageProviders[] = $provider;
1356 }
1357
1358 /**
1359 * @return ExpressionFunctionProviderInterface[]
1360 */
1361 public function getExpressionLanguageProviders()
1362 {
1363 return $this->expressionLanguageProviders;
1364 }
1365
1366 /**
1367 * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
1368 *
1369 * @param string $interface The class or interface to match
1370 *
1371 * @return ChildDefinition
1372 */
1373 public function registerForAutoconfiguration($interface)
1374 {
1375 if (!isset($this->autoconfiguredInstanceof[$interface])) {
1376 $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
1377 }
1378
1379 return $this->autoconfiguredInstanceof[$interface];
1380 }
1381
1382 /**
1383 * Returns an array of ChildDefinition[] keyed by interface.
1384 *
1385 * @return ChildDefinition[]
1386 */
1387 public function getAutoconfiguredInstanceof()
1388 {
1389 return $this->autoconfiguredInstanceof;
1390 }
1391
1392 /**
1393 * Resolves env parameter placeholders in a string or an array.
1394 *
1395 * @param mixed $value The value to resolve
1396 * @param string|true|null $format A sprintf() format returning the replacement for each env var name or
1397 * null to resolve back to the original "%env(VAR)%" format or
1398 * true to resolve to the actual values of the referenced env vars
1399 * @param array &$usedEnvs Env vars found while resolving are added to this array
1400 *
1401 * @return mixed The value with env parameters resolved if a string or an array is passed
1402 */
1403 public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
1404 {
1405 if (null === $format) {
1406 $format = '%%env(%s)%%';
1407 }
1408
1409 $bag = $this->getParameterBag();
1410 if (true === $format) {
1411 $value = $bag->resolveValue($value);
1412 }
1413
1414 if (\is_array($value)) {
1415 $result = [];
1416 foreach ($value as $k => $v) {
1417 $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
1418 }
1419
1420 return $result;
1421 }
1422
1423 if (!\is_string($value) || 38 > \strlen($value)) {
1424 return $value;
1425 }
1426 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
1427
1428 $completed = false;
1429 foreach ($envPlaceholders as $env => $placeholders) {
1430 foreach ($placeholders as $placeholder) {
1431 if (false !== stripos($value, $placeholder)) {
1432 if (true === $format) {
1433 $resolved = $bag->escapeValue($this->getEnv($env));
1434 } else {
1435 $resolved = sprintf($format, $env);
1436 }
1437 if ($placeholder === $value) {
1438 $value = $resolved;
1439 $completed = true;
1440 } else {
1441 if (!\is_string($resolved) && !is_numeric($resolved)) {
1442 throw new RuntimeException(sprintf('A string value must be composed of strings and/or numbers, but found parameter "env(%s)" of type "%s" inside string value "%s".', $env, \gettype($resolved), $this->resolveEnvPlaceholders($value)));
1443 }
1444 $value = str_ireplace($placeholder, $resolved, $value);
1445 }
1446 $usedEnvs[$env] = $env;
1447 $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
1448
1449 if ($completed) {
1450 break 2;
1451 }
1452 }
1453 }
1454 }
1455
1456 return $value;
1457 }
1458
1459 /**
1460 * Get statistics about env usage.
1461 *
1462 * @return int[] The number of time each env vars has been resolved
1463 */
1464 public function getEnvCounters()
1465 {
1466 $bag = $this->getParameterBag();
1467 $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
1468
1469 foreach ($envPlaceholders as $env => $placeholders) {
1470 if (!isset($this->envCounters[$env])) {
1471 $this->envCounters[$env] = 0;
1472 }
1473 }
1474
1475 return $this->envCounters;
1476 }
1477
1478 /**
1479 * @internal
1480 */
1481 public function getNormalizedIds()
1482 {
1483 $normalizedIds = [];
1484
1485 foreach ($this->normalizedIds as $k => $v) {
1486 if ($v !== (string) $k) {
1487 $normalizedIds[$k] = $v;
1488 }
1489 }
1490
1491 return $normalizedIds;
1492 }
1493
1494 /**
1495 * @final
1496 */
1497 public function log(CompilerPassInterface $pass, $message)
1498 {
1499 $this->getCompiler()->log($pass, $this->resolveEnvPlaceholders($message));
1500 }
1501
1502 /**
1503 * {@inheritdoc}
1504 */
1505 public function normalizeId($id)
1506 {
1507 if (!\is_string($id)) {
1508 $id = (string) $id;
1509 }
1510
1511 return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || isset($this->removedIds[$id]) ? $id : parent::normalizeId($id);
1512 }
1513
1514 /**
1515 * Gets removed binding ids.
1516 *
1517 * @return array
1518 *
1519 * @internal
1520 */
1521 public function getRemovedBindingIds()
1522 {
1523 return $this->removedBindingIds;
1524 }
1525
1526 /**
1527 * Removes bindings for a service.
1528 *
1529 * @param string $id The service identifier
1530 *
1531 * @internal
1532 */
1533 public function removeBindings($id)
1534 {
1535 if ($this->hasDefinition($id)) {
1536 foreach ($this->getDefinition($id)->getBindings() as $key => $binding) {
1537 list(, $bindingId) = $binding->getValues();
1538 $this->removedBindingIds[(int) $bindingId] = true;
1539 }
1540 }
1541 }
1542
1543 /**
1544 * Returns the Service Conditionals.
1545 *
1546 * @param mixed $value An array of conditionals to return
1547 *
1548 * @return array An array of Service conditionals
1549 *
1550 * @internal since version 3.4
1551 */
1552 public static function getServiceConditionals($value)
1553 {
1554 $services = [];
1555
1556 if (\is_array($value)) {
1557 foreach ($value as $v) {
1558 $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
1559 }
1560 } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
1561 $services[] = (string) $value;
1562 }
1563
1564 return $services;
1565 }
1566
1567 /**
1568 * Returns the initialized conditionals.
1569 *
1570 * @param mixed $value An array of conditionals to return
1571 *
1572 * @return array An array of uninitialized conditionals
1573 *
1574 * @internal
1575 */
1576 public static function getInitializedConditionals($value)
1577 {
1578 $services = [];
1579
1580 if (\is_array($value)) {
1581 foreach ($value as $v) {
1582 $services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
1583 }
1584 } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) {
1585 $services[] = (string) $value;
1586 }
1587
1588 return $services;
1589 }
1590
1591 /**
1592 * Computes a reasonably unique hash of a value.
1593 *
1594 * @param mixed $value A serializable value
1595 *
1596 * @return string
1597 */
1598 public static function hash($value)
1599 {
1600 $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7);
1601
1602 return str_replace(['/', '+'], ['.', '_'], strtolower($hash));
1603 }
1604
1605 /**
1606 * {@inheritdoc}
1607 */
1608 protected function getEnv($name)
1609 {
1610 $value = parent::getEnv($name);
1611 $bag = $this->getParameterBag();
1612
1613 if (!\is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
1614 return $value;
1615 }
1616
1617 foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
1618 if (isset($placeholders[$value])) {
1619 $bag = new ParameterBag($bag->all());
1620
1621 return $bag->unescapeValue($bag->get("env($name)"));
1622 }
1623 }
1624
1625 $this->resolving["env($name)"] = true;
1626 try {
1627 return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
1628 } finally {
1629 unset($this->resolving["env($name)"]);
1630 }
1631 }
1632
1633 private function callMethod($service, $call, array &$inlineServices)
1634 {
1635 foreach (self::getServiceConditionals($call[1]) as $s) {
1636 if (!$this->has($s)) {
1637 return;
1638 }
1639 }
1640 foreach (self::getInitializedConditionals($call[1]) as $s) {
1641 if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) {
1642 return;
1643 }
1644 }
1645
1646 \call_user_func_array([$service, $call[0]], $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices));
1647 }
1648
1649 /**
1650 * Shares a given service in the container.
1651 *
1652 * @param mixed $service
1653 * @param string|null $id
1654 */
1655 private function shareService(Definition $definition, $service, $id, array &$inlineServices)
1656 {
1657 $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service;
1658
1659 if (null !== $id && $definition->isShared()) {
1660 $this->services[$id] = $service;
1661 unset($this->loading[$id]);
1662 }
1663 }
1664
1665 private function getExpressionLanguage()
1666 {
1667 if (null === $this->expressionLanguage) {
1668 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
1669 throw new RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
1670 }
1671 $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
1672 }
1673
1674 return $this->expressionLanguage;
1675 }
1676
1677 private function inVendors($path)
1678 {
1679 if (null === $this->vendors) {
1680 $resource = new ComposerResource();
1681 $this->vendors = $resource->getVendors();
1682 $this->addResource($resource);
1683 }
1684 $path = realpath($path) ?: $path;
1685
1686 foreach ($this->vendors as $vendor) {
1687 if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, \strlen($vendor), 1), '/'.\DIRECTORY_SEPARATOR)) {
1688 return true;
1689 }
1690 }
1691
1692 return false;
1693 }
1694 }
1695