‪TYPO3CMS  9.5
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 
17 use Psr\Log\LoggerInterface;
23 
30 {
31  const ‪SCOPE_PROTOTYPE = 1;
32  const ‪SCOPE_SINGLETON = 2;
33 
41 
45  protected ‪$instantiator;
46 
52  private ‪$singletonInstances = [];
53 
60 
64  private ‪$reflectionService;
65 
71  protected function ‪getInstantiator()
72  {
73  if ($this->instantiator == null) {
74  $this->instantiator = new \Doctrine\Instantiator\Instantiator();
75  }
77  }
78 
88  public function ‪getInstance($className, $givenConstructorArguments = [])
89  {
90  $this->prototypeObjectsWhichAreCurrentlyInstanciated = [];
91  return $this->‪getInstanceInternal($className, ...$givenConstructorArguments);
92  }
93 
100  public function ‪getEmptyObject($className)
101  {
102  $className = $this->‪getImplementationClassName($className);
103  $classSchema = $this->‪getReflectionService()->‪getClassSchema($className);
104  $object = $this->‪getInstantiator()->instantiate($className);
105  $this->‪injectDependencies($object, $classSchema);
106  $this->‪initializeObject($object);
107  return $object;
108  }
109 
119  protected function ‪getInstanceInternal($className, ...$givenConstructorArguments)
120  {
121  $className = $this->‪getImplementationClassName($className);
122  if ($className === \‪TYPO3\CMS\‪Extbase\Object\Container\Container::class) {
123  return $this;
124  }
125  if ($className === \‪TYPO3\CMS\Core\Cache\CacheManager::class) {
126  return GeneralUtility::makeInstance(\‪TYPO3\CMS\Core\Cache\CacheManager::class);
127  }
128  if ($className === \‪TYPO3\CMS\Core\Package\PackageManager::class) {
129  return GeneralUtility::makeInstance(\‪TYPO3\CMS\Core\Package\PackageManager::class);
130  }
132  if (isset($this->singletonInstances[$className])) {
133  if (!empty($givenConstructorArguments)) {
134  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" fetched from singleton cache, thus, explicit constructor arguments are not allowed.', 1292857934);
135  }
136  return $this->singletonInstances[$className];
137  }
138 
139  $classSchema = $this->‪getReflectionService()->‪getClassSchema($className);
140  $classIsSingleton = $classSchema->‪isSingleton();
141  if (!$classIsSingleton) {
142  if (array_key_exists($className, $this->prototypeObjectsWhichAreCurrentlyInstanciated) !== false) {
143  throw new \TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException('Cyclic dependency in prototype object, for class "' . $className . '".', 1295611406);
144  }
145  $this->prototypeObjectsWhichAreCurrentlyInstanciated[$className] = true;
146  }
147  $instance = $this->‪instanciateObject($classSchema, ...$givenConstructorArguments);
148  $this->‪injectDependencies($instance, $classSchema);
149  $this->‪initializeObject($instance);
150  if (!$classIsSingleton) {
151  unset($this->prototypeObjectsWhichAreCurrentlyInstanciated[$className]);
152  }
153  return $instance;
154  }
155 
166  protected function ‪instanciateObject(ClassSchema $classSchema, ...$givenConstructorArguments)
167  {
168  $className = $classSchema->getClassName();
169  $classIsSingleton = $classSchema->isSingleton();
170  if ($classIsSingleton && !empty($givenConstructorArguments)) {
171  throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051);
172  }
173  $constructorArguments = $this->‪getConstructorArguments($className, $classSchema, $givenConstructorArguments);
174  $instance = GeneralUtility::makeInstance($className, ...$constructorArguments);
175  if ($classIsSingleton) {
176  $this->singletonInstances[$className] = $instance;
177  }
178  return $instance;
179  }
180 
187  protected function ‪injectDependencies($instance, ClassSchema $classSchema)
188  {
189  if (!$classSchema->hasInjectMethods() && !$classSchema->hasInjectProperties()) {
190  return;
191  }
192  foreach ($classSchema->getInjectMethods() as $injectMethodName => $classNameToInject) {
193  $instanceToInject = $this->‪getInstanceInternal($classNameToInject);
194  if ($classSchema->isSingleton() && !$instanceToInject instanceof \‪TYPO3\CMS\Core\SingletonInterface) {
195  $this->‪getLogger()->notice('The singleton "' . $classSchema->getClassName() . '" needs a prototype in "' . $injectMethodName . '". This is often a bad code smell; often you rather want to inject a singleton.');
196  }
197  if (is_callable([$instance, $injectMethodName])) {
198  $instance->{$injectMethodName}($instanceToInject);
199  }
200  }
201  foreach ($classSchema->getInjectProperties() as $injectPropertyName => $classNameToInject) {
202  $instanceToInject = $this->‪getInstanceInternal($classNameToInject);
203  if ($classSchema->isSingleton() && !$instanceToInject instanceof \‪TYPO3\CMS\Core\SingletonInterface) {
204  $this->‪getLogger()->notice('The singleton "' . $classSchema->getClassName() . '" needs a prototype in "' . $injectPropertyName . '". This is often a bad code smell; often you rather want to inject a singleton.');
205  }
206 
207  if ($classSchema->getProperty($injectPropertyName)['public']) {
208  $instance->{$injectPropertyName} = $instanceToInject;
209  } else {
210  $propertyReflection = new \ReflectionProperty($instance, $injectPropertyName);
211  $propertyReflection->setAccessible(true);
212  $propertyReflection->setValue($instance, $instanceToInject);
213  }
214  }
215  }
216 
222  protected function initializeObject($instance)
223  {
224  if (method_exists($instance, 'initializeObject') && is_callable([$instance, 'initializeObject'])) {
225  $instance->initializeObject();
226  }
227  }
228 
236  public function registerImplementation($className, $alternativeClassName)
237  {
238  $this->alternativeImplementation[$className] = $alternativeClassName;
239  }
240 
250  private function getConstructorArguments($className, ClassSchema $classSchema, array $givenConstructorArguments)
251  {
252  $parameters = [];
253  $constructorArgumentInformation = $classSchema->getConstructorArguments();
254  foreach ($constructorArgumentInformation as $constructorArgumentName => $argumentInformation) {
255  $index = $argumentInformation['position'];
256 
257  // Constructor argument given AND argument is a simple type OR instance of argument type
258  if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation['dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation['dependency']))) {
259  $parameter = $givenConstructorArguments[$index];
260  } else {
261  if (isset($argumentInformation['dependency']) && $argumentInformation['hasDefaultValue'] === false) {
262  $parameter = $this->getInstanceInternal($argumentInformation['dependency']);
263  if ($classSchema->isSingleton() && !$parameter instanceof \‪TYPO3\CMS\Core\SingletonInterface) {
264  $this->getLogger()->notice('The singleton "' . $className . '" needs a prototype in the constructor. This is often a bad code smell; often you rather want to inject a singleton.');
265  }
266  } elseif ($argumentInformation['hasDefaultValue'] === true) {
267  $parameter = $argumentInformation['defaultValue'];
268  } else {
269  throw new \InvalidArgumentException('not a correct info array of constructor dependencies was passed!', 1476107941);
270  }
271  }
272  $parameters[] = $parameter;
273  }
274  return $parameters;
275  }
276 
284  public function getImplementationClassName($className)
285  {
286  if (isset($this->alternativeImplementation[$className])) {
287  $className = $this->alternativeImplementation[$className];
288  }
289  if (substr($className, -9) === 'Interface') {
290  $className = substr($className, 0, -9);
291  }
292  return $className;
293  }
294 
300  public function isSingleton($className)
301  {
302  return $this->getReflectionService()->getClassSchema($className)->isSingleton();
303  }
304 
310  public function isPrototype($className)
311  {
312  return !$this->isSingleton($className);
313  }
314 
318  protected function getLogger()
319  {
320  return GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
321  }
322 
331  protected function getReflectionService(): ReflectionService
332  {
333  return $this->reflectionService ?? ($this->reflectionService = GeneralUtility::makeInstance(ReflectionService::class, GeneralUtility::makeInstance(CacheManager::class)));
334  }
335 }
‪TYPO3\CMS\Extbase\Object\Container\Container\$reflectionService
‪ReflectionService $reflectionService
Definition: Container.php:59
‪TYPO3\CMS\Extbase\Object\Container\Container\$singletonInstances
‪array $singletonInstances
Definition: Container.php:49
‪TYPO3\CMS\Extbase\Annotation
Definition: IgnoreValidation.php:4
‪TYPO3\CMS\Extbase\Object\Container\Container\getLogger
‪LoggerInterface getLogger()
Definition: Container.php:313
‪TYPO3\CMS\Extbase\Object\Container\Container
Definition: Container.php:30
‪TYPO3
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\isSingleton
‪bool isSingleton()
Definition: ClassSchema.php:857
‪TYPO3\CMS\Extbase\Object\Container
Definition: Container.php:2
‪TYPO3\CMS\Extbase\Object\Container\Container\isSingleton
‪bool isSingleton($className)
Definition: Container.php:295
‪TYPO3\CMS\Extbase\Object\Container\Container\getReflectionService
‪ReflectionService getReflectionService()
Definition: Container.php:326
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\hasInjectMethods
‪bool hasInjectMethods()
Definition: ClassSchema.php:890
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getInjectMethods
‪array getInjectMethods()
Definition: ClassSchema.php:898
‪TYPO3\CMS\Extbase\Reflection\ReflectionService\getClassSchema
‪ClassSchema getClassSchema($classNameOrObject)
Definition: ReflectionService.php:162
‪TYPO3\CMS\Extbase\Object\Container\Container\getInstantiator
‪Doctrine Instantiator InstantiatorInterface getInstantiator()
Definition: Container.php:66
‪TYPO3\CMS\Extbase\Object\Container\Container\initializeObject
‪initializeObject($instance)
Definition: Container.php:217
‪TYPO3\CMS\Extbase\Object\Container\Container\$instantiator
‪Doctrine Instantiator InstantiatorInterface $instantiator
Definition: Container.php:43
‪TYPO3\CMS\Extbase\Object\Container\Container\getEmptyObject
‪object &T getEmptyObject($className)
Definition: Container.php:95
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:27
‪TYPO3\CMS\Core\Core\ClassLoadingInformation\getClassNameForAlias
‪static mixed getClassNameForAlias($alias)
Definition: ClassLoadingInformation.php:180
‪TYPO3\CMS\Extbase\Object\Container\Container\$prototypeObjectsWhichAreCurrentlyInstanciated
‪array $prototypeObjectsWhichAreCurrentlyInstanciated
Definition: Container.php:55
‪TYPO3\CMS\Extbase\Object\Container\Container\getConstructorArguments
‪array getConstructorArguments($className, ClassSchema $classSchema, array $givenConstructorArguments)
Definition: Container.php:245
‪TYPO3\CMS\Extbase\Object\Container\Container\instanciateObject
‪object instanciateObject(ClassSchema $classSchema,... $givenConstructorArguments)
Definition: Container.php:161
‪TYPO3\CMS\Extbase\Object\Container\Container\$alternativeImplementation
‪array $alternativeImplementation
Definition: Container.php:39
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:34
‪TYPO3\CMS\Extbase\Object\Container\Container\SCOPE_PROTOTYPE
‪const SCOPE_PROTOTYPE
Definition: Container.php:31
‪TYPO3\CMS\Extbase\Object\Container\Container\SCOPE_SINGLETON
‪const SCOPE_SINGLETON
Definition: Container.php:32
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getConstructorArguments
‪array getConstructorArguments()
Definition: ClassSchema.php:924
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\hasInjectProperties
‪bool hasInjectProperties()
Definition: ClassSchema.php:882
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:25
‪TYPO3\CMS\Extbase\Object\Container\Container\isPrototype
‪bool isPrototype($className)
Definition: Container.php:305
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getClassName
‪string getClassName()
Definition: ClassSchema.php:588
‪TYPO3\CMS\Extbase\Object\Container\Container\getInstance
‪object &T getInstance($className, $givenConstructorArguments=[])
Definition: Container.php:83
‪TYPO3\CMS\Extbase\Reflection\ClassSchema
Definition: ClassSchema.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Extbase\Object\Container\Container\getInstanceInternal
‪object &T getInstanceInternal($className,... $givenConstructorArguments)
Definition: Container.php:114
‪TYPO3\CMS\Extbase\Object\Container\Container\registerImplementation
‪registerImplementation($className, $alternativeClassName)
Definition: Container.php:231
‪TYPO3\CMS\Extbase\Object\Container\Container\injectDependencies
‪injectDependencies($instance, ClassSchema $classSchema)
Definition: Container.php:182
‪TYPO3\CMS\Extbase\Object\Container\Container\getImplementationClassName
‪string getImplementationClassName($className)
Definition: Container.php:279