vendor/pimple/pimple/src/Pimple/Container.php line 118

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Pimple.
  4.  *
  5.  * Copyright (c) 2009 Fabien Potencier
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  8.  * of this software and associated documentation files (the "Software"), to deal
  9.  * in the Software without restriction, including without limitation the rights
  10.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11.  * copies of the Software, and to permit persons to whom the Software is furnished
  12.  * to do so, subject to the following conditions:
  13.  *
  14.  * The above copyright notice and this permission notice shall be included in all
  15.  * copies or substantial portions of the Software.
  16.  *
  17.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23.  * THE SOFTWARE.
  24.  */
  25. namespace Pimple;
  26. use Pimple\Exception\ExpectedInvokableException;
  27. use Pimple\Exception\FrozenServiceException;
  28. use Pimple\Exception\InvalidServiceIdentifierException;
  29. use Pimple\Exception\UnknownIdentifierException;
  30. /**
  31.  * Container main class.
  32.  *
  33.  * @author Fabien Potencier
  34.  */
  35. class Container implements \ArrayAccess
  36. {
  37.     private $values = array();
  38.     private $factories;
  39.     private $protected;
  40.     private $frozen = array();
  41.     private $raw = array();
  42.     private $keys = array();
  43.     /**
  44.      * Instantiates the container.
  45.      *
  46.      * Objects and parameters can be passed as argument to the constructor.
  47.      *
  48.      * @param array $values The parameters or objects
  49.      */
  50.     public function __construct(array $values = array())
  51.     {
  52.         $this->factories = new \SplObjectStorage();
  53.         $this->protected = new \SplObjectStorage();
  54.         foreach ($values as $key => $value) {
  55.             $this->offsetSet($key$value);
  56.         }
  57.     }
  58.     /**
  59.      * Sets a parameter or an object.
  60.      *
  61.      * Objects must be defined as Closures.
  62.      *
  63.      * Allowing any PHP callable leads to difficult to debug problems
  64.      * as function names (strings) are callable (creating a function with
  65.      * the same name as an existing parameter would break your container).
  66.      *
  67.      * @param string $id    The unique identifier for the parameter or object
  68.      * @param mixed  $value The value of the parameter or a closure to define an object
  69.      *
  70.      * @throws FrozenServiceException Prevent override of a frozen service
  71.      */
  72.     public function offsetSet($id$value)
  73.     {
  74.         if (isset($this->frozen[$id])) {
  75.             throw new FrozenServiceException($id);
  76.         }
  77.         $this->values[$id] = $value;
  78.         $this->keys[$id] = true;
  79.     }
  80.     /**
  81.      * Gets a parameter or an object.
  82.      *
  83.      * @param string $id The unique identifier for the parameter or object
  84.      *
  85.      * @return mixed The value of the parameter or an object
  86.      *
  87.      * @throws UnknownIdentifierException If the identifier is not defined
  88.      */
  89.     public function offsetGet($id)
  90.     {
  91.         if (!isset($this->keys[$id])) {
  92.             throw new UnknownIdentifierException($id);
  93.         }
  94.         if (
  95.             isset($this->raw[$id])
  96.             || !\is_object($this->values[$id])
  97.             || isset($this->protected[$this->values[$id]])
  98.             || !\method_exists($this->values[$id], '__invoke')
  99.         ) {
  100.             return $this->values[$id];
  101.         }
  102.         if (isset($this->factories[$this->values[$id]])) {
  103.             return $this->values[$id]($this);
  104.         }
  105.         $raw $this->values[$id];
  106.         $val $this->values[$id] = $raw($this);
  107.         $this->raw[$id] = $raw;
  108.         $this->frozen[$id] = true;
  109.         return $val;
  110.     }
  111.     /**
  112.      * Checks if a parameter or an object is set.
  113.      *
  114.      * @param string $id The unique identifier for the parameter or object
  115.      *
  116.      * @return bool
  117.      */
  118.     public function offsetExists($id)
  119.     {
  120.         return isset($this->keys[$id]);
  121.     }
  122.     /**
  123.      * Unsets a parameter or an object.
  124.      *
  125.      * @param string $id The unique identifier for the parameter or object
  126.      */
  127.     public function offsetUnset($id)
  128.     {
  129.         if (isset($this->keys[$id])) {
  130.             if (\is_object($this->values[$id])) {
  131.                 unset($this->factories[$this->values[$id]], $this->protected[$this->values[$id]]);
  132.             }
  133.             unset($this->values[$id], $this->frozen[$id], $this->raw[$id], $this->keys[$id]);
  134.         }
  135.     }
  136.     /**
  137.      * Marks a callable as being a factory service.
  138.      *
  139.      * @param callable $callable A service definition to be used as a factory
  140.      *
  141.      * @return callable The passed callable
  142.      *
  143.      * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object
  144.      */
  145.     public function factory($callable)
  146.     {
  147.         if (!\method_exists($callable'__invoke')) {
  148.             throw new ExpectedInvokableException('Service definition is not a Closure or invokable object.');
  149.         }
  150.         $this->factories->attach($callable);
  151.         return $callable;
  152.     }
  153.     /**
  154.      * Protects a callable from being interpreted as a service.
  155.      *
  156.      * This is useful when you want to store a callable as a parameter.
  157.      *
  158.      * @param callable $callable A callable to protect from being evaluated
  159.      *
  160.      * @return callable The passed callable
  161.      *
  162.      * @throws ExpectedInvokableException Service definition has to be a closure or an invokable object
  163.      */
  164.     public function protect($callable)
  165.     {
  166.         if (!\method_exists($callable'__invoke')) {
  167.             throw new ExpectedInvokableException('Callable is not a Closure or invokable object.');
  168.         }
  169.         $this->protected->attach($callable);
  170.         return $callable;
  171.     }
  172.     /**
  173.      * Gets a parameter or the closure defining an object.
  174.      *
  175.      * @param string $id The unique identifier for the parameter or object
  176.      *
  177.      * @return mixed The value of the parameter or the closure defining an object
  178.      *
  179.      * @throws UnknownIdentifierException If the identifier is not defined
  180.      */
  181.     public function raw($id)
  182.     {
  183.         if (!isset($this->keys[$id])) {
  184.             throw new UnknownIdentifierException($id);
  185.         }
  186.         if (isset($this->raw[$id])) {
  187.             return $this->raw[$id];
  188.         }
  189.         return $this->values[$id];
  190.     }
  191.     /**
  192.      * Extends an object definition.
  193.      *
  194.      * Useful when you want to extend an existing object definition,
  195.      * without necessarily loading that object.
  196.      *
  197.      * @param string   $id       The unique identifier for the object
  198.      * @param callable $callable A service definition to extend the original
  199.      *
  200.      * @return callable The wrapped callable
  201.      *
  202.      * @throws UnknownIdentifierException        If the identifier is not defined
  203.      * @throws FrozenServiceException            If the service is frozen
  204.      * @throws InvalidServiceIdentifierException If the identifier belongs to a parameter
  205.      * @throws ExpectedInvokableException        If the extension callable is not a closure or an invokable object
  206.      */
  207.     public function extend($id$callable)
  208.     {
  209.         if (!isset($this->keys[$id])) {
  210.             throw new UnknownIdentifierException($id);
  211.         }
  212.         if (isset($this->frozen[$id])) {
  213.             throw new FrozenServiceException($id);
  214.         }
  215.         if (!\is_object($this->values[$id]) || !\method_exists($this->values[$id], '__invoke')) {
  216.             throw new InvalidServiceIdentifierException($id);
  217.         }
  218.         if (isset($this->protected[$this->values[$id]])) {
  219.             @\trigger_error(\sprintf('How Pimple behaves when extending protected closures will be fixed in Pimple 4. Are you sure "%s" should be protected?'$id), \E_USER_DEPRECATED);
  220.         }
  221.         if (!\is_object($callable) || !\method_exists($callable'__invoke')) {
  222.             throw new ExpectedInvokableException('Extension service definition is not a Closure or invokable object.');
  223.         }
  224.         $factory $this->values[$id];
  225.         $extended = function ($c) use ($callable$factory) {
  226.             return $callable($factory($c), $c);
  227.         };
  228.         if (isset($this->factories[$factory])) {
  229.             $this->factories->detach($factory);
  230.             $this->factories->attach($extended);
  231.         }
  232.         return $this[$id] = $extended;
  233.     }
  234.     /**
  235.      * Returns all defined value names.
  236.      *
  237.      * @return array An array of value names
  238.      */
  239.     public function keys()
  240.     {
  241.         return \array_keys($this->values);
  242.     }
  243.     /**
  244.      * Registers a service provider.
  245.      *
  246.      * @param ServiceProviderInterface $provider A ServiceProviderInterface instance
  247.      * @param array                    $values   An array of values that customizes the provider
  248.      *
  249.      * @return static
  250.      */
  251.     public function register(ServiceProviderInterface $provider, array $values = array())
  252.     {
  253.         $provider->register($this);
  254.         foreach ($values as $key => $value) {
  255.             $this[$key] = $value;
  256.         }
  257.         return $this;
  258.     }
  259. }