Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / symfony / dependency-injection / ContainerBuilder.php
index b788de5a7b5c892ae685fdb8d6d7653f90278e8c..7f79e2f4ffc551a8b2f2c53dca7e6a7513e0a87b 100644 (file)
 
 namespace Symfony\Component\DependencyInjection;
 
+use Psr\Container\ContainerInterface as PsrContainerInterface;
+use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
+use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
+use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
 use Symfony\Component\DependencyInjection\Compiler\Compiler;
 use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
+use Symfony\Component\DependencyInjection\Compiler\ResolveEnvPlaceholdersPass;
 use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
 use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 use Symfony\Component\DependencyInjection\Exception\LogicException;
@@ -22,11 +27,19 @@ use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceExce
 use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
 use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
 use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
+use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
+use Symfony\Component\Config\Resource\ClassExistenceResource;
+use Symfony\Component\Config\Resource\ComposerResource;
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Config\Resource\FileExistenceResource;
 use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Resource\GlobResource;
+use Symfony\Component\Config\Resource\ReflectionClassResource;
 use Symfony\Component\Config\Resource\ResourceInterface;
 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\InstantiatorInterface;
 use Symfony\Component\DependencyInjection\LazyProxy\Instantiator\RealServiceInstantiator;
 use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
+use Symfony\Component\EventDispatcher\ContainerAwareEventDispatcher;
 use Symfony\Component\ExpressionLanguage\Expression;
 use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
 
@@ -86,13 +99,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     private $expressionLanguageProviders = array();
 
-    public function __construct(ParameterBagInterface $parameterBag = null)
-    {
-        parent::__construct($parameterBag);
-
-        $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
-    }
-
     /**
      * @var string[] with tag names used by findTaggedServiceIds
      */
@@ -108,13 +114,52 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     private $envCounters = array();
 
