|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010 Extbase Team 00006 * All rights reserved 00007 * 00008 * This class is a backport of the corresponding class of FLOW3. 00009 * All credits go to the v5 team. 00010 * 00011 * This script is part of the TYPO3 project. The TYPO3 project is 00012 * free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. 00016 * 00017 * The GNU General Public License can be found at 00018 * http://www.gnu.org/copyleft/gpl.html. 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 /** 00029 * Internal TYPO3 Dependency Injection container 00030 * 00031 * @author Daniel Pötzinger 00032 * @author Sebastian Kurfürst 00033 */ 00034 class Tx_Extbase_Object_Container_Container implements t3lib_Singleton { 00035 00036 /** 00037 * internal cache for classinfos 00038 * 00039 * @var Tx_Extbase_Object_Container_ClassInfoCache 00040 */ 00041 private $cache; 00042 00043 /** 00044 * registered alternative implementations of a class 00045 * e.g. used to know the class for a AbstractClass or a Dependency 00046 * 00047 * @var array 00048 */ 00049 private $alternativeImplementation; 00050 00051 /** 00052 * reference to the classinfofactory, that analyses dependencys 00053 * 00054 * @var Tx_Extbase_Object_Container_ClassInfoFactory 00055 */ 00056 private $classInfoFactory; 00057 00058 /** 00059 * holds references of singletons 00060 * 00061 * @var array 00062 */ 00063 private $singletonInstances = array(); 00064 00065 /** 00066 * Array of prototype objects currently being built, to prevent recursion. 00067 * 00068 * @var array 00069 */ 00070 private $prototypeObjectsWhichAreCurrentlyInstanciated; 00071 00072 /** 00073 * Constructor is protected since container should 00074 * be a singleton. 00075 * 00076 * @see getContainer() 00077 * @param void 00078 * @return void 00079 */ 00080 public function __construct() { 00081 $this->classInfoFactory = t3lib_div::makeInstance('Tx_Extbase_Object_Container_ClassInfoFactory'); 00082 $this->cache = t3lib_div::makeInstance('Tx_Extbase_Object_Container_ClassInfoCache'); 00083 } 00084 00085 /** 00086 * Main method which should be used to get an instance of the wished class 00087 * specified by $className. 00088 * 00089 * @param string $className 00090 * @param array $givenConstructorArguments the list of constructor arguments as array 00091 * @return object the built object 00092 */ 00093 public function getInstance($className, $givenConstructorArguments = array()) { 00094 $this->prototypeObjectsWhichAreCurrentlyInstanciated = array(); 00095 return $this->getInstanceInternal($className, $givenConstructorArguments); 00096 } 00097 00098 /** 00099 * Internal implementation for getting a class. 00100 * 00101 * @param string $className 00102 * @param array $givenConstructorArguments the list of constructor arguments as array 00103 * @return object the built object 00104 */ 00105 protected function getInstanceInternal($className, $givenConstructorArguments = array()) { 00106 $className = $this->getImplementationClassName($className); 00107 00108 if ($className === 'Tx_Extbase_Object_Container_Container') { 00109 return $this; 00110 } 00111 00112 if (isset($this->singletonInstances[$className])) { 00113 if (count($givenConstructorArguments) > 0) { 00114 throw new Tx_Extbase_Object_Exception('Object "' . $className . '" fetched from singleton cache, thus, explicit constructor arguments are not allowed.', 1292857934); 00115 } 00116 return $this->singletonInstances[$className]; 00117 } 00118 00119 $classIsSingleton = $this->isSingleton($className); 00120 if (!$classIsSingleton) { 00121 if (array_search($className, $this->prototypeObjectsWhichAreCurrentlyInstanciated) !== FALSE) { 00122 throw new Tx_Extbase_Object_Exception_CannotBuildObject('Cyclic dependency in prototype object, for class "' . $className . '".', 1295611406); 00123 } 00124 $this->prototypeObjectsWhichAreCurrentlyInstanciated[] = $className; 00125 } 00126 00127 $classInfo = $this->getClassInfo($className); 00128 00129 $instance = $this->instanciateObject($className, $classInfo, $givenConstructorArguments); 00130 $this->injectDependencies($instance, $classInfo); 00131 00132 if (method_exists($instance, 'initializeObject') && is_callable(array($instance, 'initializeObject'))) { 00133 $instance->initializeObject(); 00134 } 00135 00136 if (!$classIsSingleton) { 00137 array_pop($this->prototypeObjectsWhichAreCurrentlyInstanciated); 00138 } 00139 00140 return $instance; 00141 } 00142 00143 /** 00144 * Instanciates an object, possibly setting the constructor dependencies. 00145 * Additionally, directly registers all singletons in the singleton registry, 00146 * such that circular references of singletons are correctly instanciated. 00147 * 00148 * @param string $className 00149 * @param Tx_Extbase_Object_Container_ClassInfo $classInfo 00150 * @param array $givenConstructorArguments 00151 * @return object the new instance 00152 */ 00153 protected function instanciateObject($className, Tx_Extbase_Object_Container_ClassInfo $classInfo, array $givenConstructorArguments) { 00154 $classIsSingleton = $this->isSingleton($className); 00155 00156 if ($classIsSingleton && count($givenConstructorArguments) > 0) { 00157 throw new Tx_Extbase_Object_Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051); 00158 } 00159 00160 $constructorArguments = $this->getConstructorArguments($className, $classInfo->getConstructorArguments(), $givenConstructorArguments); 00161 array_unshift($constructorArguments, $className); 00162 $instance = call_user_func_array(array('t3lib_div', 'makeInstance'), $constructorArguments); 00163 00164 if ($classIsSingleton) { 00165 $this->singletonInstances[$className] = $instance; 00166 } 00167 return $instance; 00168 } 00169 00170 /** 00171 * Inject setter-dependencies into $instance 00172 * 00173 * @param object $instance 00174 * @param Tx_Extbase_Object_Container_ClassInfo $classInfo 00175 * @return void 00176 */ 00177 protected function injectDependencies($instance, Tx_Extbase_Object_Container_ClassInfo $classInfo) { 00178 if (!$classInfo->hasInjectMethods()) return; 00179 00180 foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) { 00181 00182 $instanceToInject = $this->getInstanceInternal($classNameToInject); 00183 if ($this->isSingleton($instance) && !($instanceToInject instanceof t3lib_Singleton)) { 00184 $this->log('The singleton "' . $classInfo->getClassName() . '" needs a prototype in "' . $injectMethodName . '". This is often a bad code smell; often you rather want to inject a singleton.', 1); 00185 } 00186 00187 $instance->$injectMethodName($instanceToInject); 00188 } 00189 } 00190 00191 /** 00192 * Wrapper for dev log, in order to ease testing 00193 * 00194 * @param string Message (in english). 00195 * @param integer Severity: 0 is info, 1 is notice, 2 is warning, 3 is fatal error, -1 is "OK" message 00196 * @return void 00197 */ 00198 protected function log($message, $severity) { 00199 t3lib_div::devLog($message, 'extbase', $severity); 00200 } 00201 00202 /** 00203 * register a classname that should be used if a dependency is required. 00204 * e.g. used to define default class for a interface 00205 * 00206 * @param string $className 00207 * @param string $alternativeClassName 00208 */ 00209 public function registerImplementation($className,$alternativeClassName) { 00210 $this->alternativeImplementation[$className] = $alternativeClassName; 00211 } 00212 00213 /** 00214 * gets array of parameter that can be used to call a constructor 00215 * 00216 * @param string $className 00217 * @param array $constructorArgumentInformation 00218 * @param array $givenConstructorArguments 00219 * @return array 00220 */ 00221 private function getConstructorArguments($className, array $constructorArgumentInformation, array $givenConstructorArguments) { 00222 $parameters=array(); 00223 foreach ($constructorArgumentInformation as $argumentInformation) { 00224 $argumentName = $argumentInformation['name']; 00225 00226 // We have a dependency we can automatically wire, 00227 // AND the class has NOT been explicitely passed in 00228 if (isset($argumentInformation['dependency']) && !(count($givenConstructorArguments) && is_a($givenConstructorArguments[0], $argumentInformation['dependency']))) { 00229 // Inject parameter 00230 $parameter = $this->getInstanceInternal($argumentInformation['dependency']); 00231 if ($this->isSingleton($className) && !($parameter instanceof t3lib_Singleton)) { 00232 $this->log('The singleton "' . $className . '" needs a prototype in the constructor. This is often a bad code smell; often you rather want to inject a singleton.', 1); 00233 } 00234 } elseif (count($givenConstructorArguments)) { 00235 // EITHER: 00236 // No dependency injectable anymore, but we still have 00237 // an explicit constructor argument 00238 // OR: 00239 // the passed constructor argument matches the type for the dependency 00240 // injection, and thus the passed constructor takes precendence over 00241 // autowiring. 00242 $parameter = array_shift($givenConstructorArguments); 00243 } elseif (array_key_exists('defaultValue', $argumentInformation)) { 00244 // no value to set anymore, we take default value 00245 $parameter = $argumentInformation['defaultValue']; 00246 } else { 00247 throw new InvalidArgumentException('not a correct info array of constructor dependencies was passed!'); 00248 } 00249 $parameters[] = $parameter; 00250 } 00251 return $parameters; 00252 } 00253 00254 /** 00255 * @param string/object $object 00256 * @return boolean TRUE if the object is a singleton, FALSE if it is a prototype. 00257 */ 00258 protected function isSingleton($object) { 00259 return in_array('t3lib_Singleton', class_implements($object)); 00260 } 00261 /** 00262 * Returns the class name for a new instance, taking into account the 00263 * class-extension API. 00264 * 00265 * @param string Base class name to evaluate 00266 * @return string Final class name to instantiate with "new [classname]" 00267 */ 00268 protected function getImplementationClassName($className) { 00269 if (isset($this->alternativeImplementation[$className])) { 00270 $className = $this->alternativeImplementation[$className]; 00271 } 00272 00273 if (substr($className, -9) === 'Interface') { 00274 $className = substr($className, 0, -9); 00275 } 00276 00277 return $className; 00278 } 00279 00280 /** 00281 * Gets Classinfos for the className - using the cache and the factory 00282 * 00283 * @param string $className 00284 * @return Tx_Extbase_Object_Container_ClassInfo 00285 */ 00286 private function getClassInfo($className) { 00287 // we also need to make sure that the cache is returning a vaild object 00288 // in case something went wrong with unserialization etc.. 00289 if (!$this->cache->has($className) || !is_object($this->cache->get($className))) { 00290 $this->cache->set($className, $this->classInfoFactory->buildClassInfoFromClassName($className)); 00291 } 00292 return $this->cache->get($className); 00293 } 00294 }
1.8.0