classInfoFactory = new Tx_Extbase_Object_Container_ClassInfoFactory(); $this->cache = new Tx_Extbase_Object_Container_ClassInfoCache(); } /** * Returns an instance of the container singleton. * * @return Tx_Extbase_Object_Container_Container */ static public function getContainer() { if (self::$containerInstance === NULL) { self::$containerInstance = new Tx_Extbase_Object_Container_Container(); } return self::$containerInstance; } private function __clone() {} /** * gets an instance of the given class * @param string $className * @return object */ public function getInstance($className) { $givenConstructorArguments=array(); if (func_num_args() > 1) { $givenConstructorArguments = func_get_args(); array_shift($givenConstructorArguments); } $object = $this->getInstanceFromClassName($className, $givenConstructorArguments, 0); $this->processSetterInjectionRegistry(); return $object; } /** * register a classname that should be used if a dependency is required. * e.g. used to define default class for a interface * * @param string $className * @param string $alternativeClassName */ public function registerImplementation($className,$alternativeClassName) { $this->alternativeImplementation[$className] = $alternativeClassName; } /** * gets an instance of the given class * @param string $className * @param array $givenConstructorArguments */ private function getInstanceFromClassName($className, array $givenConstructorArguments=array(), $level=0) { if ($level > 30) { throw new Tx_Extbase_Object_Container_Exception_TooManyRecursionLevelsException('Too many recursion levels. This should never happen, please file a bug!' . $className, 1289386945); } if ($className === 'Tx_Extbase_Object_Container_Container') { return $this; } if (isset($this->singletonInstances[$className])) { return $this->singletonInstances[$className]; } $className = $this->getClassName($className); $classInfo = $this->getClassInfo($className); $constructorArguments = $this->getConstructorArguments($classInfo->getConstructorArguments(), $givenConstructorArguments, $level); $instance = $this->newObject($className, $constructorArguments); if ($level > 0 && !($instance instanceof t3lib_Singleton)) { throw new Tx_Extbase_Object_Exception_WrongScope('Object "' . $className . '" is of not of scope singleton, but only singleton instances can be injected into other classes.', 1289387047); } if ($classInfo->hasInjectMethods()) { $this->setterInjectionRegistry[]=array($instance, $classInfo->getInjectMethods(), $level); } if ($instance instanceof t3lib_Singleton) { $this->singletonInstances[$className] = $instance; } return $instance; } /** * returns a object of the given type, called with the constructor arguments. * For speed improvements reflection is avoided * * @param string $className * @param array $constructorArguments */ private function newObject($className, array $constructorArguments) { array_unshift($constructorArguments, $className); return call_user_func_array(array('t3lib_div', 'makeInstance'), $constructorArguments); } /** * gets array of parameter that can be used to call a constructor * * @param array $constructorArgumentInformation * @param array $givenConstructorArguments * @return array */ private function getConstructorArguments(array $constructorArgumentInformation, array $givenConstructorArguments, $level) { $parameters=array(); foreach ($constructorArgumentInformation as $argumentInformation) { $argumentName = $argumentInformation['name']; if (count($givenConstructorArguments)) { // we have a value to set $parameter = array_shift($givenConstructorArguments); } elseif (isset($argumentInformation['dependency'])) { // Inject parameter $parameter = $this->getInstanceFromClassName($argumentInformation['dependency'], array(), $level+1); } elseif (isset($argumentInformation['defaultValue'])) { // no value to set anymore, we take default value $parameter = $argumentInformation['defaultValue']; } else { throw new InvalidArgumentException('not a correct info array of constructor dependencies was passed!'); } $parameters[] = $parameter; } return $parameters; } /** * Returns the class name for a new instance, taking into account the * class-extension API. * * @param string Base class name to evaluate * @return string Final class name to instantiate with "new [classname]" */ protected function getClassName($className) { if (isset($this->alternativeImplementation[$className])) { $className = $this->alternativeImplementation[$className]; } if (substr($className, -9) === 'Interface') { $className = substr($className, 0, -9); } return $className; } /** * do inject dependecies to object $instance using the given methods * * @param object $instance * @param array $setterMethods * @param integer $level */ private function handleSetterInjection($instance, array $setterMethods, $level) { foreach ($setterMethods as $method => $dependency) { $instance->$method($this->getInstanceFromClassName($dependency, array(), $level+1)); } } /** * Gets Classinfos for the className - using the cache and the factory * * @param string $className * @return Tx_Extbase_Object_Container_ClassInfo */ private function getClassInfo($className) { // we also need to make sure that the cache is returning a vaild object // in case something went wrong with unserialization etc.. if (!$this->cache->has($className) || !is_object($this->cache->get($className))) { $this->cache->set($className, $this->classInfoFactory->buildClassInfoFromClassName($className)); } return $this->cache->get($className); } /** * does setter injection based on the data in $this->setterInjectionRegistry * Its done as long till no setters are left * * @return void */ private function processSetterInjectionRegistry() { while (count($this->setterInjectionRegistry)>0) { $currentSetterData = $this->setterInjectionRegistry; $this->setterInjectionRegistry = array(); foreach ($currentSetterData as $setterInjectionData) { $this->handleSetterInjection($setterInjectionData[0], $setterInjectionData[1], $setterInjectionData[2]); } } } }