+    /**
+     * @var string[] the list of vendor directories
+     */
+    private $vendors;
+
+    private $autoconfiguredInstanceof = array();
+
+    private $removedIds = array();
+    private $alreadyLoading = array();
+
+    private static $internalTypes = array(
+        'int' => true,
+        'float' => true,
+        'string' => true,
+        'bool' => true,
+        'resource' => true,
+        'object' => true,
+        'array' => true,
+        'null' => true,
+        'callable' => true,
+        'iterable' => true,
+        'mixed' => true,
+    );
+
+    public function __construct(ParameterBagInterface $parameterBag = null)
+    {
+        parent::__construct($parameterBag);
+
+        $this->trackResources = interface_exists('Symfony\Component\Config\Resource\ResourceInterface');
+        $this->setDefinition('service_container', (new Definition(ContainerInterface::class))->setSynthetic(true)->setPublic(true));
+        $this->setAlias(PsrContainerInterface::class, new Alias('service_container', false));
+        $this->setAlias(ContainerInterface::class, new Alias('service_container', false));
+    }
+
+    /**
+     * @var \ReflectionClass[] a list of class reflectors
+     */
+    private $classReflectors;
+
     /**
      * Sets the track resources flag.
      *
      * If you are not using the loaders and therefore don't want
      * to depend on the Config component, set this flag to false.
      *
-     * @param bool $track true if you want to track resources, false otherwise
+     * @param bool $track True if you want to track resources, false otherwise
      */
     public function setResourceTracking($track)
     {
@@ -124,7 +169,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * Checks if resources are tracked.
      *
-     * @return bool true if resources are tracked, false otherwise
+     * @return bool true If resources are tracked, false otherwise
      */
     public function isTrackingResources()
     {
@@ -133,19 +178,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
     /**
      * Sets the instantiator to be used when fetching proxies.
-     *
-     * @param InstantiatorInterface $proxyInstantiator
      */
     public function setProxyInstantiator(InstantiatorInterface $proxyInstantiator)
     {
         $this->proxyInstantiator = $proxyInstantiator;
     }
 
-    /**
-     * Registers an extension.
-     *
-     * @param ExtensionInterface $extension An extension instance
-     */
     public function registerExtension(ExtensionInterface $extension)
     {
         $this->extensions[$extension->getAlias()] = $extension;
@@ -206,14 +244,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getResources()
     {
-        return array_unique($this->resources);
+        return array_values($this->resources);
     }
 
     /**
-     * Adds a resource for this configuration.
-     *
-     * @param ResourceInterface $resource A resource instance
-     *
      * @return $this
      */
     public function addResource(ResourceInterface $resource)
@@ -222,7 +256,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             return $this;
         }
 
-        $this->resources[] = $resource;
+        if ($resource instanceof GlobResource && $this->inVendors($resource->getPrefix())) {
+            return $this;
+        }
+
+        $this->resources[(string) $resource] = $resource;
 
         return $this;
     }
@@ -248,14 +286,39 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * Adds the object class hierarchy as resources.
      *
-     * @param object $object An object instance
+     * @param object|string $object An object instance or class name
      *
      * @return $this
      */
     public function addObjectResource($object)
     {
         if ($this->trackResources) {
-            $this->addClassResource(new \ReflectionClass($object));
+            if (is_object($object)) {
+                $object = get_class($object);
+            }
+            if (!isset($this->classReflectors[$object])) {
+                $this->classReflectors[$object] = new \ReflectionClass($object);
+            }
+            $class = $this->classReflectors[$object];
+
+            foreach ($class->getInterfaceNames() as $name) {
+                if (null === $interface = &$this->classReflectors[$name]) {
+                    $interface = new \ReflectionClass($name);
+                }
+                $file = $interface->getFileName();
+                if (false !== $file && file_exists($file)) {
+                    $this->fileExists($file);
+                }
+            }
+            do {
+                $file = $class->getFileName();
+                if (false !== $file && file_exists($file)) {
+                    $this->fileExists($file);
+                }
+                foreach ($class->getTraitNames() as $name) {
+                    $this->addObjectResource($name);
+                }
+            } while ($class = $class->getParentClass());
         }
 
         return $this;
@@ -264,23 +327,109 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     /**
      * Adds the given class hierarchy as resources.
      *
-     * @param \ReflectionClass $class
-     *
      * @return $this
+     *
+     * @deprecated since version 3.3, to be removed in 4.0. Use addObjectResource() or getReflectionClass() instead.
      */
     public function addClassResource(\ReflectionClass $class)
     {
-        if (!$this->trackResources) {
-            return $this;
+        @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);
+
+        return $this->addObjectResource($class->name);
+    }
+
+    /**
+     * Retrieves the requested reflection class and registers it for resource tracking.
+     *
+     * @param string $class
+     * @param bool   $throw
+     *
+     * @return \ReflectionClass|null
+     *
+     * @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
+     *
+     * @final
+     */
+    public function getReflectionClass($class, $throw = true)
+    {
+        if (!$class = $this->getParameterBag()->resolveValue($class)) {
+            return;
+        }
+
+        if (isset(self::$internalTypes[$class])) {
+            return null;
         }
 
-        do {
-            if (is_file($class->getFileName())) {
-                $this->addResource(new FileResource($class->getFileName()));
+        $resource = null;
+
+        try {
+            if (isset($this->classReflectors[$class])) {
+                $classReflector = $this->classReflectors[$class];
+            } elseif ($this->trackResources) {
+                $resource = new ClassExistenceResource($class, false);
+                $classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
+            } else {
+                $classReflector = new \ReflectionClass($class);
             }
-        } while ($class = $class->getParentClass());
+        } catch (\ReflectionException $e) {
+            if ($throw) {
+                throw $e;
+            }
+            $classReflector = false;
+        }
 
-        return $this;
+        if ($this->trackResources) {
+            if (!$classReflector) {
+                $this->addResource($resource ?: new ClassExistenceResource($class, false));
+            } elseif (!$classReflector->isInternal()) {
+                $path = $classReflector->getFileName();
+
+                if (!$this->inVendors($path)) {
+                    $this->addResource(new ReflectionClassResource($classReflector, $this->vendors));
+                }
+            }
+            $this->classReflectors[$class] = $classReflector;
+        }
+
+        return $classReflector ?: null;
+    }
+
+    /**
+     * Checks whether the requested file or directory exists and registers the result for resource tracking.
+     *
+     * @param string      $path          The file or directory path for which to check the existence
+     * @param bool|string $trackContents Whether to track contents of the given resource. If a string is passed,
+     *                                   it will be used as pattern for tracking contents of the requested directory
+     *
+     * @return bool
+     *
+     * @final
+     */
+    public function fileExists($path, $trackContents = true)
+    {
+        $exists = file_exists($path);
+
+        if (!$this->trackResources || $this->inVendors($path)) {
+            return $exists;
+        }
+
+        if (!$exists) {
+            $this->addResource(new FileExistenceResource($path));
+
+            return $exists;
+        }
+
+        if (is_dir($path)) {
+            if ($trackContents) {
+                $this->addResource(new DirectoryResource($path, is_string($trackContents) ? $trackContents : null));
+            } else {
+                $this->addResource(new GlobResource($path, '/*', false));
+            }
+        } elseif ($trackContents) {
+            $this->addResource(new FileResource($path));
+        }
+
+        return $exists;
     }
 
     /**
@@ -291,13 +440,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @return $this
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
-     * @throws \LogicException        if the container is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
+     * @throws \LogicException        if the extension is not registered
      */
-    public function loadFromExtension($extension, array $values = array())
+    public function loadFromExtension($extension, array $values = null)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Cannot load from an extension on a frozen container.');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Cannot load from an extension on a compiled container.');
+        }
+
+        if (func_num_args() < 2) {
+            $values = array();
         }
 
         $namespace = $this->getExtension($extension)->getAlias();
@@ -316,7 +469,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @return $this
      */
-    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, $priority = 0*/)
+    public function addCompilerPass(CompilerPassInterface $pass, $type = PassConfig::TYPE_BEFORE_OPTIMIZATION/*, int $priority = 0*/)
     {
         if (func_num_args() >= 3) {
             $priority = func_get_arg(2);
@@ -324,7 +477,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             if (__CLASS__ !== get_class($this)) {
                 $r = new \ReflectionMethod($this, __FUNCTION__);
                 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
-                    @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);
+                    @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);
                 }
             }
 
