Source for file Container.php

Documentation is available at Container.php

  1. <?php
  2. /**
  3.  * Part of the Joomla Framework DI Package
  4.  *
  5.  * @copyright  Copyright (C) 2013 Open Source Matters, Inc. All rights reserved.
  6.  * @license    GNU General Public License version 2 or later; see LICENSE
  7.  */
  8.  
  9. namespace Joomla\DI;
  10.  
  11. use Joomla\DI\Exception\DependencyResolutionException;
  12.  
  13. /**
  14.  * The Container class.
  15.  *
  16.  * @since  1.0
  17.  */
  18. class Container
  19. {
  20.     /**
  21.      * Holds the key aliases.
  22.      *
  23.      * @var    array  $aliases 
  24.      *
  25.      * @since  1.0
  26.      */
  27.     protected $aliases = array();
  28.  
  29.     /**
  30.      * Holds the shared instances.
  31.      *
  32.      * @var    array  $instances 
  33.      *
  34.      * @since  1.0
  35.      */
  36.     protected $instances = array();
  37.  
  38.     /**
  39.      * Holds the keys, their callbacks, and whether or not
  40.      * the item is meant to be a shared resource.
  41.      *
  42.      * @var    array  $dataStore 
  43.      *
  44.      * @since  1.0
  45.      */
  46.     protected $dataStore = array();
  47.  
  48.     /**
  49.      * Parent for hierarchical containers.
  50.      *
  51.      * @var    Container 
  52.      *
  53.      * @since  1.0
  54.      */
  55.     protected $parent;
  56.  
  57.     /**
  58.      * Constructor for the DI Container
  59.      *
  60.      * @param   Container  $parent  Parent for hierarchical containers.
  61.      *
  62.      * @since  1.0
  63.      */
  64.     public function __construct(Container $parent null)
  65.     {
  66.         $this->parent = $parent;
  67.     }
  68.  
  69.     /**
  70.      * Create an alias for a given key for easy access.
  71.      *
  72.      * @param   string  $alias  The alias name
  73.      * @param   string  $key    The key to alias
  74.      *
  75.      * @return  Container 
  76.      */
  77.     public function alias($alias$key)
  78.     {
  79.         $this->aliases[$alias$key;
  80.  
  81.         return $this;
  82.     }
  83.  
  84.     /**
  85.      * Search the aliases property for a matching alias key.
  86.      *
  87.      * @param   string  $key  The key to search for.
  88.      *
  89.      * @return  string 
  90.      *
  91.      * @since   1.0
  92.      */
  93.     protected function resolveAlias($key)
  94.     {
  95.         if (isset($this->aliases[$key]))
  96.         {
  97.             return $this->aliases[$key];
  98.         }
  99.  
  100.         return $key;
  101.     }
  102.  
  103.     /**
  104.      * Build an object of class $key;
  105.      *
  106.      * @param   string   $key     The class name to build.
  107.      * @param   boolean  $shared  True to create a shared resource.
  108.      *
  109.      * @return  mixed  Instance of class specified by $key with all dependencies injected.
  110.      *                  Returns an object if the class exists and false otherwise
  111.      *
  112.      * @since   1.0
  113.      */
  114.     public function buildObject($key$shared false)
  115.     {
  116.         try
  117.         {
  118.             $reflection new \ReflectionClass($key);
  119.         }
  120.         catch (\ReflectionException $e)
  121.         {
  122.             return false;
  123.         }
  124.  
  125.         $constructor $reflection->getConstructor();
  126.  
  127.         // If there are no parameters, just return a new object.
  128.         if (is_null($constructor))
  129.         {
  130.             $callback function (use ($key{
  131.                 return new $key;
  132.             };
  133.         }
  134.         else
  135.         {
  136.             $newInstanceArgs $this->getMethodArgs($constructor);
  137.  
  138.             // Create a callable for the dataStore
  139.             $callback function (use ($reflection$newInstanceArgs{
  140.                 return $reflection->newInstanceArgs($newInstanceArgs);
  141.             };
  142.         }
  143.  
  144.         return $this->set($key$callback$shared)->get($key);
  145.     }
  146.  
  147.     /**
  148.      * Convenience method for building a shared object.
  149.      *
  150.      * @param   string  $key  The class name to build.
  151.      *
  152.      * @return  object  Instance of class specified by $key with all dependencies injected.
  153.      *
  154.      * @since   1.0
  155.      */
  156.     public function buildSharedObject($key)
  157.     {
  158.         return $this->buildObject($keytrue);
  159.     }
  160.  
  161.     /**
  162.      * Create a child Container with a new property scope that
  163.      * that has the ability to access the parent scope when resolving.
  164.      *
  165.      * @return Container 
  166.      *
  167.      * @since  1.0
  168.      */
  169.     public function createChild()
  170.     {
  171.         return new static($this);
  172.     }
  173.  
  174.     /**
  175.      * Extend a defined service Closure by wrapping the existing one with a new Closure.  This
  176.      * works very similar to a decorator pattern.  Note that this only works on service Closures
  177.      * that have been defined in the current Provider, not parent providers.
  178.      *
  179.      * @param   string    $key       The unique identifier for the Closure or property.
  180.      * @param   \Closure  $callable  A Closure to wrap the original service Closure.
  181.      *
  182.      * @return  void 
  183.      *
  184.      * @since   1.0
  185.      * @throws  \InvalidArgumentException
  186.      */
  187.     public function extend($key\Closure $callable)
  188.     {
  189.         $raw $this->getRaw($key);
  190.  
  191.         if (is_null($raw))
  192.         {
  193.             throw new \InvalidArgumentException(sprintf('The requested key %s does not exist to extend.'$key));
  194.         }
  195.  
  196.         $closure function ($cuse($callable$raw{
  197.             return $callable($raw['callback']($c)$c);
  198.         };
  199.  
  200.         $this->set($key$closure$raw['shared']);
  201.     }
  202.  
  203.     /**
  204.      * Build an array of constructor parameters.
  205.      *
  206.      * @param   \ReflectionMethod  $method  Method for which to build the argument array.
  207.      *
  208.      * @return  array  Array of arguments to pass to the method.
  209.      *
  210.      * @since   1.0
  211.      * @throws  DependencyResolutionException
  212.      */
  213.     protected function getMethodArgs(\ReflectionMethod $method)
  214.     {
  215.         $methodArgs array();
  216.  
  217.         foreach ($method->getParameters(as $param)
  218.         {
  219.             $dependency $param->getClass();
  220.             $dependencyVarName $param->getName();
  221.  
  222.             // If we have a dependency, that means it has been type-hinted.
  223.             if (!is_null($dependency))
  224.             {
  225.                 $dependencyClassName $dependency->getName();
  226.  
  227.                 // If the dependency class name is registered with this container or a parent, use it.
  228.                 if ($this->getRaw($dependencyClassName!== null)
  229.                 {
  230.                     $depObject $this->get($dependencyClassName);
  231.                 }
  232.                 else
  233.                 {
  234.                     $depObject $this->buildObject($dependencyClassName);
  235.                 }
  236.  
  237.                 if ($depObject instanceof $dependencyClassName)
  238.                 {
  239.                     $methodArgs[$depObject;
  240.                     continue;
  241.                 }
  242.             }
  243.  
  244.             // Finally, if there is a default parameter, use it.
  245.             if ($param->isOptional())
  246.             {
  247.                 $methodArgs[$param->getDefaultValue();
  248.                 continue;
  249.             }
  250.  
  251.             // Couldn't resolve dependency, and no default was provided.
  252.             throw new DependencyResolutionException(sprintf('Could not resolve dependency: %s'$dependencyVarName));
  253.         }
  254.  
  255.         return $methodArgs;
  256.     }
  257.  
  258.     /**
  259.      * Method to set the key and callback to the dataStore array.
  260.      *
  261.      * @param   string   $key        Name of dataStore key to set.
  262.      * @param   mixed    $value      Callable function to run or string to retrive when requesting the specified $key.
  263.      * @param   boolean  $shared     True to create and store a shared instance.
  264.      * @param   boolean  $protected  True to protect this item from being overwritten. Useful for services.
  265.      *
  266.      * @return  \Joomla\DI\Container  This instance to support chaining.
  267.      *
  268.      * @throws  \OutOfBoundsException      Thrown if the provided key is already set and is protected.
  269.      *
  270.      * @since   1.0
  271.      */
  272.     public function set($key$value$shared false$protected false)
  273.     {
  274.         if (isset($this->dataStore[$key]&& $this->dataStore[$key]['protected'=== true)
  275.         {
  276.             throw new \OutOfBoundsException(sprintf('Key %s is protected and can\'t be overwritten.'$key));
  277.         }
  278.  
  279.         // If the provided $value is not a closure, make it one now for easy resolution.
  280.         if (!($value instanceof \Closure))
  281.         {
  282.             $value function (use ($value{
  283.                 return $value;
  284.             };
  285.         }
  286.  
  287.         $this->dataStore[$keyarray(
  288.             'callback' => $value,
  289.             'shared' => $shared,
  290.             'protected' => $protected
  291.         );
  292.  
  293.         return $this;
  294.     }
  295.  
  296.     /**
  297.      * Convenience method for creating protected keys.
  298.      *
  299.      * @param   string    $key       Name of dataStore key to set.
  300.      * @param   callable  $callback  Callable function to run when requesting the specified $key.
  301.      * @param   bool      $shared    True to create and store a shared instance.
  302.      *
  303.      * @return  \Joomla\DI\Container  This instance to support chaining.
  304.      *
  305.      * @since   1.0
  306.      */
  307.     public function protect($key$callback$shared false)
  308.     {
  309.         return $this->set($key$callback$sharedtrue);
  310.     }
  311.  
  312.     /**
  313.      * Convenience method for creating shared keys.
  314.      *
  315.      * @param   string    $key        Name of dataStore key to set.
  316.      * @param   callable  $callback   Callable function to run when requesting the specified $key.
  317.      * @param   bool      $protected  True to create and store a shared instance.
  318.      *
  319.      * @return  \Joomla\DI\Container  This instance to support chaining.
  320.      *
  321.      * @since   1.0
  322.      */
  323.     public function share($key$callback$protected false)
  324.     {
  325.         return $this->set($key$callbacktrue$protected);
  326.     }
  327.  
  328.     /**
  329.      * Method to retrieve the results of running the $callback for the specified $key;
  330.      *
  331.      * @param   string   $key       Name of the dataStore key to get.
  332.      * @param   boolean  $forceNew  True to force creation and return of a new instance.
  333.      *
  334.      * @return  mixed   Results of running the $callback for the specified $key.
  335.      *
  336.      * @since   1.0
  337.      * @throws  \InvalidArgumentException
  338.      */
  339.     public function get($key$forceNew false)
  340.     {
  341.         $raw $this->getRaw($key);
  342.  
  343.         if (is_null($raw))
  344.         {
  345.             throw new \InvalidArgumentException(sprintf('Key %s has not been registered with the container.'$key));
  346.         }
  347.  
  348.         if ($raw['shared'])
  349.         {
  350.             if (!isset($this->instances[$key]|| $forceNew)
  351.             {
  352.                 $this->instances[$key$raw['callback']($this);
  353.             }
  354.  
  355.             return $this->instances[$key];
  356.         }
  357.  
  358.         return $raw['callback']($this);
  359.     }
  360.  
  361.     /**
  362.      * Get the raw data assigned to a key.
  363.      *
  364.      * @param   string  $key  The key for which to get the stored item.
  365.      *
  366.      * @return  mixed 
  367.      */
  368.     protected function getRaw($key)
  369.     {
  370.         $key $this->resolveAlias($key);
  371.  
  372.         if (isset($this->dataStore[$key]))
  373.         {
  374.             return $this->dataStore[$key];
  375.         }
  376.         elseif ($this->parent instanceof Container)
  377.         {
  378.             return $this->parent->getRaw($key);
  379.         }
  380.  
  381.         return null;
  382.     }
  383.  
  384.     /**
  385.      * Method to force the container to return a new instance
  386.      * of the results of the callback for requested $key.
  387.      *
  388.      * @param   string  $key  Name of the dataStore key to get.
  389.      *
  390.      * @return  mixed   Results of running the $callback for the specified $key.
  391.      *
  392.      * @since   1.0
  393.      */
  394.     public function getNewInstance($key)
  395.     {
  396.         return $this->get($keytrue);
  397.     }
  398.  
  399.     /**
  400.      * Register a service provider to the container.
  401.      *
  402.      * @param   ServiceProviderInterface  $provider  The service provider to register.w
  403.      *
  404.      * @return  Container  This object for chaining.
  405.      *
  406.      * @since   1.0
  407.      */
  408.     public function registerServiceProvider(ServiceProviderInterface $provider)
  409.     {
  410.         $provider->register($this);
  411.  
  412.         return $this;
  413.     }
  414. }

Documentation generated on Tue, 19 Nov 2013 14:56:48 +0100 by phpDocumentor 1.4.3