TYPO3 API  SVNRelease
DependencyContainer.php
Go to the documentation of this file.
00001 <?php
00002 
00003 /*
00004  * This file is part of SwiftMailer.
00005  * (c) 2004-2009 Chris Corbyn
00006  *
00007  * For the full copyright and license information, please view the LICENSE
00008  * file that was distributed with this source code.
00009  */
00010 
00011 //@require 'Swift/DependencyException.php';
00012 
00013 /**
00014  * Dependency Injection container.
00015  * @package Swift
00016  * @author Chris Corbyn
00017  */
00018 class Swift_DependencyContainer
00019 {
00020 
00021   /** Constant for literal value types */
00022   const TYPE_VALUE = 0x0001;
00023 
00024   /** Constant for new instance types */
00025   const TYPE_INSTANCE = 0x0010;
00026 
00027   /** Constant for shared instance types */
00028   const TYPE_SHARED = 0x0100;
00029 
00030   /** Constant for aliases */
00031   const TYPE_ALIAS = 0x1000;
00032 
00033   /** Singleton instance */
00034   private static $_instance = null;
00035 
00036   /** The data container */
00037   private $_store = array();
00038 
00039   /** The current endpoint in the data container */
00040   private $_endPoint;
00041 
00042   /**
00043    * Constructor should not be used.
00044    * Use {@link getInstance()} instead.
00045    */
00046   public function __construct() { }
00047 
00048   /**
00049    * Returns a singleton of the DependencyContainer.
00050    * @return Swift_DependencyContainer
00051    */
00052   public static function getInstance()
00053   {
00054     if (!isset(self::$_instance))
00055     {
00056       self::$_instance = new self();
00057     }
00058     return self::$_instance;
00059   }
00060 
00061   /**
00062    * List the names of all items stored in the Container.
00063    * @return array
00064    */
00065   public function listItems()
00066   {
00067     return array_keys($this->_store);
00068   }
00069 
00070   /**
00071    * Test if an item is registered in this container with the given name.
00072    * @param string $itemName
00073    * @return boolean
00074    * @see register()
00075    */
00076   public function has($itemName)
00077   {
00078     return array_key_exists($itemName, $this->_store)
00079       && isset($this->_store[$itemName]['lookupType']);
00080   }
00081 
00082   /**
00083    * Lookup the item with the given $itemName.
00084    * @param string $itemName
00085    * @return mixed
00086    * @throws Swift_DependencyException If the dependency is not found
00087    * @see register()
00088    */
00089   public function lookup($itemName)
00090   {
00091     if (!$this->has($itemName))
00092     {
00093       throw new Swift_DependencyException(
00094         'Cannot lookup dependency "' . $itemName . '" since it is not registered.'
00095         );
00096     }
00097 
00098     switch ($this->_store[$itemName]['lookupType'])
00099     {
00100       case self::TYPE_ALIAS:
00101         return $this->_createAlias($itemName);
00102       case self::TYPE_VALUE:
00103         return $this->_getValue($itemName);
00104       case self::TYPE_INSTANCE:
00105         return $this->_createNewInstance($itemName);
00106       case self::TYPE_SHARED:
00107         return $this->_createSharedInstance($itemName);
00108     }
00109   }
00110 
00111   /**
00112    * Create an array of arguments passed to the constructor of $itemName.
00113    * @param string $itemName
00114    * @return array
00115    */
00116   public function createDependenciesFor($itemName)
00117   {
00118     $args = array();
00119     if (isset($this->_store[$itemName]['args']))
00120     {
00121       $args = $this->_resolveArgs($this->_store[$itemName]['args']);
00122     }
00123     return $args;
00124   }
00125 
00126   /**
00127    * Register a new dependency with $itemName.
00128    * This method returns the current DependencyContainer instance because it
00129    * requires the use of the fluid interface to set the specific details for the
00130    * dependency.
00131    *
00132    * @param string $itemName
00133    * @return Swift_DependencyContainer
00134    * @see asNewInstanceOf(), asSharedInstanceOf(), asValue()
00135    */
00136   public function register($itemName)
00137   {
00138     $this->_store[$itemName] = array();
00139     $this->_endPoint =& $this->_store[$itemName];
00140     return $this;
00141   }
00142 
00143   /**
00144    * Specify the previously registered item as a literal value.
00145    * {@link register()} must be called before this will work.
00146    *
00147    * @param mixed $value
00148    * @return Swift_DependencyContainer
00149    */
00150   public function asValue($value)
00151   {
00152     $endPoint =& $this->_getEndPoint();
00153     $endPoint['lookupType'] = self::TYPE_VALUE;
00154     $endPoint['value'] = $value;
00155     return $this;
00156   }
00157 
00158   /**
00159    * Specify the previously registered item as an alias of another item.
00160    * @param string $lookup
00161    * @return Swift_DependencyContainer
00162    */
00163   public function asAliasOf($lookup)
00164   {
00165     $endPoint =& $this->_getEndPoint();
00166     $endPoint['lookupType'] = self::TYPE_ALIAS;
00167     $endPoint['ref'] = $lookup;
00168     return $this;
00169   }
00170 
00171   /**
00172    * Specify the previously registered item as a new instance of $className.
00173    * {@link register()} must be called before this will work.
00174    * Any arguments can be set with {@link withDependencies()},
00175    * {@link addConstructorValue()} or {@link addConstructorLookup()}.
00176    *
00177    * @param string $className
00178    * @return Swift_DependencyContainer
00179    * @see withDependencies(), addConstructorValue(), addConstructorLookup()
00180    */
00181   public function asNewInstanceOf($className)
00182   {
00183     $endPoint =& $this->_getEndPoint();
00184     $endPoint['lookupType'] = self::TYPE_INSTANCE;
00185     $endPoint['className'] = $className;
00186     return $this;
00187   }
00188 
00189   /**
00190    * Specify the previously registered item as a shared instance of $className.
00191    * {@link register()} must be called before this will work.
00192    * @param string $className
00193    * @return Swift_DependencyContainer
00194    */
00195   public function asSharedInstanceOf($className)
00196   {
00197     $endPoint =& $this->_getEndPoint();
00198     $endPoint['lookupType'] = self::TYPE_SHARED;
00199     $endPoint['className'] = $className;
00200     return $this;
00201   }
00202 
00203   /**
00204    * Specify a list of injected dependencies for the previously registered item.
00205    * This method takes an array of lookup names.
00206    *
00207    * @param array $lookups
00208    * @return Swift_DependencyContainer
00209    * @see addConstructorValue(), addConstructorLookup()
00210    */
00211   public function withDependencies(array $lookups)
00212   {
00213     $endPoint =& $this->_getEndPoint();
00214     $endPoint['args'] = array();
00215     foreach ($lookups as $lookup)
00216     {
00217       $this->addConstructorLookup($lookup);
00218     }
00219     return $this;
00220   }
00221 
00222   /**
00223    * Specify a literal (non looked up) value for the constructor of the
00224    * previously registered item.
00225    *
00226    * @param mixed $value
00227    * @return Swift_DependencyContainer
00228    * @see withDependencies(), addConstructorLookup()
00229    */
00230   public function addConstructorValue($value)
00231   {
00232     $endPoint =& $this->_getEndPoint();
00233     if (!isset($endPoint['args']))
00234     {
00235       $endPoint['args'] = array();
00236     }
00237     $endPoint['args'][] = array('type' => 'value', 'item' => $value);
00238     return $this;
00239   }
00240 
00241   /**
00242    * Specify a dependency lookup for the constructor of the previously
00243    * registered item.
00244    *
00245    * @param string $lookup
00246    * @return Swift_DependencyContainer
00247    * @see withDependencies(), addConstructorValue()
00248    */
00249   public function addConstructorLookup($lookup)
00250   {
00251     $endPoint =& $this->_getEndPoint();
00252     if (!isset($this->_endPoint['args']))
00253     {
00254       $endPoint['args'] = array();
00255     }
00256     $endPoint['args'][] = array('type' => 'lookup', 'item' => $lookup);
00257     return $this;
00258   }
00259 
00260   // -- Private methods
00261 
00262   /** Get the literal value with $itemName */
00263   private function _getValue($itemName)
00264   {
00265     return $this->_store[$itemName]['value'];
00266   }
00267 
00268   /** Resolve an alias to another item */
00269   private function _createAlias($itemName)
00270   {
00271     return $this->lookup($this->_store[$itemName]['ref']);
00272   }
00273 
00274   /** Create a fresh instance of $itemName */
00275   private function _createNewInstance($itemName)
00276   {
00277     $reflector = new ReflectionClass($this->_store[$itemName]['className']);
00278     if ($reflector->getConstructor())
00279     {
00280       return $reflector->newInstanceArgs(
00281         $this->createDependenciesFor($itemName)
00282         );
00283     }
00284     else
00285     {
00286       return $reflector->newInstance();
00287     }
00288   }
00289 
00290   /** Create and register a shared instance of $itemName */
00291   private function _createSharedInstance($itemName)
00292   {
00293     if (!isset($this->_store[$itemName]['instance']))
00294     {
00295       $this->_store[$itemName]['instance'] = $this->_createNewInstance($itemName);
00296     }
00297     return $this->_store[$itemName]['instance'];
00298   }
00299 
00300   /** Get the current endpoint in the store */
00301   private function &_getEndPoint()
00302   {
00303     if (!isset($this->_endPoint))
00304     {
00305       throw new BadMethodCallException(
00306         'Component must first be registered by calling register()'
00307         );
00308     }
00309     return $this->_endPoint;
00310   }
00311 
00312   /** Get an argument list with dependencies resolved */
00313   private function _resolveArgs(array $args)
00314   {
00315     $resolved = array();
00316     foreach ($args as $argDefinition)
00317     {
00318       switch ($argDefinition['type'])
00319       {
00320         case 'lookup':
00321           $resolved[] = $this->_lookupRecursive($argDefinition['item']);
00322           break;
00323         case 'value':
00324           $resolved[] = $argDefinition['item'];
00325           break;
00326       }
00327     }
00328     return $resolved;
00329   }
00330 
00331   /** Resolve a single dependency with an collections */
00332   private function _lookupRecursive($item)
00333   {
00334     if (is_array($item))
00335     {
00336       $collection = array();
00337       foreach ($item as $k => $v)
00338       {
00339         $collection[$k] = $this->_lookupRecursive($v);
00340       }
00341       return $collection;
00342     }
00343     else
00344     {
00345       return $this->lookup($item);
00346     }
00347   }
00348 
00349 }