@@ -368,18 +521,18 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @param string $id      The service identifier
      * @param object $service The service instance
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
     public function set($id, $service)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
-        if ($this->isFrozen() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
-            // setting a synthetic service on a frozen container is alright
-            throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a frozen container is not allowed.', $id));
+        if ($this->isCompiled() && (isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())) {
+            // setting a synthetic service on a compiled container is alright
+            throw new BadMethodCallException(sprintf('Setting service "%s" for an unknown or non-synthetic service definition on a compiled container is not allowed.', $id));
         }
 
-        unset($this->definitions[$id], $this->aliasDefinitions[$id]);
+        unset($this->definitions[$id], $this->aliasDefinitions[$id], $this->removedIds[$id]);
 
         parent::set($id, $service);
     }
@@ -391,7 +544,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function removeDefinition($id)
     {
-        unset($this->definitions[strtolower($id)]);
+        if (isset($this->definitions[$id = $this->normalizeId($id)])) {
+            unset($this->definitions[$id]);
+            $this->removedIds[$id] = true;
+        }
     }
 
     /**
@@ -403,7 +559,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function has($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || parent::has($id);
     }
@@ -425,14 +581,29 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE)
     {
-        $id = strtolower($id);
+        if ($this->isCompiled() && isset($this->removedIds[$id = $this->normalizeId($id)])) {
+            @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);
+        }
+
+        return $this->doGet($id, $invalidBehavior);
+    }
+
+    private function doGet($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE, array &$inlineServices = array())
+    {
+        $id = $this->normalizeId($id);
 
+        if (isset($inlineServices[$id])) {
+            return $inlineServices[$id];
+        }
+        if (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $invalidBehavior) {
+            return parent::get($id, $invalidBehavior);
+        }
         if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
             return $service;
         }
 
         if (!isset($this->definitions[$id]) && isset($this->aliasDefinitions[$id])) {
-            return $this->get((string) $this->aliasDefinitions[$id], $invalidBehavior);
+            return $this->doGet((string) $this->aliasDefinitions[$id], $invalidBehavior, $inlineServices);
         }
 
         try {
@@ -445,12 +616,13 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             throw $e;
         }
 
-        $this->loading[$id] = true;
+        $loading = isset($this->alreadyLoading[$id]) ? 'loading' : 'alreadyLoading';
+        $this->{$loading}[$id] = true;
 
         try {
-            $service = $this->createService($definition, $id);
+            $service = $this->createService($definition, $inlineServices, $id);
         } finally {
-            unset($this->loading[$id]);
+            unset($this->{$loading}[$id]);
         }
 
         return $service;
@@ -474,14 +646,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * parameter, the value will still be 'bar' as defined in the ContainerBuilder
      * constructor.
      *
-     * @param ContainerBuilder $container The ContainerBuilder instance to merge
-     *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
-    public function merge(ContainerBuilder $container)
+    public function merge(self $container)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Cannot merge on a frozen container.');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Cannot merge on a compiled container.');
         }
 
         $this->addDefinitions($container->getDefinitions());
@@ -503,16 +673,30 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         }
 
         if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
+            $envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
             $this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
+        } else {
+            $envPlaceholders = array();
         }
 
         foreach ($container->envCounters as $env => $count) {
+            if (!$count && !isset($envPlaceholders[$env])) {
+                continue;
+            }
             if (!isset($this->envCounters[$env])) {
                 $this->envCounters[$env] = $count;
             } else {
                 $this->envCounters[$env] += $count;
             }
         }
+
+        foreach ($container->getAutoconfiguredInstanceof() as $interface => $childDefinition) {
+            if (isset($this->autoconfiguredInstanceof[$interface])) {
+                throw new InvalidArgumentException(sprintf('"%s" has already been autoconfigured and merge() does not support merging autoconfiguration for the same class/interface.', $interface));
+            }
+
+            $this->autoconfiguredInstanceof[$interface] = $childDefinition;
+        }
     }
 
     /**
@@ -559,9 +743,25 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *  * Parameter values are resolved;
      *  * The parameter bag is frozen;
      *  * Extension loading is disabled.
+     *
+     * @param bool $resolveEnvPlaceholders Whether %env()% parameters should be resolved using the current
+     *                                     env vars or be replaced by uniquely identifiable placeholders.
+     *                                     Set to "true" when you want to use the current ContainerBuilder
+     *                                     directly, keep to "false" when the container is dumped instead.
      */
