TYPO3 CMS  TYPO3_7-6
Container.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 {
22  const SCOPE_PROTOTYPE = 1;
23  const SCOPE_SINGLETON = 2;
24 
30  private $cache = null;
31 
39 
45  private $classInfoFactory = null;
46 
50  protected $instantiator = null;
51 
57  private $singletonInstances = [];
58 
65 
72  public function __construct()
73  {
74  }
75 
81  protected function getClassInfoFactory()
82  {
83  if ($this->classInfoFactory == null) {
84  $this->classInfoFactory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory::class);
85  }
87  }
88 
94  protected function getClassInfoCache()
95  {
96  if ($this->cache == null) {
97  $this->cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\ClassInfoCache::class);
98  }
99  return $this->cache;
100  }
101 
107  protected function getInstantiator()
108  {
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 = [])
124  {
125  $this->prototypeObjectsWhichAreCurrentlyInstanciated = [];
126  return $this->getInstanceInternal($className, $givenConstructorArguments);
127  }
128 
135  public function getEmptyObject($className)
136  {
137  $className = $this->getImplementationClassName($className);
138  $classInfo = $this->getClassInfo($className);
139  $object = $this->getInstantiator()->instantiate($className);
140  $this->injectDependencies($object, $classInfo);
141  $this->initializeObject($object, $classInfo);
142  return $object;
143  }
144 
154  protected function getInstanceInternal($className, $givenConstructorArguments = [])
155  {
156  $className = $this->getImplementationClassName($className);
157  if ($className === \TYPO3\CMS\Extbase\Object\Container\Container::class) {
158  return $this;
159  }
160  if ($className === \TYPO3\CMS\Core\Cache\CacheManager::class) {
161  return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class);
162  }
163  if ($className === \TYPO3\CMS\Core\Package\PackageManager::class) {
164  return \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Package\PackageManager::class);
165  }
167  if (isset($this->singletonInstances[$className])) {
168  if (!empty($givenConstructorArguments)) {
169  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" fetched from singleton cache, thus, explicit constructor arguments are not allowed.', 1292857934);
170  }
171  return $this->singletonInstances[$className];
172  }
173  $classInfo = $this->getClassInfo($className);
174  $classIsSingleton = $classInfo->getIsSingleton();
175  if (!$classIsSingleton) {
176  if (array_key_exists($className, $this->prototypeObjectsWhichAreCurrentlyInstanciated) !== false) {
177  throw new \TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException('Cyclic dependency in prototype object, for class "' . $className . '".', 1295611406);
178  }
179  $this->prototypeObjectsWhichAreCurrentlyInstanciated[$className] = true;
180  }
181  $instance = $this->instanciateObject($classInfo, $givenConstructorArguments);
182  $this->injectDependencies($instance, $classInfo);
183  $this->initializeObject($instance, $classInfo);
184  if (!$classIsSingleton) {
185  unset($this->prototypeObjectsWhichAreCurrentlyInstanciated[$className]);
186  }
187  return $instance;
188  }
189 
200  protected function instanciateObject(\TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
201  {
202  $className = $classInfo->getClassName();
203  $classIsSingleton = $classInfo->getIsSingleton();
204  if ($classIsSingleton && !empty($givenConstructorArguments)) {
205  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051);
206  }
207  $constructorArguments = $this->getConstructorArguments($className, $classInfo, $givenConstructorArguments);
208  array_unshift($constructorArguments, $className);
209  $instance = call_user_func_array([\TYPO3\CMS\Core\Utility\GeneralUtility::class, 'makeInstance'], $constructorArguments);
210  if ($classIsSingleton) {
211  $this->singletonInstances[$className] = $instance;
212  }
213  return $instance;
214  }
215 
223  protected function injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
224  {
225  if (!$classInfo->hasInjectMethods() && !$classInfo->hasInjectProperties()) {
226  return;
227  }
228  foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) {
229  $instanceToInject = $this->getInstanceInternal($classNameToInject);
230  if (
231  $classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface
232  && strpos($classNameToInject, 'TYPO3\CMS') === false
233  ) {
234  $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);
235  }
236  if (is_callable([$instance, $injectMethodName])) {
237  $instance->{$injectMethodName}($instanceToInject);
238  }
239  }
240  foreach ($classInfo->getInjectProperties() as $injectPropertyName => $classNameToInject) {
241  $instanceToInject = $this->getInstanceInternal($classNameToInject);
242  if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) {
243  $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);
244  }
245  $propertyReflection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Reflection\PropertyReflection::class, $instance, $injectPropertyName);
246 
247  $propertyReflection->setAccessible(true);
248  $propertyReflection->setValue($instance, $instanceToInject);
249  }
250  }
251 
258  protected function initializeObject($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
259  {
260  if ($classInfo->getIsInitializeable() && is_callable([$instance, 'initializeObject'])) {
261  $instance->initializeObject();
262  }
263  }
264 
272  protected function log($message, $severity)
273  {
274  \TYPO3\CMS\Core\Utility\GeneralUtility::devLog($message, 'extbase', $severity);
275  }
276 
284  public function registerImplementation($className, $alternativeClassName)
285  {
286  $this->alternativeImplementation[$className] = $alternativeClassName;
287  }
288 
298  private function getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
299  {
300  $parameters = [];
301  $constructorArgumentInformation = $classInfo->getConstructorArguments();
302  foreach ($constructorArgumentInformation as $index => $argumentInformation) {
303  // Constructor argument given AND argument is a simple type OR instance of argument type
304  if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation['dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation['dependency']))) {
305  $parameter = $givenConstructorArguments[$index];
306  } else {
307  if (isset($argumentInformation['dependency']) && !array_key_exists('defaultValue', $argumentInformation)) {
308  $parameter = $this->getInstanceInternal($argumentInformation['dependency']);
309  if ($classInfo->getIsSingleton() && !$parameter instanceof \TYPO3\CMS\Core\SingletonInterface) {
310  $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);
311  }
312  } elseif (array_key_exists('defaultValue', $argumentInformation)) {
313  $parameter = $argumentInformation['defaultValue'];
314  } else {
315  throw new \InvalidArgumentException('not a correct info array of constructor dependencies was passed!');
316  }
317  }
318  $parameters[] = $parameter;
319  }
320  return $parameters;
321  }
322 
330  public function getImplementationClassName($className)
331  {
332  if (isset($this->alternativeImplementation[$className])) {
333  $className = $this->alternativeImplementation[$className];
334  }
335  if (substr($className, -9) === 'Interface') {
336  $className = substr($className, 0, -9);
337  }
338  return $className;
339  }
340 
347  private function getClassInfo($className)
348  {
349  $classNameHash = md5($className);
350  $classInfo = $this->getClassInfoCache()->get($classNameHash);
351  if (!$classInfo instanceof \TYPO3\CMS\Extbase\Object\Container\ClassInfo) {
352  $classInfo = $this->getClassInfoFactory()->buildClassInfoFromClassName($className);
353  $this->getClassInfoCache()->set($classNameHash, $classInfo);
354  }
355  return $classInfo;
356  }
357 
363  public function isSingleton($className)
364  {
365  return $this->getClassInfo($className)->getIsSingleton();
366  }
367 
373  public function isPrototype($className)
374  {
375  return !$this->isSingleton($className);
376  }
377 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
Definition: Container.php:298
registerImplementation($className, $alternativeClassName)
Definition: Container.php:284
instanciateObject(\TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
Definition: Container.php:200
getInstanceInternal($className, $givenConstructorArguments=[])
Definition: Container.php:154
initializeObject($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
Definition: Container.php:258
injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
Definition: Container.php:223
getInstance($className, $givenConstructorArguments=[])
Definition: Container.php:123