TYPO3 CMS  TYPO3_8-7
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 
222  protected function injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
223  {
224  if (!$classInfo->hasInjectMethods() && !$classInfo->hasInjectProperties()) {
225  return;
226  }
227  foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) {
228  $instanceToInject = $this->getInstanceInternal($classNameToInject);
229  if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) {
230  $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);
231  }
232  if (is_callable([$instance, $injectMethodName])) {
233  $instance->{$injectMethodName}($instanceToInject);
234  }
235  }
236  foreach ($classInfo->getInjectProperties() as $injectPropertyName => $classNameToInject) {
237  $instanceToInject = $this->getInstanceInternal($classNameToInject);
238  if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) {
239  $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);
240  }
241  $propertyReflection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Reflection\PropertyReflection::class, $instance, $injectPropertyName);
242 
243  $propertyReflection->setAccessible(true);
244  $propertyReflection->setValue($instance, $instanceToInject);
245  }
246  }
247 
254  protected function initializeObject($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
255  {
256  if ($classInfo->getIsInitializeable() && is_callable([$instance, 'initializeObject'])) {
257  $instance->initializeObject();
258  }
259  }
260 
267  protected function log($message, $severity)
268  {
269  \TYPO3\CMS\Core\Utility\GeneralUtility::devLog($message, 'extbase', $severity);
270  }
271 
279  public function registerImplementation($className, $alternativeClassName)
280  {
281  $this->alternativeImplementation[$className] = $alternativeClassName;
282  }
283 
293  private function getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
294  {
295  $parameters = [];
296  $constructorArgumentInformation = $classInfo->getConstructorArguments();
297  foreach ($constructorArgumentInformation as $index => $argumentInformation) {
298  // Constructor argument given AND argument is a simple type OR instance of argument type
299  if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation['dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation['dependency']))) {
300  $parameter = $givenConstructorArguments[$index];
301  } else {
302  if (isset($argumentInformation['dependency']) && !array_key_exists('defaultValue', $argumentInformation)) {
303  $parameter = $this->getInstanceInternal($argumentInformation['dependency']);
304  if ($classInfo->getIsSingleton() && !$parameter instanceof \TYPO3\CMS\Core\SingletonInterface) {
305  $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);
306  }
307  } elseif (array_key_exists('defaultValue', $argumentInformation)) {
308  $parameter = $argumentInformation['defaultValue'];
309  } else {
310  throw new \InvalidArgumentException('not a correct info array of constructor dependencies was passed!', 1476107941);
311  }
312  }
313  $parameters[] = $parameter;
314  }
315  return $parameters;
316  }
317 
325  public function getImplementationClassName($className)
326  {
327  if (isset($this->alternativeImplementation[$className])) {
328  $className = $this->alternativeImplementation[$className];
329  }
330  if (substr($className, -9) === 'Interface') {
331  $className = substr($className, 0, -9);
332  }
333  return $className;
334  }
335 
342  private function getClassInfo($className)
343  {
344  $classNameHash = md5($className);
345  $classInfo = $this->getClassInfoCache()->get($classNameHash);
346  if (!$classInfo instanceof \TYPO3\CMS\Extbase\Object\Container\ClassInfo) {
347  $classInfo = $this->getClassInfoFactory()->buildClassInfoFromClassName($className);
348  $this->getClassInfoCache()->set($classNameHash, $classInfo);
349  }
350  return $classInfo;
351  }
352 
358  public function isSingleton($className)
359  {
360  return $this->getClassInfo($className)->getIsSingleton();
361  }
362 
368  public function isPrototype($className)
369  {
370  return !$this->isSingleton($className);
371  }
372 }
static devLog($msg, $extKey, $severity=0, $dataVar=false)
getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments)
Definition: Container.php:293
registerImplementation($className, $alternativeClassName)
Definition: Container.php:279
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:254
static makeInstance($className,... $constructorArguments)
injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo)
Definition: Container.php:222
getInstance($className, $givenConstructorArguments=[])
Definition: Container.php:123