-    public function compile()
+    public function compile(/*$resolveEnvPlaceholders = false*/)
     {
+        if (1 <= func_num_args()) {
+            $resolveEnvPlaceholders = func_get_arg(0);
+        } else {
+            if (__CLASS__ !== static::class) {
+                $r = new \ReflectionMethod($this, __FUNCTION__);
+                if (__CLASS__ !== $r->getDeclaringClass()->getName() && (1 > $r->getNumberOfParameters() || 'resolveEnvPlaceholders' !== $r->getParameters()[0]->name)) {
+                    @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);
+                }
+            }
+            $resolveEnvPlaceholders = false;
+        }
         $compiler = $this->getCompiler();
 
         if ($this->trackResources) {
@@ -569,21 +769,37 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 $this->addObjectResource($pass);
             }
         }
+        $bag = $this->getParameterBag();
+
+        if ($resolveEnvPlaceholders && $bag instanceof EnvPlaceholderParameterBag) {
+            $compiler->addPass(new ResolveEnvPlaceholdersPass(), PassConfig::TYPE_AFTER_REMOVING, -1000);
+        }
 
         $compiler->compile($this);
 
         foreach ($this->definitions as $id => $definition) {
-            if ($this->trackResources && $definition->isLazy() && ($class = $definition->getClass()) && class_exists($class)) {
-                $this->addClassResource(new \ReflectionClass($class));
+            if ($this->trackResources && $definition->isLazy()) {
+                $this->getReflectionClass($definition->getClass());
             }
         }
 
         $this->extensionConfigs = array();
-        $bag = $this->getParameterBag();
+
+        if ($bag instanceof EnvPlaceholderParameterBag) {
+            if ($resolveEnvPlaceholders) {
+                $this->parameterBag = new ParameterBag($this->resolveEnvPlaceholders($bag->all(), true));
+            }
+
+            $this->envPlaceholders = $bag->getEnvPlaceholders();
+        }
 
         parent::compile();
 
-        $this->envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : array();
+        foreach ($this->definitions + $this->aliasDefinitions as $id => $definition) {
+            if (!$definition->isPublic() || $definition->isPrivate()) {
+                $this->removedIds[$id] = true;
+            }
+        }
     }
 
     /**
@@ -597,9 +813,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
     }
 
     /**
-     * Adds the service aliases.
+     * Gets removed service or alias ids.
      *
-     * @param array $aliases An array of aliases
+     * @return array
+     */
+    public function getRemovedIds()
+    {
+        return $this->removedIds;
+    }
+
+    /**
+     * Adds the service aliases.
      */
     public function addAliases(array $aliases)
     {
@@ -610,8 +834,6 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
     /**
      * Sets the service aliases.
-     *
-     * @param array $aliases An array of aliases
      */
     public function setAliases(array $aliases)
     {
@@ -625,15 +847,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @param string       $alias The alias to create
      * @param string|Alias $id    The service to alias
      *
+     * @return Alias
+     *
      * @throws InvalidArgumentException if the id is not a string or an Alias
      * @throws InvalidArgumentException if the alias is for itself
      */
     public function setAlias($alias, $id)
     {
-        $alias = strtolower($alias);
+        $alias = $this->normalizeId($alias);
 
         if (is_string($id)) {
-            $id = new Alias($id);
+            $id = new Alias($this->normalizeId($id));
         } elseif (!$id instanceof Alias) {
             throw new InvalidArgumentException('$id must be a string, or an Alias object.');
         }
@@ -642,9 +866,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             throw new InvalidArgumentException(sprintf('An alias can not reference itself, got a circular reference on "%s".', $alias));
         }
 
-        unset($this->definitions[$alias]);
+        unset($this->definitions[$alias], $this->removedIds[$alias]);
 
-        $this->aliasDefinitions[$alias] = $id;
+        return $this->aliasDefinitions[$alias] = $id;
     }
 
     /**
@@ -654,7 +878,10 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function removeAlias($alias)
     {
-        unset($this->aliasDefinitions[strtolower($alias)]);
+        if (isset($this->aliasDefinitions[$alias = $this->normalizeId($alias)])) {
+            unset($this->aliasDefinitions[$alias]);
+            $this->removedIds[$alias] = true;
+        }
     }
 
     /**
@@ -666,7 +893,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function hasAlias($id)
     {
-        return isset($this->aliasDefinitions[strtolower($id)]);
+        return isset($this->aliasDefinitions[$this->normalizeId($id)]);
     }
 
     /**
@@ -690,7 +917,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getAlias($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         if (!isset($this->aliasDefinitions[$id])) {
             throw new InvalidArgumentException(sprintf('The service alias "%s" does not exist.', $id));
@@ -705,8 +932,8 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * This methods allows for simple registration of service definition
      * with a fluid interface.
      *
-     * @param string $id    The service identifier
-     * @param string $class The service class
+     * @param string $id         The service identifier
+     * @param string $class|null The service class
      *
      * @return Definition A Definition instance
      */
@@ -715,6 +942,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->setDefinition($id, new Definition($class));
     }
 
