JOOMLA中国
  • Joomla中国首页
  • 社区
  • 教程
  • 应用市场
  • B计划
Joomla! Framework TM
  • Namespace
  • Class
  • Tree
  • Deprecated

Namespaces

  • Composer
    • Autoload
  • Joomla
    • Application
      • Cli
        • Output
          • Processor
      • Web
    • Data
    • DI
      • Exception
    • Event
    • Filter
    • Input
    • Ldap
    • Registry
      • Format
    • Session
      • Storage
    • String
    • Uri
    • Utilities
  • None
  • PasswordCompat
    • binary
  • PHP
  • Psr
    • Log
  • Symfony
    • Component
      • Yaml
        • Exception
    • Polyfill
      • Util

Classes

  • Container

Interfaces

  • ContainerAwareInterface
  • ServiceProviderInterface

Traits

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