17 use Psr\Log\LoggerInterface;
73 if ($this->instantiator ==
null) {
74 $this->instantiator = new \Doctrine\Instantiator\Instantiator();
88 public function getInstance($className, $givenConstructorArguments = [])
90 $this->prototypeObjectsWhichAreCurrentlyInstanciated = [];
122 if ($className === \
TYPO3\CMS\
Extbase\Object\Container\Container::class) {
125 if ($className === \
TYPO3\CMS\Core\Cache\CacheManager::class) {
126 return GeneralUtility::makeInstance(\
TYPO3\CMS\Core\Cache\CacheManager::class);
128 if ($className === \
TYPO3\CMS\Core\Package\PackageManager::class) {
129 return GeneralUtility::makeInstance(\
TYPO3\CMS\Core\Package\PackageManager::class);
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);
136 return $this->singletonInstances[$className];
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);
145 $this->prototypeObjectsWhichAreCurrentlyInstanciated[$className] =
true;
150 if (!$classIsSingleton) {
151 unset($this->prototypeObjectsWhichAreCurrentlyInstanciated[$className]);
166 protected function instanciateObject(ClassSchema $classSchema, ...$givenConstructorArguments)
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);
174 $instance = GeneralUtility::makeInstance($className, ...$constructorArguments);
175 if ($classIsSingleton) {
176 $this->singletonInstances[$className] = $instance;
189 if (!$classSchema->hasInjectMethods() && !$classSchema->hasInjectProperties()) {
192 foreach ($classSchema->getInjectMethods() as $injectMethodName => $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.');
197 if (is_callable([$instance, $injectMethodName])) {
198 $instance->{$injectMethodName}($instanceToInject);
201 foreach ($classSchema->getInjectProperties() as $injectPropertyName => $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.');
207 if ($classSchema->getProperty($injectPropertyName)[
'public']) {
208 $instance->{$injectPropertyName} = $instanceToInject;
210 $propertyReflection = new \ReflectionProperty($instance, $injectPropertyName);
211 $propertyReflection->setAccessible(
true);
212 $propertyReflection->setValue($instance, $instanceToInject);
222 protected function initializeObject($instance)
224 if (method_exists($instance,
'initializeObject') && is_callable([$instance,
'initializeObject'])) {
225 $instance->initializeObject();
236 public function registerImplementation($className, $alternativeClassName)
238 $this->alternativeImplementation[$className] = $alternativeClassName;
250 private function getConstructorArguments($className, ClassSchema $classSchema, array $givenConstructorArguments)
253 $constructorArgumentInformation = $classSchema->getConstructorArguments();
254 foreach ($constructorArgumentInformation as $constructorArgumentName => $argumentInformation) {
255 $index = $argumentInformation[
'position'];
258 if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation[
'dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation[
'dependency']))) {
259 $parameter = $givenConstructorArguments[$index];
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.');
266 } elseif ($argumentInformation[
'hasDefaultValue'] ===
true) {
267 $parameter = $argumentInformation[
'defaultValue'];
269 throw new \InvalidArgumentException(
'not a correct info array of constructor dependencies was passed!', 1476107941);
272 $parameters[] = $parameter;
284 public function getImplementationClassName($className)
286 if (isset($this->alternativeImplementation[$className])) {
287 $className = $this->alternativeImplementation[$className];
289 if (substr($className, -9) ===
'Interface') {
290 $className = substr($className, 0, -9);
300 public function isSingleton($className)
302 return $this->getReflectionService()->getClassSchema($className)->isSingleton();
310 public function isPrototype($className)
312 return !$this->isSingleton($className);
318 protected function getLogger()
320 return GeneralUtility::makeInstance(LogManager::class)->getLogger(static::class);
331 protected function getReflectionService(): ReflectionService
333 return $this->reflectionService ?? ($this->reflectionService = GeneralUtility::makeInstance(ReflectionService::class, GeneralUtility::makeInstance(CacheManager::class)));