+    /**
+     * Registers an autowired service definition.
+     *
+     * This method implements a shortcut for using setDefinition() with
+     * an autowired definition.
+     *
+     * @param string      $id    The service identifier
+     * @param null|string $class The service class
+     *
+     * @return Definition The created definition
+     */
+    public function autowire($id, $class = null)
+    {
+        return $this->setDefinition($id, (new Definition($class))->setAutowired(true));
+    }
+
     /**
      * Adds the service definitions.
      *
@@ -756,17 +999,17 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *
      * @return Definition the service definition
      *
-     * @throws BadMethodCallException When this ContainerBuilder is frozen
+     * @throws BadMethodCallException When this ContainerBuilder is compiled
      */
     public function setDefinition($id, Definition $definition)
     {
-        if ($this->isFrozen()) {
-            throw new BadMethodCallException('Adding definition to a frozen container is not allowed');
+        if ($this->isCompiled()) {
+            throw new BadMethodCallException('Adding definition to a compiled container is not allowed');
         }
 
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
-        unset($this->aliasDefinitions[$id]);
+        unset($this->aliasDefinitions[$id], $this->removedIds[$id]);
 
         return $this->definitions[$id] = $definition;
     }
@@ -780,7 +1023,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function hasDefinition($id)
     {
-        return isset($this->definitions[strtolower($id)]);
+        return isset($this->definitions[$this->normalizeId($id)]);
     }
 
     /**
@@ -794,7 +1037,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function getDefinition($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
         if (!isset($this->definitions[$id])) {
             throw new ServiceNotFoundException($id);
@@ -816,10 +1059,21 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      */
     public function findDefinition($id)
     {
-        $id = strtolower($id);
+        $id = $this->normalizeId($id);
 
+        $seen = array();
         while (isset($this->aliasDefinitions[$id])) {
             $id = (string) $this->aliasDefinitions[$id];
+
+            if (isset($seen[$id])) {
+                $seen = array_values($seen);
+                $seen = array_slice($seen, array_search($id, $seen));
+                $seen[] = $id;
+
+                throw new ServiceCircularReferenceException($id, $seen);
+            }
+
+            $seen[$id] = $id;
         }
 
         return $this->getDefinition($id);
@@ -838,9 +1092,13 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      * @throws RuntimeException         When the service is a synthetic service
      * @throws InvalidArgumentException When configure callable is not callable
      */
-    private function createService(Definition $definition, $id, $tryProxy = true)
+    private function createService(Definition $definition, array &$inlineServices, $id = null, $tryProxy = true)
     {
-        if ($definition instanceof DefinitionDecorator) {
+        if (null === $id && isset($inlineServices[$h = spl_object_hash($definition)])) {
+            return $inlineServices[$h];
+        }
+
+        if ($definition instanceof ChildDefinition) {
             throw new RuntimeException(sprintf('Constructing service "%s" from a parent definition is not supported at build time.', $id));
         }
 
@@ -858,11 +1116,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 ->instantiateProxy(
                     $this,
                     $definition,
-                    $id, function () use ($definition, $id) {
-                        return $this->createService($definition, $id, false);
+                    $id, function () use ($definition, &$inlineServices, $id) {
+                        return $this->createService($definition, $inlineServices, $id, false);
                     }
                 );
-            $this->shareService($definition, $proxy, $id);
+            $this->shareService($definition, $proxy, $id, $inlineServices);
 
             return $proxy;
         }
@@ -873,11 +1131,15 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             require_once $parameterBag->resolveValue($definition->getFile());
         }
 
-        $arguments = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())));
+        $arguments = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getArguments())), $inlineServices);
+
+        if (null !== $id && $definition->isShared() && isset($this->services[$id]) && ($tryProxy || !$definition->isLazy())) {
+            return $this->services[$id];
+        }
 
         if (null !== $factory = $definition->getFactory()) {
             if (is_array($factory)) {
-                $factory = array($this->resolveServices($parameterBag->resolveValue($factory[0])), $factory[1]);
+                $factory = array($this->doResolveServices($parameterBag->resolveValue($factory[0]), $inlineServices), $factory[1]);
             } elseif (!is_string($factory)) {
                 throw new RuntimeException(sprintf('Cannot create service "%s" because of invalid factory', $id));
             }
@@ -892,27 +1154,30 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 }
             }
         } else {
-            $r = new \ReflectionClass($parameterBag->resolveValue($definition->getClass()));
+            $r = new \ReflectionClass($class = $parameterBag->resolveValue($definition->getClass()));
 
             $service = null === $r->getConstructor() ? $r->newInstance() : $r->newInstanceArgs($arguments);
+            // don't trigger deprecations for internal uses
+            // @deprecated since version 3.3, to be removed in 4.0 along with the deprecated class
+            $deprecationWhitelist = array('event_dispatcher' => ContainerAwareEventDispatcher::class);
 
-            if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ")) {
+            if (!$definition->isDeprecated() && 0 < strpos($r->getDocComment(), "\n * @deprecated ") && (!isset($deprecationWhitelist[$id]) || $deprecationWhitelist[$id] !== $class)) {
                 @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);
             }
         }
 
         if ($tryProxy || !$definition->isLazy()) {
             // share only if proxying failed, or if not a proxy
-            $this->shareService($definition, $service, $id);
+            $this->shareService($definition, $service, $id, $inlineServices);
         }
 
