TYPO3 CMS  TYPO3_6-2
Container.php
Go to the documentation of this file.
1 <?php
3 
25 
26  const SCOPE_PROTOTYPE = 1;
27  const SCOPE_SINGLETON = 2;
28 
34  private $cache = NULL;
35 
43 
49  private $classInfoFactory = NULL;
50 
54  protected $instantiator = NULL;
55 
61  private $singletonInstances = array();
62 
69 
76  public function __construct() {
77  }
78 
84  protected function getClassInfoFactory() {
85  if ($this->classInfoFactory == NULL) {
86  $this->classInfoFactory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\Container\\ClassInfoFactory');
87  }
89  }
90 
96  protected function getClassInfoCache() {
97  if ($this->cache == NULL) {
98  $this->cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\Container\\ClassInfoCache');
99  }
100  return $this->cache;
101  }
102 
108  protected function getInstantiator() {
109  if ($this->instantiator == NULL) {
110  $this->instantiator = new \Doctrine\Instantiator\Instantiator();
111  }
112  return $this->instantiator;
113  }
114 
123  public function getInstance($className, $givenConstructorArguments = array()) {
124  $this->prototypeObjectsWhichAreCurrentlyInstanciated = array();
125  return $this->getInstanceInternal($className, $givenConstructorArguments);
126  }
127 
134  public function getEmptyObject($className) {
135  $className = $this->getImplementationClassName($className);
136  $classInfo = $this->getClassInfo($className);
137  $object = $this->getInstantiator()->instantiate($className);
138  if ($classInfo->getIsInitializeable() && is_callable(array($object, 'initializeObject'))) {
139  $object->initializeObject();
140  }
141  $this->injectDependencies($object, $classInfo);
142  return $object;
143  }
144 
154  protected function getInstanceInternal($className, $givenConstructorArguments = array()) {
155  // Never instantiate with a beginning backslash, otherwise things like singletons won't work.
156  if ($className[0] === '\\') {
157  $className = substr($className, 1);
158  }
159  $className = $this->getImplementationClassName($className);
160  if ($className === 'TYPO3\\CMS\\Extbase\\Object\\Container\\Container') {
161  return $this;
162  }
163  if ($className === 'TYPO3\\CMS\\Core\\Cache\\CacheManager') {
164  return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Cache\\CacheManager');
165  }
166  if ($className === 'TYPO3\\CMS\\Core\\Package\\PackageManager') {
167  return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Package\\PackageManager');
168  }
170  if (isset($this->singletonInstances[$className])) {
171  if (count($givenConstructorArguments) > 0) {
172  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" fetched from singleton cache, thus, explicit constructor arguments are not allowed.', 1292857934);
173  }
174  return $this->singletonInstances[$className];
175  }
176  $classInfo = $this->getClassInfo($className);
177  $classIsSingleton = $classInfo->getIsSingleton();
178  if (!$classIsSingleton) {
179  if (array_key_exists($className, $this->prototypeObjectsWhichAreCurrentlyInstanciated) !== FALSE) {
180  throw new \TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException('Cyclic dependency in prototype object, for class "' . $className . '".', 1295611406);
181  }
182  $this->prototypeObjectsWhichAreCurrentlyInstanciated[$className] = TRUE;
183  }
184  $instance = $this->instanciateObject($classInfo, $givenConstructorArguments);
185  $this->injectDependencies($instance, $classInfo);
186  if ($classInfo->getIsInitializeable() && is_callable(array($instance, 'initializeObject'))) {
187  $instance->initializeObject();
188  }
189  if (!$classIsSingleton) {
190  unset($this->prototypeObjectsWhichAreCurrentlyInstanciated[$className]);
191  }
192  return $instance;
193  }
194 
205  protected function instanciateObject(\TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments) {
206  $className = $classInfo->getClassName();
207  $classIsSingleton = $classInfo->getIsSingleton();
208  if ($classIsSingleton && count($givenConstructorArguments) > 0) {
209  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051);
210  }
211  $constructorArguments = $this->getConstructorArguments($className, $classInfo, $givenConstructorArguments);
212  array_unshift($constructorArguments, $className);
213  $instance = call_user_func_array(array('TYPO3\\CMS\\Core\\Utility\\GeneralUtility', 'makeInstance'), $constructorArguments);
214  if ($classIsSingleton) {
215  $this->singletonInstances[$className] = $instance;
216  }
217  return $instance;
218  }
219 
227  protected function injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo) {
228  if (!$classInfo->hasInjectMethods() && !$classInfo->hasInjectProperties()) {
229  return;
230  }
231  foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) {
232  $instanceToInject = $this->getInstanceInternal($classNameToInject);
233  if (
234  $classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface
235  && strpos($classNameToInject, 'TYPO3\\CMS') === FALSE
236  ) {
237  $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);
238  }
239  if (is_callable(array($instance, $injectMethodName))) {
240  $instance->{$injectMethodName}($instanceToInject);
241  }
242  }
243  foreach ($classInfo->getInjectProperties() as $injectPropertyName => $classNameToInject) {
244  $instanceToInject = $this->getInstanceInternal($classNameToInject);
245  if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) {
246  $this->log('The singleton "' . $classInfo->getClassName() . '" needs a prototype in "' . $injectPropertyName . '". This is often a bad code smell; often you rather want to inject a singleton.', 1);
247  }
248  $propertyReflection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Reflection\\PropertyReflection', $instance, $injectPropertyName);
249 
250  $propertyReflection->setAccessible(TRUE);
251  $propertyReflection->setValue($instance, $instanceToInject);
252  }
253  }
254 
262  protected function log($message, $severity) {
263  \TYPO3\CMS\Core\Utility\GeneralUtility::devLog($message, 'extbase', $severity);
264  }
265 
273  public function registerImplementation($className, $alternativeClassName) {
274  $this->alternativeImplementation[$className] = $alternativeClassName;
275  }
276 
286  private function getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments) {
287  $parameters = array();
288  $constructorArgumentInformation = $classInfo->getConstructorArguments();
289  foreach ($constructorArgumentInformation as $index => $argumentInformation) {
290  // Constructor argument given AND argument is a simple type OR instance of argument type
291  if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation['dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation['dependency']))) {
292  $parameter = $givenConstructorArguments[$index];
293  } else {
294  if (isset($argumentInformation['dependency']) && !array_key_exists('defaultValue', $argumentInformation)) {
295  $parameter = $this->getInstanceInternal($argumentInformation['dependency']);
296  if ($classInfo->getIsSingleton() && !$parameter instanceof \TYPO3\CMS\Core\SingletonInterface) {
297  $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);
298  }
299  } elseif (array_key_exists('defaultValue', $argumentInformation)) {
300  $parameter = $argumentInformation['defaultValue'];
301  } else {
302  throw new \InvalidArgumentException('not a correct info array of constructor dependencies was passed!');
303  }
304  }
305  $parameters[] = $parameter;
306  }
307  return $parameters;
308  }
309 
317  public function getImplementationClassName($className) {
318  if (isset($this->alternativeImplementation[$className])) {
319  $className = $this->alternativeImplementation[$className];
320  }
321  if (substr($className, -9) === 'Interface') {
322  $className = substr($className, 0, -9);
323  }
324  return $className;
325  }
326 
333  private function getClassInfo($className) {
334  $classNameHash = md5($className);
335  $classInfo = $this->getClassInfoCache()->get($classNameHash);
336  if (!$classInfo instanceof \TYPO3\CMS\Extbase\Object\Container\ClassInfo) {
337  $classInfo = $this->getClassInfoFactory()->buildClassInfoFromClassName($className);
338  $this->getClassInfoCache()->set($classNameHash, $classInfo);
339  }
340  return $classInfo;
341  }
342 
348  public function isSingleton($className) {
349  return $this->getClassInfo($className)->getIsSingleton();
350  }
351 
357  public function isPrototype($className) {
358  return !$this->isSingleton($className);
359  }
360 }
getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
Definition: Container.php:286
getInstanceInternal($className, $givenConstructorArguments=array())
Definition: Container.php:154
registerImplementation($className, $alternativeClassName)
Definition: Container.php:273
$parameters
Definition: FileDumpEID.php:15
static devLog($msg, $extKey, $severity=0, $dataVar=FALSE)
instanciateObject(\TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
Definition: Container.php:205
getInstance($className, $givenConstructorArguments=array())
Definition: Container.php:123
injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
Definition: Container.php:227