annotate vendor/symfony/dependency-injection/ContainerBuilder.php @ 19:fa3358dc1485 tip

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