-        $properties = $this->resolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())));
+        $properties = $this->doResolveServices($parameterBag->unescapeValue($parameterBag->resolveValue($definition->getProperties())), $inlineServices);
         foreach ($properties as $name => $value) {
             $service->$name = $value;
         }
 
         foreach ($definition->getMethodCalls() as $call) {
-            $this->callMethod($service, $call);
+            $this->callMethod($service, $call, $inlineServices);
         }
 
         if ($callable = $definition->getConfigurator()) {
@@ -920,9 +1185,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
                 $callable[0] = $parameterBag->resolveValue($callable[0]);
 
                 if ($callable[0] instanceof Reference) {
-                    $callable[0] = $this->get((string) $callable[0], $callable[0]->getInvalidBehavior());
+                    $callable[0] = $this->doGet((string) $callable[0], $callable[0]->getInvalidBehavior(), $inlineServices);
                 } elseif ($callable[0] instanceof Definition) {
-                    $callable[0] = $this->createService($callable[0], null);
+                    $callable[0] = $this->createService($callable[0], $inlineServices);
                 }
             }
 
@@ -945,15 +1210,62 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *               the real service instances and all expressions evaluated
      */
     public function resolveServices($value)
+    {
+        return $this->doResolveServices($value);
+    }
+
+    private function doResolveServices($value, array &$inlineServices = array())
     {
         if (is_array($value)) {
             foreach ($value as $k => $v) {
-                $value[$k] = $this->resolveServices($v);
+                $value[$k] = $this->doResolveServices($v, $inlineServices);
             }
+        } elseif ($value instanceof ServiceClosureArgument) {
+            $reference = $value->getValues()[0];
+            $value = function () use ($reference) {
+                return $this->resolveServices($reference);
+            };
+        } elseif ($value instanceof IteratorArgument) {
+            $value = new RewindableGenerator(function () use ($value) {
+                foreach ($value->getValues() as $k => $v) {
+                    foreach (self::getServiceConditionals($v) as $s) {
+                        if (!$this->has($s)) {
+                            continue 2;
+                        }
+                    }
+                    foreach (self::getInitializedConditionals($v) as $s) {
+                        if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
+                            continue 2;
+                        }
+                    }
+
+                    yield $k => $this->resolveServices($v);
+                }
+            }, function () use ($value) {
+                $count = 0;
+                foreach ($value->getValues() as $v) {
+                    foreach (self::getServiceConditionals($v) as $s) {
+                        if (!$this->has($s)) {
+                            continue 2;
+                        }
+                    }
+                    foreach (self::getInitializedConditionals($v) as $s) {
+                        if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE)) {
+                            continue 2;
+                        }
+                    }
+
+                    ++$count;
+                }
+
+                return $count;
+            });
         } elseif ($value instanceof Reference) {
-            $value = $this->get((string) $value, $value->getInvalidBehavior());
+            $value = $this->doGet((string) $value, $value->getInvalidBehavior(), $inlineServices);
         } elseif ($value instanceof Definition) {
-            $value = $this->createService($value, null);
+            $value = $this->createService($value, $inlineServices);
+        } elseif ($value instanceof Parameter) {
+            $value = $this->getParameter((string) $value);
         } elseif ($value instanceof Expression) {
             $value = $this->getExpressionLanguage()->evaluate($value, array('container' => $this));
         }
@@ -975,16 +1287,20 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
      *     }
      * }
      *
-     * @param string $name The tag name
+     * @param string $name
+     * @param bool   $throwOnAbstract
      *
      * @return array An array of tags with the tagged service as key, holding a list of attribute arrays
      */
