vendor/symfony/symfony/src/Symfony/Component/DependencyInjection/Container.php line 329

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\DependencyInjection;
  11. use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
  12. use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
  13. use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
  14. use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
  15. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  16. use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
  17. use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
  18. /**
  19.  * Container is a dependency injection container.
  20.  *
  21.  * It gives access to object instances (services).
  22.  *
  23.  * Services and parameters are simple key/pair stores.
  24.  *
  25.  * Parameter and service keys are case insensitive.
  26.  *
  27.  * A service can also be defined by creating a method named
  28.  * getXXXService(), where XXX is the camelized version of the id:
  29.  *
  30.  * <ul>
  31.  *   <li>request -> getRequestService()</li>
  32.  *   <li>mysql_session_storage -> getMysqlSessionStorageService()</li>
  33.  *   <li>symfony.mysql_session_storage -> getSymfony_MysqlSessionStorageService()</li>
  34.  * </ul>
  35.  *
  36.  * The container can have three possible behaviors when a service does not exist:
  37.  *
  38.  *  * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default)
  39.  *  * NULL_ON_INVALID_REFERENCE:      Returns null
  40.  *  * IGNORE_ON_INVALID_REFERENCE:    Ignores the wrapping command asking for the reference
  41.  *                                    (for instance, ignore a setter if the service does not exist)
  42.  *
  43.  * @author Fabien Potencier <fabien@symfony.com>
  44.  * @author Johannes M. Schmitt <schmittjoh@gmail.com>
  45.  */
  46. class Container implements ResettableContainerInterface
  47. {
  48.     protected $parameterBag;
  49.     protected $services = array();
  50.     protected $methodMap = array();
  51.     protected $aliases = array();
  52.     protected $loading = array();
  53.     /**
  54.      * @internal
  55.      */
  56.     protected $privates = array();
  57.     /**
  58.      * @internal
  59.      */
  60.     protected $normalizedIds = array();
  61.     private $underscoreMap = array('_' => '''.' => '_''\\' => '_');
  62.     private $envCache = array();
  63.     private $compiled false;
  64.     public function __construct(ParameterBagInterface $parameterBag null)
  65.     {
  66.         $this->parameterBag $parameterBag ?: new EnvPlaceholderParameterBag();
  67.     }
  68.     /**
  69.      * Compiles the container.
  70.      *
  71.      * This method does two things:
  72.      *
  73.      *  * Parameter values are resolved;
  74.      *  * The parameter bag is frozen.
  75.      */
  76.     public function compile()
  77.     {
  78.         $this->parameterBag->resolve();
  79.         $this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
  80.         $this->compiled true;
  81.     }
  82.     /**
  83.      * Returns true if the container is compiled.
  84.      *
  85.      * @return bool
  86.      */
  87.     public function isCompiled()
  88.     {
  89.         return $this->compiled;
  90.     }
  91.     /**
  92.      * Returns true if the container parameter bag are frozen.
  93.      *
  94.      * @deprecated since version 3.3, to be removed in 4.0.
  95.      *
  96.      * @return bool true if the container parameter bag are frozen, false otherwise
  97.      */
  98.     public function isFrozen()
  99.     {
  100.         @trigger_error(sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.'__METHOD__), E_USER_DEPRECATED);
  101.         return $this->parameterBag instanceof FrozenParameterBag;
  102.     }
  103.     /**
  104.      * Gets the service container parameter bag.
  105.      *
  106.      * @return ParameterBagInterface A ParameterBagInterface instance
  107.      */
  108.     public function getParameterBag()
  109.     {
  110.         return $this->parameterBag;
  111.     }
  112.     /**
  113.      * Gets a parameter.
  114.      *
  115.      * @param string $name The parameter name
  116.      *
  117.      * @return mixed The parameter value
  118.      *
  119.      * @throws InvalidArgumentException if the parameter is not defined
  120.      */
  121.     public function getParameter($name)
  122.     {
  123.         return $this->parameterBag->get($name);
  124.     }
  125.     /**
  126.      * Checks if a parameter exists.
  127.      *
  128.      * @param string $name The parameter name
  129.      *
  130.      * @return bool The presence of parameter in container
  131.      */
  132.     public function hasParameter($name)
  133.     {
  134.         return $this->parameterBag->has($name);
  135.     }
  136.     /**
  137.      * Sets a parameter.
  138.      *
  139.      * @param string $name  The parameter name
  140.      * @param mixed  $value The parameter value
  141.      */
  142.     public function setParameter($name$value)
  143.     {
  144.         $this->parameterBag->set($name$value);
  145.     }
  146.     /**
  147.      * Sets a service.
  148.      *
  149.      * Setting a service to null resets the service: has() returns false and get()
  150.      * behaves in the same way as if the service was never created.
  151.      *
  152.      * @param string $id      The service identifier
  153.      * @param object $service The service instance
  154.      */
  155.     public function set($id$service)
  156.     {
  157.         $id $this->normalizeId($id);
  158.         if ('service_container' === $id) {
  159.             throw new InvalidArgumentException('You cannot set service "service_container".');
  160.         }
  161.         if (isset($this->aliases[$id])) {
  162.             unset($this->aliases[$id]);
  163.         }
  164.         $wasSet = isset($this->services[$id]);
  165.         $this->services[$id] = $service;
  166.         if (null === $service) {
  167.             unset($this->services[$id]);
  168.         }
  169.         if (isset($this->privates[$id])) {
  170.             if (null === $service) {
  171.                 @trigger_error(sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.'$id), E_USER_DEPRECATED);
  172.                 unset($this->privates[$id]);
  173.             } else {
  174.                 @trigger_error(sprintf('The "%s" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0.'$id), E_USER_DEPRECATED);
  175.             }
  176.         } elseif ($wasSet && isset($this->methodMap[$id])) {
  177.             if (null === $service) {
  178.                 @trigger_error(sprintf('The "%s" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0.'$id), E_USER_DEPRECATED);
  179.             } else {
  180.                 @trigger_error(sprintf('The "%s" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.'$id), E_USER_DEPRECATED);
  181.             }
  182.         }
  183.     }
  184.     /**
  185.      * Returns true if the given service is defined.
  186.      *
  187.      * @param string $id The service identifier
  188.      *
  189.      * @return bool true if the service is defined, false otherwise
  190.      */
  191.     public function has($id)
  192.     {
  193.         for ($i 2;;) {
  194.             if (isset($this->privates[$id])) {
  195.                 @trigger_error(sprintf('The "%s" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0.'$id), E_USER_DEPRECATED);
  196.             }
  197.             if (isset($this->aliases[$id])) {
  198.                 $id $this->aliases[$id];
  199.             }
  200.             if (isset($this->services[$id])) {
  201.                 return true;
  202.             }
  203.             if ('service_container' === $id) {
  204.                 return true;
  205.             }
  206.             if (isset($this->methodMap[$id])) {
  207.                 return true;
  208.             }
  209.             if (--$i && $id !== $normalizedId $this->normalizeId($id)) {
  210.                 $id $normalizedId;
  211.                 continue;
  212.             }
  213.             // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
  214.             // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
  215.             if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this'get'.strtr($id$this->underscoreMap).'Service')) {
  216.                 @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.'E_USER_DEPRECATED);
  217.                 return true;
  218.             }
  219.             return false;
  220.         }
  221.     }
  222.     /**
  223.      * Gets a service.
  224.      *
  225.      * If a service is defined both through a set() method and
  226.      * with a get{$id}Service() method, the former has always precedence.
  227.      *
  228.      * @param string $id              The service identifier
  229.      * @param int    $invalidBehavior The behavior when the service does not exist
  230.      *
  231.      * @return object The associated service
  232.      *
  233.      * @throws ServiceCircularReferenceException When a circular reference is detected
  234.      * @throws ServiceNotFoundException          When the service is not defined
  235.      * @throws \Exception                        if an exception has been thrown when the service has been resolved
  236.      *
  237.      * @see Reference
  238.      */
  239.     public function get($id$invalidBehavior self::EXCEPTION_ON_INVALID_REFERENCE)
  240.     {
  241.         // Attempt to retrieve the service by checking first aliases then
  242.         // available services. Service IDs are case insensitive, however since
  243.         // this method can be called thousands of times during a request, avoid
  244.         // calling $this->normalizeId($id) unless necessary.
  245.         for ($i 2;;) {
  246.             if (isset($this->privates[$id])) {
  247.                 @trigger_error(sprintf('The "%s" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop getting services directly from the container and use dependency injection instead.'$id), E_USER_DEPRECATED);
  248.             }
  249.             if (isset($this->aliases[$id])) {
  250.                 $id $this->aliases[$id];
  251.             }
  252.             // Re-use shared service instance if it exists.
  253.             if (isset($this->services[$id])) {
  254.                 return $this->services[$id];
  255.             }
  256.             if ('service_container' === $id) {
  257.                 return $this;
  258.             }
  259.             if (isset($this->loading[$id])) {
  260.                 throw new ServiceCircularReferenceException($idarray_keys($this->loading));
  261.             }
  262.             if (isset($this->methodMap[$id])) {
  263.                 $method $this->methodMap[$id];
  264.             } elseif (--$i && $id !== $normalizedId $this->normalizeId($id)) {
  265.                 $id $normalizedId;
  266.                 continue;
  267.             } elseif (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class && method_exists($this$method 'get'.strtr($id$this->underscoreMap).'Service')) {
  268.                 // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
  269.                 // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
  270.                 @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.'E_USER_DEPRECATED);
  271.                 // $method is set to the right value, proceed
  272.             } else {
  273.                 if (self::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) {
  274.                     if (!$id) {
  275.                         throw new ServiceNotFoundException($id);
  276.                     }
  277.                     $alternatives = array();
  278.                     foreach ($this->getServiceIds() as $knownId) {
  279.                         $lev levenshtein($id$knownId);
  280.                         if ($lev <= strlen($id) / || false !== strpos($knownId$id)) {
  281.                             $alternatives[] = $knownId;
  282.                         }
  283.                     }
  284.                     throw new ServiceNotFoundException($idnullnull$alternatives);
  285.                 }
  286.                 return;
  287.             }
  288.             $this->loading[$id] = true;
  289.             try {
  290.                 $service $this->$method();
  291.             } catch (\Exception $e) {
  292.                 unset($this->services[$id]);
  293.                 throw $e;
  294.             } finally {
  295.                 unset($this->loading[$id]);
  296.             }
  297.             return $service;
  298.         }
  299.     }
  300.     /**
  301.      * Returns true if the given service has actually been initialized.
  302.      *
  303.      * @param string $id The service identifier
  304.      *
  305.      * @return bool true if service has already been initialized, false otherwise
  306.      */
  307.     public function initialized($id)
  308.     {
  309.         $id $this->normalizeId($id);
  310.         if (isset($this->aliases[$id])) {
  311.             $id $this->aliases[$id];
  312.         }
  313.         if ('service_container' === $id) {
  314.             return false;
  315.         }
  316.         return isset($this->services[$id]);
  317.     }
  318.     /**
  319.      * {@inheritdoc}
  320.      */
  321.     public function reset()
  322.     {
  323.         $this->services = array();
  324.     }
  325.     /**
  326.      * Gets all service ids.
  327.      *
  328.      * @return array An array of all defined service ids
  329.      */
  330.     public function getServiceIds()
  331.     {
  332.         $ids = array();
  333.         if (!$this->methodMap && !$this instanceof ContainerBuilder && __CLASS__ !== static::class) {
  334.             // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder,
  335.             // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper)
  336.             @trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.'E_USER_DEPRECATED);
  337.             foreach (get_class_methods($this) as $method) {
  338.                 if (preg_match('/^get(.+)Service$/'$method$match)) {
  339.                     $ids[] = self::underscore($match[1]);
  340.                 }
  341.             }
  342.         }
  343.         $ids[] = 'service_container';
  344.         return array_unique(array_merge($idsarray_keys($this->methodMap), array_keys($this->services)));
  345.     }
  346.     /**
  347.      * Camelizes a string.
  348.      *
  349.      * @param string $id A string to camelize
  350.      *
  351.      * @return string The camelized string
  352.      */
  353.     public static function camelize($id)
  354.     {
  355.         return strtr(ucwords(strtr($id, array('_' => ' ''.' => '_ ''\\' => '_ '))), array(' ' => ''));
  356.     }
  357.     /**
  358.      * A string to underscore.
  359.      *
  360.      * @param string $id The string to underscore
  361.      *
  362.      * @return string The underscored string
  363.      */
  364.     public static function underscore($id)
  365.     {
  366.         return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/''/([a-z\d])([A-Z])/'), array('\\1_\\2''\\1_\\2'), str_replace('_''.'$id)));
  367.     }
  368.     /**
  369.      * Fetches a variable from the environment.
  370.      *
  371.      * @param string $name The name of the environment variable
  372.      *
  373.      * @return mixed The value to use for the provided environment variable name
  374.      *
  375.      * @throws EnvNotFoundException When the environment variable is not found and has no default value
  376.      */
  377.     protected function getEnv($name)
  378.     {
  379.         if (isset($this->envCache[$name]) || array_key_exists($name$this->envCache)) {
  380.             return $this->envCache[$name];
  381.         }
  382.         if (isset($_ENV[$name])) {
  383.             return $this->envCache[$name] = $_ENV[$name];
  384.         }
  385.         if (isset($_SERVER[$name]) && !== strpos($name'HTTP_')) {
  386.             return $this->envCache[$name] = $_SERVER[$name];
  387.         }
  388.         if (false !== ($env getenv($name)) && null !== $env) { // null is a possible value because of thread safety issues
  389.             return $this->envCache[$name] = $env;
  390.         }
  391.         if (!$this->hasParameter("env($name)")) {
  392.             throw new EnvNotFoundException($name);
  393.         }
  394.         return $this->envCache[$name] = $this->getParameter("env($name)");
  395.     }
  396.     /**
  397.      * Returns the case sensitive id used at registration time.
  398.      *
  399.      * @param string $id
  400.      *
  401.      * @return string
  402.      *
  403.      * @internal
  404.      */
  405.     public function normalizeId($id)
  406.     {
  407.         if (!is_string($id)) {
  408.             $id = (string) $id;
  409.         }
  410.         if (isset($this->normalizedIds[$normalizedId strtolower($id)])) {
  411.             $normalizedId $this->normalizedIds[$normalizedId];
  412.             if ($id !== $normalizedId) {
  413.                 @trigger_error(sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.3.'$id$normalizedId), E_USER_DEPRECATED);
  414.             }
  415.         } else {
  416.             $normalizedId $this->normalizedIds[$normalizedId] = $id;
  417.         }
  418.         return $normalizedId;
  419.     }
  420.     private function __clone()
  421.     {
  422.     }
  423. }