[ Index ]

PHP Cross Reference of phpBB-3.3.0-deutsch

title

Body

[close]

/vendor/symfony/dependency-injection/ -> ContainerBuilder.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\DependencyInjection;
  13  
  14  use Psr\Container\ContainerInterface as PsrContainerInterface;
  15  use Symfony\Component\Config\Resource\ClassExistenceResource;
  16  use Symfony\Component\Config\Resource\ComposerResource;
  17  use Symfony\Component\Config\Resource\DirectoryResource;
  18  use Symfony\Component\Config\Resource\FileExistenceResource;
  19  use Symfony\Component\Config\Resource\FileResource;
  20  use Symfony\Component\Config\Resource\GlobResource;
  21  use Symfony\Component\Config\Resource\ReflectionClassResource;
  22  use Symfony\Component\Config\Resource\ResourceInterface;
  23  use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
  24  use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
  25  use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
  26  use Symfony\Component\DependencyInjection\Compiler\Compiler;
  27  use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
  28  use Symfony\Component\DependencyInjection\Compiler\PassConfig;
  29  use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
  30  use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
  31  use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  32  use Symfony\Component\DependencyInjection\Exception\LogicException;
  33  use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  34  use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  35  use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  36  use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
  37  use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
  38  use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
  39  use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
  40  use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
  41  use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  42  use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
  43  use Symfony\Component\ExpressionLanguage\Expression;
  44  use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
  45  
  46  /**
  47   * ContainerBuilder is a DI container that provides an API to easily describe services.
  48   *
  49   * @author Fabien Potencier <fabien@symfony.com>
  50   */
  51  class ContainerBuilder extends Container implements TaggedContainerInterface
  52  {
  53      /**
  54       * @var ExtensionInterface[]
  55       */
  56      private $extensions = [];
  57  
  58      /**
  59       * @var ExtensionInterface[]
  60       */
  61      private $extensionsByNs = [];
  62  
  63      /**
  64       * @var Definition[]
  65       */
  66      private $definitions = [];
  67  
  68      /**
  69       * @var Alias[]
  70       */
  71      private $aliasDefinitions = [];
  72  
  73      /**
  74       * @var ResourceInterface[]
  75       */
  76      private $resources = [];
  77  
  78      private $extensionConfigs = [];
  79  
  80      /**
  81       * @var Compiler
  82       */
  83      private $compiler;
  84  
  85      private $trackResources;
  86  
  87      /**
  88       * @var InstantiatorInterface|null
  89       */
  90      private $proxyInstantiator;
  91  
  92      /**
  93       * @var ExpressionLanguage|null
  94       */
  95      private $expressionLanguage;
  96  
  97      /**
  98       * @var ExpressionFunctionProviderInterface[]
  99       */
 100      private $expressionLanguageProviders = [];
 101  
 102      /**
 103       * @var string[] with tag names used by findTaggedServiceIds
 104       */
 105      private $usedTags = [];
 106  
 107      /**
 108       * @var string[][] a map of env var names to their placeholders
 109       */
 110      private $envPlaceholders = [];
 111  
 112      /**
 113       * @var int[] a map of env vars to their resolution counter
 114       */
 115      private $envCounters = [];
 116  
 117      /**
 118       * @var string[] the list of vendor directories
 119       */
 120      private $vendors;
 121  
 122      private $autoconfiguredInstanceof = [];
 123  
 124      private $removedIds = [];
 125  
 126      private $removedBindingIds = [];
 127  
 128      private static $internalTypes = [
 129          'int' => true,
 130          'float' => true,
 131          'string' => true,
 132          'bool' => true,
 133          'resource' => true,
 134          'object' => true,
 135          'array' => true,
 136          'null' => true,
 137          'callable' => true,
 138          'iterable' => true,
 139          'mixed' => true,
 140      ];
 141  
 142      public function __construct(ParameterBagInterface $parameterBag = null)
 143      {
 144          parent::__construct($parameterBag);
 145  
 146          $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
 147          $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
 148          $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
 149          $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
 150      }
 151  
 152      /**
 153       * @var \ReflectionClass[] a list of class reflectors
 154       */
 155      private $classReflectors;
 156  
 157      /**
 158       * Sets the track resources flag.
 159       *
 160       * If you are not using the loaders and therefore don't want
 161       * to depend on the Config component, set this flag to false.
 162       *
 163       * @param bool $track True if you want to track resources, false otherwise
 164       */
 165      public function setResourceTracking($track)
 166      {
 167          $this->trackResources = (bool) $track;
 168      }
 169  
 170      /**
 171       * Checks if resources are tracked.
 172       *
 173       * @return bool true If resources are tracked, false otherwise
 174       */
 175      public function isTrackingResources()
 176      {
 177          return $this->trackResources;
 178      }
 179  
 180      /**
 181       * Sets the instantiator to be used when fetching proxies.
 182       */
 183      public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
 184      {
 185          $this->proxyInstantiator = $proxyInstantiator;
 186      }
 187  
 188      public function registerExtension(ExtensionInterface $extension)
 189      {
 190          $this->extensions[$extension->getAlias()] = $extension;
 191  
 192          if (false !== $extension->getNamespace()) {
 193              $this->extensionsByNs[$extension->getNamespace()] = $extension;
 194          }
 195      }
 196  
 197      /**
 198       * Returns an extension by alias or namespace.
 199       *
 200       * @param string $name An alias or a namespace
 201       *
 202       * @return ExtensionInterface An extension instance
 203       *
 204       * @throws LogicException if the extension is not registered
 205       */
 206      public function getExtension($name)
 207      {
 208          if (isset($this->extensions[$name])) {
 209              return $this->extensions[$name];
 210          }
 211  
 212          if (isset($this->extensionsByNs[$name])) {
 213              return $this->extensionsByNs[$name];
 214          }
 215  
 216          throw new LogicException(sprintf('Container extension "%s" is not registered', $name));
 217      }
 218  
 219      /**
 220       * Returns all registered extensions.
 221       *
 222       * @return ExtensionInterface[] An array of ExtensionInterface
 223       */
 224      public function getExtensions()
 225      {
 226          return $this->extensions;
 227      }
 228  
 229      /**
 230       * Checks if we have an extension.
 231       *
 232       * @param string $name The name of the extension
 233       *
 234       * @return bool If the extension exists
 235       */
 236      public function hasExtension($name)
 237      {
 238          return isset($this->extensions[$name]) || isset($this->extensionsByNs[$name]);
 239      }
 240  
 241      /**
 242       * Returns an array of resources loaded to build this configuration.
 243       *
 244       * @return ResourceInterface[] An array of resources
 245       */
 246      public function getResources()
 247      {
 248          return array_values($this->resources);
 249      }
 250  
 251      /**
 252       * @return $this
 253       */
 254      public function addResource(ResourceInterface $resource)
 255      {
 256          if (!$this->trackResources) {
 257              return $this;
 258          }
 259  
 260          if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
 261              return $this;
 262          }
 263  
 264          $this->resources[(string) $resource] = $resource;
 265  
 266          return $this;
 267      }
 268  
 269      /**
 270       * Sets the resources for this configuration.
 271       *
 272       * @param ResourceInterface[] $resources An array of resources
 273       *
 274       * @return $this
 275       */
 276      public function setResources(array $resources)
 277      {
 278          if (!$this->trackResources) {
 279              return $this;
 280          }
 281  
 282          $this->resources = $resources;
 283  
 284          return $this;
 285      }
 286  
 287      /**
 288       * Adds the object class hierarchy as resources.
 289       *
 290       * @param object|string $object An object instance or class name
 291       *
 292       * @return $this
 293       */
 294      public function addObjectResource($object)
 295      {
 296          if ($this->trackResources) {
 297              if (\is_object($object)) {
 298                  $object = \get_class($object);
 299              }
 300              if (!isset($this->classReflectors[$object])) {
 301                  $this->classReflectors[$object] = new \ReflectionClass($object);
 302              }
 303              $class = $this->classReflectors[$object];
 304  
 305              foreach ($class->getInterfaceNames() as $name) {
 306                  if (null === $interface = &$this->classReflectors[$name]) {
 307                      $interface = new \ReflectionClass($name);
 308                  }
 309                  $file = $interface->getFileName();
 310                  if (false !== $file && file_exists($file)) {
 311                      $this->fileExists($file);
 312                  }
 313              }
 314              do {
 315                  $file = $class->getFileName();
 316                  if (false !== $file && file_exists($file)) {
 317                      $this->fileExists($file);
 318                  }
 319                  foreach ($class->getTraitNames() as $name) {
 320                      $this->addObjectResource($name);
 321                  }
 322              } while ($class = $class->getParentClass());
 323          }
 324  
 325          return $this;
 326      }
 327  
 328      /**
 329       * Adds the given class hierarchy as resources.
 330       *
 331       * @return $this
 332       *
 333       * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
 334       */
 335      public function addClassResource(\ReflectionClass $class)
 336      {
 337          @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);
 338  
 339          return $this->addObjectResource($class->name);
 340      }
 341  
 342      /**
 343       * Retrieves the requested reflection class and registers it for resource tracking.
 344       *
 345       * @param string $class
 346       * @param bool   $throw
 347       *
 348       * @return \ReflectionClass|null
 349       *
 350       * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
 351       *
 352       * @final
 353       */
 354      public function getReflectionClass($class, $throw = true)
 355      {
 356          if (!$class = $this->getParameterBag()->resolveValue($class)) {
 357              return null;
 358          }
 359  
 360          if (isset(self::$internalTypes[$class])) {
 361              return null;
 362          }
 363  
 364          $resource = $classReflector = null;
 365  
 366          try {
 367              if (isset($this->classReflectors[$class])) {
 368                  $classReflector = $this->classReflectors[$class];
 369              } elseif (class_exists(ClassExistenceResource::class)) {
 370                  $resource = new ClassExistenceResource($class, false);
 371                  $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
 372              } else {
 373                  $classReflector = class_exists($class) ? new \ReflectionClass($class) : false;
 374              }
 375          } catch (\ReflectionException $e) {
 376              if ($throw) {
 377                  throw $e;
 378              }
 379          }
 380  
 381          if ($this->trackResources) {
 382              if (!$classReflector) {
 383                  $this->addResource($resource ?: new ClassExistenceResource($class, false));
 384              } elseif (!$classReflector->isInternal()) {
 385                  $path = $classReflector->getFileName();
 386  
 387                  if (!$this->inVendors($path)) {
 388                      $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
 389                  }
 390              }
 391              $this->classReflectors[$class] = $classReflector;
 392          }
 393  
 394          return $classReflector ?: null;
 395      }
 396  
 397      /**
 398       * Checks whether the requested file or directory exists and registers the result for resource tracking.
 399       *
 400       * @param string      $path          The file or directory path for which to check the existence
 401       * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
 402       *                                   it will be used as pattern for tracking contents of the requested directory
 403       *
 404       * @return bool
 405       *
 406       * @final
 407       */
 408      public function fileExists($path, $trackContents = true)
 409      {
 410          $exists = file_exists($path);
 411  
 412          if (!$this->trackResources || $this->inVendors($path)) {
 413              return $exists;
 414          }
 415  
 416          if (!$exists) {
 417              $this->addResource(new FileExistenceResource($path));
 418  
 419              return $exists;
 420          }
 421  
 422          if (is_dir($path)) {
 423              if ($trackContents) {
 424                  $this->addResource(new DirectoryResource($path, \is_string($trackContents) ? $trackContents : null));
 425              } else {
 426                  $this->addResource(new GlobResource($path, '/*', false));
 427              }
 428          } elseif ($trackContents) {
 429              $this->addResource(new FileResource($path));
 430          }
 431  
 432          return $exists;
 433      }
 434  
 435      /**
 436       * Loads the configuration for an extension.
 437       *
 438       * @param string $extension The extension alias or namespace
 439       * @param array  $values    An array of values that customizes the extension
 440       *
 441       * @return $this
 442       *
 443       * @throws BadMethodCallException When this ContainerBuilder is compiled
 444       * @throws \LogicException        if the extension is not registered
 445       */
 446      public function loadFromExtension($extension, array $values = null)
 447      {
 448          if ($this->isCompiled()) {
 449              throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
 450          }
 451  
 452          if (\func_num_args() < 2) {
 453              $values = [];
 454          }
 455  
 456          $namespace = $this->getExtension($extension)->getAlias();
 457  
 458          $this->extensionConfigs[$namespace][] = $values;
 459  
 460          return $this;
 461      }
 462  
 463      /**
 464       * Adds a compiler pass.
 465       *
 466       * @param CompilerPassInterface $pass     A compiler pass
 467       * @param string                $type     The type of compiler pass
 468       * @param int                   $priority Used to sort the passes
 469       *
 470       * @return $this
 471       */
 472      public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
 473      {
 474          if (\func_num_args() >= 3) {
 475              $priority = func_get_arg(2);
 476          } else {
 477              if (__CLASS__ !== \get_class($this)) {
 478                  $r = new \ReflectionMethod($this, __FUNCTION__);
 479                  if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
 480                      @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);
 481                  }
 482              }
 483  
 484              $priority = 0;
 485          }
 486  
 487          $this->getCompiler()->addPass($pass, $type, $priority);
 488  
 489          $this->addObjectResource($pass);
 490  
 491          return $this;
 492      }
 493  
 494      /**
 495       * Returns the compiler pass config which can then be modified.
 496       *
 497       * @return PassConfig The compiler pass config
 498       */
 499      public function getCompilerPassConfig()
 500      {
 501          return $this->getCompiler()->getPassConfig();
 502      }
 503  
 504      /**
 505       * Returns the compiler.
 506       *
 507       * @return Compiler The compiler
 508       */
 509      public function getCompiler()
 510      {
 511          if (null === $this->compiler) {
 512              $this->compiler = new Compiler();
 513          }
 514  
 515          return $this->compiler;
 516      }
 517  
 518      /**
 519       * Sets a service.
 520       *
 521       * @param string      $id      The service identifier
 522       * @param object|null $service The service instance
 523       *
 524       * @throws BadMethodCallException When this ContainerBuilder is compiled
 525       */
 526      public function set($id, $service)
 527      {
 528          $id = $this->normalizeId($id);
 529  
 530          if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
 531              // setting a synthetic service on a compiled container is alright
 532              throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
 533          }
 534  
 535          unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
 536  
 537          parent::set($id, $service);
 538      }
 539  
 540      /**
 541       * Removes a service definition.
 542       *
 543       * @param string $id The service identifier
 544       */
 545      public function removeDefinition($id)
 546      {
 547          if (isset($this->definitions[$id = $this->normalizeId($id)])) {
 548              unset($this->definitions[$id]);
 549              $this->removedIds[$id] = true;
 550          }
 551      }
 552  
 553      /**
 554       * Returns true if the given service is defined.
 555       *
 556       * @param string $id The service identifier
 557       *
 558       * @return bool true if the service is defined, false otherwise
 559       */
 560      public function has($id)
 561      {
 562          $id = $this->normalizeId($id);
 563  
 564          return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
 565      }
 566  
 567      /**
 568       * Gets a service.
 569       *
 570       * @param string $id              The service identifier
 571       * @param int    $invalidBehavior The behavior when the service does not exist
 572       *
 573       * @return object|null The associated service
 574       *
 575       * @throws InvalidArgumentException          when no definitions are available
 576       * @throws ServiceCircularReferenceException When a circular reference is detected
 577       * @throws ServiceNotFoundException          When the service is not defined
 578       * @throws \Exception
 579       *
 580       * @see Reference
 581       */
 582      public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
 583      {
 584          if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) {
 585              @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);
 586          }
 587  
 588          return $this->doGet($id, $invalidBehavior);
 589      }
 590  
 591      private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = null, $isConstructorArgument = false)
 592      {
 593          $id = $this->normalizeId($id);
 594  
 595          if (isset($inlineServices[$id])) {
 596              return $inlineServices[$id];
 597          }
 598          if (null === $inlineServices) {
 599              $isConstructorArgument = true;
 600              $inlineServices = [];
 601          }
 602          try {
 603              if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
 604                  return parent::get($id, $invalidBehavior);
 605              }
 606              if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
 607                  return $service;
 608              }
 609          } catch (ServiceCircularReferenceException $e) {
 610              if ($isConstructorArgument) {
 611                  throw $e;
 612              }
 613          }
 614  
 615          if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
 616              return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices, $isConstructorArgument);
 617          }
 618  
 619          try {
 620              $definition = $this->getDefinition($id);
 621          } catch (ServiceNotFoundException $e) {
 622              if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) {
 623                  return null;
 624              }
 625  
 626              throw $e;
 627          }
 628  
 629          if ($isConstructorArgument) {
 630              $this->loading[$id] = true;
 631          }
 632  
 633          try {
 634              return $this->createService($definition, $inlineServices, $isConstructorArgument, $id);
 635          } finally {
 636              if ($isConstructorArgument) {
 637                  unset($this->loading[$id]);
 638              }
 639          }
 640      }
 641  
 642      /**
 643       * Merges a ContainerBuilder with the current ContainerBuilder configuration.
 644       *
 645       * Service definitions overrides the current defined ones.
 646       *
 647       * But for parameters, they are overridden by the current ones. It allows
 648       * the parameters passed to the container constructor to have precedence
 649       * over the loaded ones.
 650       *
 651       *     $container = new ContainerBuilder(new ParameterBag(['foo' => 'bar']));
 652       *     $loader = new LoaderXXX($container);
 653       *     $loader->load('resource_name');
 654       *     $container->register('foo', 'stdClass');
 655       *
 656       * In the above example, even if the loaded resource defines a foo
 657       * parameter, the value will still be 'bar' as defined in the ContainerBuilder
 658       * constructor.
 659       *
 660       * @throws BadMethodCallException When this ContainerBuilder is compiled
 661       */
 662      public function merge(self $container)
 663      {
 664          if ($this->isCompiled()) {
 665              throw new BadMethodCallException('Cannot merge on a compiled container.');
 666          }
 667  
 668          $this->addDefinitions($container->getDefinitions());
 669          $this->addAliases($container->getAliases());
 670          $this->getParameterBag()->add($container->getParameterBag()->all());
 671  
 672          if ($this->trackResources) {
 673              foreach ($container->getResources() as $resource) {
 674                  $this->addResource($resource);
 675              }
 676          }
 677  
 678          foreach ($this->extensions as $name => $extension) {
 679              if (!isset($this->extensionConfigs[$name])) {
 680                  $this->extensionConfigs[$name] = [];
 681              }
 682  
 683              $this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
 684          }
 685  
 686          if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
 687              $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
 688              $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
 689          } else {
 690              $envPlaceholders = [];
 691          }
 692  
 693          foreach ($container->envCounters as $env => $count) {
 694              if (!$count && !isset($envPlaceholders[$env])) {
 695                  continue;
 696              }
 697              if (!isset($this->envCounters[$env])) {
 698                  $this->envCounters[$env] = $count;
 699              } else {
 700                  $this->envCounters[$env] += $count;
 701              }
 702          }
 703  
 704          foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
 705              if (isset($this->autoconfiguredInstanceof[$interface])) {
 706                  throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
 707              }
 708  
 709              $this->autoconfiguredInstanceof[$interface] = $childDefinition;
 710          }
 711      }
 712  
 713      /**
 714       * Returns the configuration array for the given extension.
 715       *
 716       * @param string $name The name of the extension
 717       *
 718       * @return array An array of configuration
 719       */
 720      public function getExtensionConfig($name)
 721      {
 722          if (!isset($this->extensionConfigs[$name])) {
 723              $this->extensionConfigs[$name] = [];
 724          }
 725  
 726          return $this->extensionConfigs[$name];
 727      }
 728  
 729      /**
 730       * Prepends a config array to the configs of the given extension.
 731       *
 732       * @param string $name   The name of the extension
 733       * @param array  $config The config to set
 734       */
 735      public function prependExtensionConfig($name, array $config)
 736      {
 737          if (!isset($this->extensionConfigs[$name])) {
 738              $this->extensionConfigs[$name] = [];
 739          }
 740  
 741          array_unshift($this->extensionConfigs[$name], $config);
 742      }
 743  
 744      /**
 745       * Compiles the container.
 746       *
 747       * This method passes the container to compiler
 748       * passes whose job is to manipulate and optimize
 749       * the container.
 750       *
 751       * The main compiler passes roughly do four things:
 752       *
 753       *  * The extension configurations are merged;
 754       *  * Parameter values are resolved;
 755       *  * The parameter bag is frozen;
 756       *  * Extension loading is disabled.
 757       *
 758       * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
 759       *                                     env vars or be replaced by uniquely identifiable placeholders.
 760       *                                     Set to "true" when you want to use the current ContainerBuilder
 761       *                                     directly, keep to "false" when the container is dumped instead.
 762       */
 763      public function compile(/*$resolveEnvPlaceholders = false*/)
 764      {
 765          if (1 <= \func_num_args()) {
 766              $resolveEnvPlaceholders = func_get_arg(0);
 767          } else {
 768              if (__CLASS__ !== static::class) {
 769                  $r = new \ReflectionMethod($this, __FUNCTION__);
 770                  if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
 771                      @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);
 772                  }
 773              }
 774              $resolveEnvPlaceholders = false;
 775          }
 776          $compiler = $this->getCompiler();
 777  
 778          if ($this->trackResources) {
 779              foreach ($compiler->getPassConfig()->getPasses() as $pass) {
 780                  $this->addObjectResource($pass);
 781              }
 782          }
 783          $bag = $this->getParameterBag();
 784  
 785          if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
 786              $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
 787          }
 788  
 789          $compiler->compile($this);
 790  
 791          foreach ($this->definitions as $id => $definition) {
 792              if ($this->trackResources && $definition->isLazy()) {
 793                  $this->getReflectionClass($definition->getClass());
 794              }
 795          }
 796  
 797          $this->extensionConfigs = [];
 798  
 799          if ($bag instanceof EnvPlaceholderParameterBag) {
 800              if ($resolveEnvPlaceholders) {
 801                  $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
 802              }
 803  
 804              $this->envPlaceholders = $bag->getEnvPlaceholders();
 805          }
 806  
 807          parent::compile();
 808  
 809          foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) {
 810              if (!$definition->isPublic() || $definition->isPrivate()) {
 811                  $this->removedIds[$id] = true;
 812              }
 813          }
 814      }
 815  
 816      /**
 817       * {@inheritdoc}
 818       */
 819      public function getServiceIds()
 820      {
 821          return array_map('strval', array_unique(array_merge(array_keys($this->getDefinitions()), array_keys($this->aliasDefinitions), parent::getServiceIds())));
 822      }
 823  
 824      /**
 825       * Gets removed service or alias ids.
 826       *
 827       * @return array
 828       */
 829      public function getRemovedIds()
 830      {
 831          return $this->removedIds;
 832      }
 833  
 834      /**
 835       * Adds the service aliases.
 836       */
 837      public function addAliases(array $aliases)
 838      {
 839          foreach ($aliases as $alias => $id) {
 840              $this->setAlias($alias, $id);
 841          }
 842      }
 843  
 844      /**
 845       * Sets the service aliases.
 846       */
 847      public function setAliases(array $aliases)
 848      {
 849          $this->aliasDefinitions = [];
 850          $this->addAliases($aliases);
 851      }
 852  
 853      /**
 854       * Sets an alias for an existing service.
 855       *
 856       * @param string       $alias The alias to create
 857       * @param string|Alias $id    The service to alias
 858       *
 859       * @return Alias
 860       *
 861       * @throws InvalidArgumentException if the id is not a string or an Alias
 862       * @throws InvalidArgumentException if the alias is for itself
 863       */
 864      public function setAlias($alias, $id)
 865      {
 866          $alias = $this->normalizeId($alias);
 867  
 868          if ('' === $alias || '\\' === substr($alias, -1) || \strlen($alias) !== strcspn($alias, "\0\r\n'")) {
 869              throw new InvalidArgumentException(sprintf('Invalid alias id: "%s"', $alias));
 870          }
 871  
 872          if (\is_string($id)) {
 873              $id = new Alias($this->normalizeId($id));
 874          } elseif (!$id instanceof Alias) {
 875              throw new InvalidArgumentException('$id must be a string, or an Alias object.');
 876          }
 877  
 878          if ($alias === (string) $id) {
 879              throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
 880          }
 881  
 882          unset($this->definitions[$alias], $this->removedIds[$alias]);
 883  
 884          return $this->aliasDefinitions[$alias] = $id;
 885      }
 886  
 887      /**
 888       * Removes an alias.
 889       *
 890       * @param string $alias The alias to remove
 891       */
 892      public function removeAlias($alias)
 893      {
 894          if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) {
 895              unset($this->aliasDefinitions[$alias]);
 896              $this->removedIds[$alias] = true;
 897          }
 898      }
 899  
 900      /**
 901       * Returns true if an alias exists under the given identifier.
 902       *
 903       * @param string $id The service identifier
 904       *
 905       * @return bool true if the alias exists, false otherwise
 906       */
 907      public function hasAlias($id)
 908      {
 909          return isset($this->aliasDefinitions[$this->normalizeId($id)]);
 910      }
 911  
 912      /**
 913       * Gets all defined aliases.
 914       *
 915       * @return Alias[] An array of aliases
 916       */
 917      public function getAliases()
 918      {
 919          return $this->aliasDefinitions;
 920      }
 921  
 922      /**
 923       * Gets an alias.
 924       *
 925       * @param string $id The service identifier
 926       *
 927       * @return Alias An Alias instance
 928       *
 929       * @throws InvalidArgumentException if the alias does not exist
 930       */
 931      public function getAlias($id)
 932      {
 933          $id = $this->normalizeId($id);
 934  
 935          if (!isset($this->aliasDefinitions[$id])) {
 936              throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
 937          }
 938  
 939          return $this->aliasDefinitions[$id];
 940      }
 941  
 942      /**
 943       * Registers a service definition.
 944       *
 945       * This methods allows for simple registration of service definition
 946       * with a fluid interface.
 947       *
 948       * @param string      $id    The service identifier
 949       * @param string|null $class The service class
 950       *
 951       * @return Definition A Definition instance
 952       */
 953      public function register($id, $class = null)
 954      {
 955          return $this->setDefinition($id, new Definition($class));
 956      }
 957  
 958      /**
 959       * Registers an autowired service definition.
 960       *
 961       * This method implements a shortcut for using setDefinition() with
 962       * an autowired definition.
 963       *
 964       * @param string      $id    The service identifier
 965       * @param string|null $class The service class
 966       *
 967       * @return Definition The created definition
 968       */
 969      public function autowire($id, $class = null)
 970      {
 971          return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
 972      }
 973  
 974      /**
 975       * Adds the service definitions.
 976       *
 977       * @param Definition[] $definitions An array of service definitions
 978       */
 979      public function addDefinitions(array $definitions)
 980      {
 981          foreach ($definitions as $id => $definition) {
 982              $this->setDefinition($id, $definition);
 983          }
 984      }
 985  
 986      /**
 987       * Sets the service definitions.
 988       *
 989       * @param Definition[] $definitions An array of service definitions
 990       */
 991      public function setDefinitions(array $definitions)
 992      {
 993          $this->definitions = [];
 994          $this->addDefinitions($definitions);
 995      }
 996  
 997      /**
 998       * Gets all service definitions.
 999       *
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($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              $deprecationWhitelist = ['event_dispatcher' => ContainerAwareEventDispatcher::class];
1180  
1181              if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$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  }


Generated: Tue Apr 7 19:44:41 2020 Cross-referenced by PHPXref 0.7.1