-    public function findTaggedServiceIds($name)
+    public function findTaggedServiceIds($name, $throwOnAbstract = false)
     {
         $this->usedTags[] = $name;
         $tags = array();
         foreach ($this->getDefinitions() as $id => $definition) {
             if ($definition->hasTag($name)) {
+                if ($throwOnAbstract && $definition->isAbstract()) {
+                    throw new InvalidArgumentException(sprintf('The service "%s" tagged "%s" must not be abstract.', $id, $name));
+                }
                 $tags[$id] = $definition->getTag($name);
             }
         }
@@ -1030,14 +1346,42 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->expressionLanguageProviders;
     }
 
+    /**
+     * Returns a ChildDefinition that will be used for autoconfiguring the interface/class.
+     *
+     * @param string $interface The class or interface to match
+     *
+     * @return ChildDefinition
+     */
+    public function registerForAutoconfiguration($interface)
+    {
+        if (!isset($this->autoconfiguredInstanceof[$interface])) {
+            $this->autoconfiguredInstanceof[$interface] = new ChildDefinition('');
+        }
+
+        return $this->autoconfiguredInstanceof[$interface];
+    }
+
+    /**
+     * Returns an array of ChildDefinition[] keyed by interface.
+     *
+     * @return ChildDefinition[]
+     */
+    public function getAutoconfiguredInstanceof()
+    {
+        return $this->autoconfiguredInstanceof;
+    }
+
     /**
      * Resolves env parameter placeholders in a string or an array.
      *
-     * @param mixed       $value     The value to resolve
-     * @param string|null $format    A sprintf() format to use as replacement for env placeholders or null to use the default parameter format
-     * @param array       &$usedEnvs Env vars found while resolving are added to this array
+     * @param mixed            $value     The value to resolve
+     * @param string|true|null $format    A sprintf() format returning the replacement for each env var name or
+     *                                    null to resolve back to the original "%env(VAR)%" format or
+     *                                    true to resolve to the actual values of the referenced env vars
+     * @param array            &$usedEnvs Env vars found while resolving are added to this array
      *
-     * @return string The string with env parameters resolved
+     * @return mixed The value with env parameters resolved if a string or an array is passed
      */
     public function resolveEnvPlaceholders($value, $format = null, array &$usedEnvs = null)
     {
@@ -1045,26 +1389,41 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             $format = '%%env(%s)%%';
         }
 
-        if (is_array($value)) {
+        $bag = $this->getParameterBag();
+        if (true === $format) {
+            $value = $bag->resolveValue($value);
+        }
+
+        if (\is_array($value)) {
             $result = array();
             foreach ($value as $k => $v) {
-                $result[$this->resolveEnvPlaceholders($k, $format, $usedEnvs)] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
+                $result[\is_string($k) ? $this->resolveEnvPlaceholders($k, $format, $usedEnvs) : $k] = $this->resolveEnvPlaceholders($v, $format, $usedEnvs);
             }
 
             return $result;
         }
 
-        if (!is_string($value)) {
+        if (!\is_string($value) || 38 > \strlen($value)) {
             return $value;
         }
-
-        $bag = $this->getParameterBag();
         $envPlaceholders = $bag instanceof EnvPlaceholderParameterBag ? $bag->getEnvPlaceholders() : $this->envPlaceholders;
 
         foreach ($envPlaceholders as $env => $placeholders) {
             foreach ($placeholders as $placeholder) {
                 if (false !== stripos($value, $placeholder)) {
-                    $value = str_ireplace($placeholder, sprintf($format, $env), $value);
+                    if (true === $format) {
+                        $resolved = $bag->escapeValue($this->getEnv($env));
+                    } else {
+                        $resolved = sprintf($format, $env);
+                    }
+                    if ($placeholder === $value) {
+                        $value = $resolved;
+                    } else {
+                        if (!is_string($resolved) && !is_numeric($resolved)) {
+                            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));
+                        }
+                        $value = str_ireplace($placeholder, $resolved, $value);
+                    }
                     $usedEnvs[$env] = $env;
                     $this->envCounters[$env] = isset($this->envCounters[$env]) ? 1 + $this->envCounters[$env] : 1;
                 }
@@ -1093,12 +1452,50 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->envCounters;
     }
 
+    /**
+     * @internal
+     */
+    public function getNormalizedIds()
+    {
+        $normalizedIds = array();
+
+        foreach ($this->normalizedIds as $k => $v) {
+            if ($v !== (string) $k) {
+                $normalizedIds[$k] = $v;
+            }
+        }
+
+        return $normalizedIds;
+    }
+
+    /**
+     * @final
+     */
+    public function log(CompilerPassInterface $pass, $message)
+    {
+        $this->getCompiler()->log($pass, $message);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function normalizeId($id)
+    {
+        if (!\is_string($id)) {
+            $id = (string) $id;
+        }
+
+        return isset($this->definitions[$id]) || isset($this->aliasDefinitions[$id]) || isset($this->removedIds[$id]) ? $id : parent::normalizeId($id);
+    }
+
     /**
      * Returns the Service Conditionals.
      *
      * @param mixed $value An array of conditionals to return
      *
      * @return array An array of Service conditionals
+     *
+     * @internal since version 3.4
      */
     public static function getServiceConditionals($value)
     {
@@ -1108,13 +1505,79 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
             foreach ($value as $v) {
                 $services = array_unique(array_merge($services, self::getServiceConditionals($v)));
             }
-        } elseif ($value instanceof Reference && $value->getInvalidBehavior() === ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
+        } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_INVALID_REFERENCE === $value->getInvalidBehavior()) {
+            $services[] = (string) $value;
+        }
+
+        return $services;
+    }
+
+    /**
+     * Returns the initialized conditionals.
+     *
+     * @param mixed $value An array of conditionals to return
+     *
+     * @return array An array of uninitialized conditionals
+     *
+     * @internal
+     */
+    public static function getInitializedConditionals($value)
+    {
+        $services = array();
+
+        if (is_array($value)) {
+            foreach ($value as $v) {
+                $services = array_unique(array_merge($services, self::getInitializedConditionals($v)));
+            }
+        } elseif ($value instanceof Reference && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()) {
             $services[] = (string) $value;
         }
 
         return $services;
     }
 
