comparison vendor/symfony/dependency-injection/ContainerBuilder.php @ 14:1fec387a4317

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