+    /**
+     * Computes a reasonably unique hash of a value.
+     *
+     * @param mixed $value A serializable value
+     *
+     * @return string
+     */
+    public static function hash($value)
+    {
+        $hash = substr(base64_encode(hash('sha256', serialize($value), true)), 0, 7);
+
+        return str_replace(array('/', '+'), array('.', '_'), strtolower($hash));
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function getEnv($name)
+    {
+        $value = parent::getEnv($name);
+        $bag = $this->getParameterBag();
+
+        if (!is_string($value) || !$bag instanceof EnvPlaceholderParameterBag) {
+            return $value;
+        }
+
+        foreach ($bag->getEnvPlaceholders() as $env => $placeholders) {
+            if (isset($placeholders[$value])) {
+                $bag = new ParameterBag($bag->all());
+
+                return $bag->unescapeValue($bag->get("env($name)"));
+            }
+        }
+
+        $this->resolving["env($name)"] = true;
+        try {
+            return $bag->unescapeValue($this->resolveEnvPlaceholders($bag->escapeValue($value), true));
+        } finally {
+            unset($this->resolving["env($name)"]);
+        }
+    }
+
     /**
      * Retrieves the currently set proxy instantiator or instantiates one.
      *
@@ -1129,30 +1592,36 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
         return $this->proxyInstantiator;
     }
 
-    private function callMethod($service, $call)
+    private function callMethod($service, $call, array &$inlineServices)
     {
-        $services = self::getServiceConditionals($call[1]);
-
-        foreach ($services as $s) {
+        foreach (self::getServiceConditionals($call[1]) as $s) {
             if (!$this->has($s)) {
                 return;
             }
         }
+        foreach (self::getInitializedConditionals($call[1]) as $s) {
+            if (!$this->doGet($s, ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE, $inlineServices)) {
+                return;
+            }
+        }
 
-        call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1]))));
+        call_user_func_array(array($service, $call[0]), $this->doResolveServices($this->getParameterBag()->unescapeValue($this->getParameterBag()->resolveValue($call[1])), $inlineServices));
     }
 
     /**
      * Shares a given service in the container.
      *
      * @param Definition  $definition
-     * @param mixed       $service
+     * @param object      $service
      * @param string|null $id
      */
-    private function shareService(Definition $definition, $service, $id)
+    private function shareService(Definition $definition, $service, $id, array &$inlineServices)
     {
+        $inlineServices[null !== $id ? $id : spl_object_hash($definition)] = $service;
+
         if (null !== $id && $definition->isShared()) {
-            $this->services[strtolower($id)] = $service;
+            $this->services[$id] = $service;
+            unset($this->loading[$id], $this->alreadyLoading[$id]);
         }
     }
 
@@ -1167,4 +1636,22 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
 
         return $this->expressionLanguage;
     }
+
+    private function inVendors($path)
+    {
+        if (null === $this->vendors) {
+            $resource = new ComposerResource();
+            $this->vendors = $resource->getVendors();
+            $this->addResource($resource);
+        }
+        $path = realpath($path) ?: $path;
+
+        foreach ($this->vendors as $vendor) {
+            if (0 === strpos($path, $vendor) && false !== strpbrk(substr($path, strlen($vendor), 1), '/'.DIRECTORY_SEPARATOR)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }