From 73b5e17d1807bc0e9bb2a11dc5f2affc65b304bd Mon Sep 17 00:00:00 2001 From: Alexander Schnitzler <git@alexanderschnitzler.de> Date: Thu, 12 Oct 2017 16:58:33 +0200 Subject: [PATCH] [!!!][TASK] Replace ClassInfo with ClassSchema Extbase came along with two main caches for reflection data. extbase_reflection and extbase_object. The latter mostly stored information that were relevant to the dependency injection, like inject methods and properties and constructor parameters. The information was gathered by actual reflection and by analysing doc blocks of properties and methods. extbase_reflection stored similar reflection and doc block data about objects but mainly for the parts outside dependency injection. For example, the validation resolver used it to identify @validate tags, the ActionController used it to identity which properties not to validate. The ORM also used it a lot to find annotated types via @var and so on. There were a few issues with these two approaches: - A lot of redundant data was fetched - Data was fetched multiple times at different locations - The extbase_reflection cache was stored each plugin seperately, resulting in a lot of redundant cache data for each plugin cache - At a lot of places, the reflection service was used to reflect objects, but the data wasn't cached or taken from a cache resulting in performance drawbacks This patch solves these issues with several approaches: - The extbase_object cache has been removed completely and all necessary information about objects, mainly @inject stuff, is now fetched from the ReflectionService as well. - The ReflectionService does still create ClassSchema instances but these were improved a lot. All necessary information is now gathered during the instantiation of ClassSchema instances. That means that all necessary data is fetched once and then it can be used everywhere making any further reflection superfluous. - As runtime reflection has been removed completey, along with it several reflection classes, that analyzed doc blocks, have been removed as well. These are no longer necessary. - The extbase_reflection cache is no longer plugin based and will no longer be stored in the database in the first place. Serialized ClassSchema instances will stored in typo3temp. Releases: master Resolves: #57594 Resolves: #55654 Change-Id: I93b905c85c4f28775f014ca8b585347bf6f1e7d3 Reviewed-on: https://review.typo3.org/54381 Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Benni Mack <benni@typo3.org> Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> --- .../Configuration/DefaultConfiguration.php | 10 +- ...OptimizeReflectionServiceCacheHandling.rst | 63 +++ ...OptimizeReflectionServiceCacheHandling.rst | 42 ++ ...OptimizeReflectionServiceCacheHandling.rst | 54 ++ .../sysext/extbase/Classes/Core/Bootstrap.php | 45 -- .../extbase/Classes/Mvc/Cli/Command.php | 58 +-- .../Mvc/Controller/ActionController.php | 2 +- .../sysext/extbase/Classes/Mvc/Dispatcher.php | 13 - .../Classes/Mvc/RequestHandlerResolver.php | 13 - .../Classes/Object/Container/ClassInfo.php | 170 ------- .../Object/Container/ClassInfoCache.php | 84 --- .../Object/Container/ClassInfoFactory.php | 172 ------- .../Classes/Object/Container/Container.php | 136 ++--- .../Classes/Persistence/Generic/Backend.php | 4 +- .../Persistence/Generic/Mapper/DataMapper.php | 2 +- .../Classes/Persistence/Generic/Session.php | 13 - .../Classes/Reflection/ClassReflection.php | 187 ------- .../Classes/Reflection/ClassSchema.php | 441 +++++++++++++++- .../Classes/Reflection/MethodReflection.php | 111 ---- .../Classes/Reflection/ObjectAccess.php | 12 +- .../Reflection/ParameterReflection.php | 46 -- .../Classes/Reflection/PropertyReflection.php | 98 ---- .../Classes/Reflection/ReflectionService.php | 480 +++--------------- .../Classes/Scheduler/TaskExecutor.php | 9 - .../Tests/Unit/Mvc/Cli/CommandTest.php | 224 ++++++-- .../Command/MockCCommandController.php | 60 +++ .../Mvc/Controller/ActionControllerTest.php | 9 +- .../Object/Container/ClassInfoFactoryTest.php | 81 --- .../Unit/Object/Container/ContainerTest.php | 38 +- .../Generic/Mapper/DataMapperTest.php | 42 +- .../Property/TypeConverter/Fixtures/Query.php | 22 + .../PersistentObjectConverterTest.php | 2 +- .../Tests/Unit/Reflection/ClassSchemaTest.php | 209 ++++++-- .../DummyClassWithAllTypesOfMethods.php | 75 +++ .../DummyClassWithAllTypesOfProperties.php | 56 ++ ...WithConstructorAndConstructorArguments.php | 27 + ...ndConstructorArgumentsWithDependencies.php | 27 + .../Unit/Reflection/Fixture/DummyModel.php | 24 + .../Fixture/DummyModelRepository.php | 24 + .../Unit/Reflection/ReflectionServiceTest.php | 34 +- .../Classes/Domain/Runtime/FormRuntime.php | 3 +- .../ExtbaseObjectCache/ApcPreset.php | 67 --- .../ExtbaseObjectCache/ApcuPreset.php | 67 --- .../ExtbaseObjectCache/DatabasePreset.php | 57 --- .../ExtbaseObjectCacheFeature.php | 37 -- .../Classes/Configuration/FeatureManager.php | 2 - .../ExtensionScanner/Php/ClassNameMatcher.php | 33 ++ .../Php/MethodCallMatcher.php | 70 +++ .../Settings/Presets/ExtbaseObjectCache.html | 27 - .../Presets/ExtbaseObjectCache/Apc.html | 37 -- .../Presets/ExtbaseObjectCache/Apcu.html | 37 -- .../Presets/ExtbaseObjectCache/Database.html | 28 - 52 files changed, 1567 insertions(+), 2117 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Breaking-57594-OptimizeReflectionServiceCacheHandling.rst create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-57594-OptimizeReflectionServiceCacheHandling.rst delete mode 100644 typo3/sysext/extbase/Classes/Object/Container/ClassInfo.php delete mode 100644 typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php delete mode 100644 typo3/sysext/extbase/Classes/Object/Container/ClassInfoFactory.php delete mode 100644 typo3/sysext/extbase/Classes/Reflection/ClassReflection.php delete mode 100644 typo3/sysext/extbase/Classes/Reflection/MethodReflection.php delete mode 100644 typo3/sysext/extbase/Classes/Reflection/ParameterReflection.php delete mode 100644 typo3/sysext/extbase/Classes/Reflection/PropertyReflection.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Mvc/Cli/Fixture/Command/MockCCommandController.php delete mode 100644 typo3/sysext/extbase/Tests/Unit/Object/Container/ClassInfoFactoryTest.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/Fixtures/Query.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArguments.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArgumentsWithDependencies.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModel.php create mode 100644 typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModelRepository.php delete mode 100644 typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcPreset.php delete mode 100644 typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcuPreset.php delete mode 100644 typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/DatabasePreset.php delete mode 100644 typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ExtbaseObjectCacheFeature.php delete mode 100644 typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache.html delete mode 100644 typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apc.html delete mode 100644 typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apcu.html delete mode 100644 typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Database.html diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php index 7bb1f4c0346c..526a7059b8a2 100644 --- a/typo3/sysext/core/Configuration/DefaultConfiguration.php +++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php @@ -183,17 +183,9 @@ return [ 'frontend' => \TYPO3\CMS\Fluid\Core\Cache\FluidTemplateCache::class, 'groups' => ['system'], ], - 'extbase_object' => [ - 'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, - 'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class, - 'options' => [ - 'defaultLifetime' => 0, - ], - 'groups' => ['system'] - ], 'extbase_reflection' => [ 'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, - 'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class, + 'backend' => \TYPO3\CMS\Core\Cache\Backend\SimpleFileBackend::class, 'options' => [ 'defaultLifetime' => 0, ], diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-57594-OptimizeReflectionServiceCacheHandling.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-57594-OptimizeReflectionServiceCacheHandling.rst new file mode 100644 index 000000000000..6d3b89357057 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Breaking-57594-OptimizeReflectionServiceCacheHandling.rst @@ -0,0 +1,63 @@ +.. include:: ../../Includes.txt + +============================================================ +Breaking: #57594 - Optimize ReflectionService Cache handling +============================================================ + +See :issue:`57594` + +Description +=========== + +The `extbase_object` cache has been removed completely and all necessary information about objects, +mainly @inject information, is now fetched from the ReflectionService as well. + +The ReflectionService does still create `ClassSchema` instances but these were improved a lot. All +necessary information is now gathered during the instantiation of `ClassSchema` instances. That means +that all necessary data is fetched once and then it can be used everywhere making any further +reflection superfluous. + +As runtime reflection has been removed completely, along with it several reflection classes, that +analyzed doc blocks, have been removed as well. These are no longer necessary. + +The `extbase_reflection` cache is no longer plugin based and will no longer be stored in the database +in the first place. Serialized ClassSchema instances will be stored in `typo3temp/var/Cache`. + +The following classes for internal use only and have been removed: + +* :php:`ClassInfo` +* :php:`ClassInfoCache` +* :php:`ClassInfoFactory` +* :php:`ClassReflection` +* :php:`MethodReflection` +* :php:`ParameterReflection` +* :php:`PropertyReflection` + +The following methods of the PHP class :php:`ReflectionService` have been removed: + +* :php:`injectConfigurationManager` +* :php:`setDataCache` +* :php:`initialize` +* :php:`isInitialized` +* :php:`shutdown` + + +Impact +====== + +Installations using the above classes or methods will throw a fatal error. + + +Affected Installations +====================== + +Installations using one of the mentioned classes or methods instead of the ReflectionService API. + + +Migration +========= + +Use the class :php:`ReflectionService` as API which will be automatically initialized on +instantiation. + +.. index:: PHP-API, FullyScanned diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst new file mode 100644 index 000000000000..f2b5d1992d42 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst @@ -0,0 +1,42 @@ +.. include:: ../../Includes.txt + +=============================================================== +Deprecation: #57594 - Optimize ReflectionService Cache handling +=============================================================== + +See :issue:`57594` + +Description +=========== + +In the process of streamlining the internal reflection / docparser cache handling, the following +methods of the PHP class :php:`ClassSchema` have been deprecated: + +* :php:`addProperty()` +* :php:`setModelType()` +* :php:`getModelType()` +* :php:`setUuidPropertyName()` +* :php:`getUuidPropertyName()` +* :php:`markAsIdentityProperty()` +* :php:`getIdentityProperties()` + + +Impact +====== + +Installations using the above methods will trigger a :php:`E_USER_DEPRECATED` warning. + + +Affected Installations +====================== + +Installations using one of the mentioned methods instead of the ReflectionService API. + + +Migration +========= + +Use the class :php:`ReflectionService` as API which will be automatically initialized on +nstantiation. + +.. index:: PHP-API, FullyScanned diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-57594-OptimizeReflectionServiceCacheHandling.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-57594-OptimizeReflectionServiceCacheHandling.rst new file mode 100644 index 000000000000..32cc9ac230e1 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-57594-OptimizeReflectionServiceCacheHandling.rst @@ -0,0 +1,54 @@ +.. include:: ../../Includes.txt + +=========================================================== +Feature: #57594 - Optimize ReflectionService Cache handling +=========================================================== + +See :issue:`57594` + +Description +=========== + +Since its beginnings, Extbase came along with two main caches for reflection data, +`extbase_reflection` and `extbase_object`. The latter mostly stored information that were relevant +to the dependency injection, like inject methods, inject properties and constructor parameters. The +information was gathered by actual reflection and by analysing doc blocks of properties and methods. + +`extbase_reflection` stored similar reflection and doc block data about objects but mainly for the +parts outside dependency injection. + +For example, the validation resolver used it to identify `@validate` tags, the ActionController used +it to identity which properties not to validate. The ORM also used it a lot to find annotated types +via `@var`. + +There were a few issues with these two approaches: + +* A lot of redundant data was fetched + +* Data was fetched multiple times at different locations + +* The `extbase_reflection` cache was stored each plugin separately, resulting in a lot of redundant +cache data for each plugin cache + +* At a lot of places, the reflection service was used to reflect objects, but the data wasn't cached +or taken from a cache resulting in performance drawbacks + + +Impact +====== + +* The `extbase_object` cache has been removed completely and all necessary information about objects, +mainly `@inject` functionality, is now fetched from the `ReflectionService` as well. + +* The `ReflectionService` does still create `ClassSchema` instances but these were improved a lot. +All necessary information is now gathered during the instantiation of ClassSchema instances. This +means that all necessary data is fetched once and then it can be used everywhere making any further +reflection superfluous. + +* As runtime reflection has been removed completely, along with it several reflection classes, that +analyzed doc blocks, have been removed as well. These are no longer necessary. + +* The `extbase_reflection` cache is no longer plugin based and will no longer be stored in the +database in the first place. Serialized `ClassSchema` instances will be stored in `typo3temp/var/Cache`. + +.. index:: PHP-API diff --git a/typo3/sysext/extbase/Classes/Core/Bootstrap.php b/typo3/sysext/extbase/Classes/Core/Bootstrap.php index 39c84f587e31..6b9d9427a193 100644 --- a/typo3/sysext/extbase/Classes/Core/Bootstrap.php +++ b/typo3/sysext/extbase/Classes/Core/Bootstrap.php @@ -30,13 +30,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface */ public $cObj; - /** - * The application context - * - * @var string - */ - protected $context; - /** * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManager */ @@ -47,16 +40,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface */ protected $objectManager; - /** - * @var \TYPO3\CMS\Core\Cache\CacheManager - */ - protected $cacheManager; - - /** - * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService - */ - protected $reflectionService; - /** * @var \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager */ @@ -86,8 +69,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface $this->initializeObjectManager(); $this->initializeConfiguration($configuration); $this->configureObjectManager(); - $this->initializeCache(); - $this->initializeReflection(); $this->initializePersistence(); } @@ -137,30 +118,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface } } - /** - * Initializes the cache framework - * - * @see initialize() - */ - protected function initializeCache() - { - $this->cacheManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class); - } - - /** - * Initializes the Reflection Service - * - * @see initialize() - */ - protected function initializeReflection() - { - $this->reflectionService = $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); - $this->reflectionService->setDataCache($this->cacheManager->getCache('extbase_reflection')); - if (!$this->reflectionService->isInitialized()) { - $this->reflectionService->initialize(); - } - } - /** * Initializes the persistence framework * @@ -201,7 +158,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface // This happens for instance, when a USER object was converted to a USER_INT // @see TYPO3\CMS\Extbase\Mvc\Web\FrontendRequestHandler::handleRequest() if ($response === null) { - $this->reflectionService->shutdown(); $content = ''; } else { $content = $response->shutdown(); @@ -221,7 +177,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface protected function resetSingletons() { $this->persistenceManager->persistAll(); - $this->reflectionService->shutdown(); } /** diff --git a/typo3/sysext/extbase/Classes/Mvc/Cli/Command.php b/typo3/sysext/extbase/Classes/Mvc/Cli/Command.php index 7653a2e2efc5..aa3a51e61fee 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Cli/Command.php +++ b/typo3/sysext/extbase/Classes/Mvc/Cli/Command.php @@ -14,6 +14,8 @@ namespace TYPO3\CMS\Extbase\Mvc\Cli; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Extbase\Reflection\ClassSchema; + /** * Represents a Command * @@ -41,11 +43,6 @@ class Command */ protected $commandIdentifier; - /** - * @var \TYPO3\CMS\Extbase\Reflection\MethodReflection - */ - protected $commandMethodReflection; - /** * Name of the extension to which this command belongs * @@ -58,6 +55,16 @@ class Command */ protected $reflectionService; + /** + * @var ClassSchema + */ + protected $classSchema; + + /** + * @var string + */ + protected $controllerCommandMethod; + /** * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager */ @@ -85,6 +92,7 @@ class Command { $this->controllerClassName = $controllerClassName; $this->controllerCommandName = $controllerCommandName; + $this->controllerCommandMethod = $this->controllerCommandName . 'Command'; $delimiter = strpos($controllerClassName, '\\') !== false ? '\\' : '_'; $classNameParts = explode($delimiter, $controllerClassName); if (isset($classNameParts[0]) && $classNameParts[0] === 'TYPO3' && isset($classNameParts[1]) && $classNameParts[1] === 'CMS') { @@ -111,6 +119,11 @@ class Command $this->commandIdentifier = strtolower($extensionKey . ':' . substr($classNameParts[$numberOfClassNameParts - 1], 0, -17) . ':' . $controllerCommandName); } + public function initializeObject() + { + $this->classSchema = $this->reflectionService->getClassSchema($this->controllerClassName); + } + /** * @return string */ @@ -154,7 +167,7 @@ class Command */ public function getShortDescription() { - $lines = explode(LF, $this->getCommandMethodReflection()->getDescription()); + $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']); return !empty($lines) ? trim($lines[0]) : '<no description available>'; } @@ -167,7 +180,7 @@ class Command */ public function getDescription() { - $lines = explode(LF, $this->getCommandMethodReflection()->getDescription()); + $lines = explode(LF, $this->classSchema->getMethod($this->controllerCommandMethod)['description']); array_shift($lines); $descriptionLines = []; foreach ($lines as $line) { @@ -186,7 +199,7 @@ class Command */ public function hasArguments() { - return !empty($this->getCommandMethodReflection()->getParameters()); + return !empty($this->classSchema->getMethod($this->controllerCommandMethod)['params']); } /** @@ -202,12 +215,11 @@ class Command return []; } $commandArgumentDefinitions = []; - $commandMethodReflection = $this->getCommandMethodReflection(); - $annotations = $commandMethodReflection->getTagsValues(); - $commandParameters = $this->reflectionService->getMethodParameters($this->controllerClassName, $this->controllerCommandName . 'Command'); + $commandParameters = $this->classSchema->getMethod($this->controllerCommandMethod)['params']; + $commandParameterTags = $this->classSchema->getMethod($this->controllerCommandMethod)['tags']['param']; $i = 0; foreach ($commandParameters as $commandParameterName => $commandParameterDefinition) { - $explodedAnnotation = preg_split('/\s+/', $annotations['param'][$i], 3); + $explodedAnnotation = preg_split('/\s+/', $commandParameterTags[$i], 3); $description = !empty($explodedAnnotation[2]) ? $explodedAnnotation[2] : ''; $required = $commandParameterDefinition['optional'] !== true; $commandArgumentDefinitions[] = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class, $commandParameterName, $required, $description); @@ -225,7 +237,7 @@ class Command */ public function isInternal() { - return $this->getCommandMethodReflection()->isTaggedWith('internal'); + return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['internal']); } /** @@ -235,7 +247,7 @@ class Command */ public function isCliOnly() { - return $this->getCommandMethodReflection()->isTaggedWith('cli'); + return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['cli']); } /** @@ -247,7 +259,7 @@ class Command */ public function isFlushingCaches() { - return $this->getCommandMethodReflection()->isTaggedWith('flushesCaches'); + return isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['flushesCaches']); } /** @@ -258,27 +270,15 @@ class Command */ public function getRelatedCommandIdentifiers() { - $commandMethodReflection = $this->getCommandMethodReflection(); - if (!$commandMethodReflection->isTaggedWith('see')) { + if (!isset($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'])) { return []; } $relatedCommandIdentifiers = []; - foreach ($commandMethodReflection->getTagValues('see') as $tagValue) { + foreach ($this->classSchema->getMethod($this->controllerCommandMethod)['tags']['see'] as $tagValue) { if (preg_match('/^[\\w\\d\\.]+:[\\w\\d]+:[\\w\\d]+$/', $tagValue) === 1) { $relatedCommandIdentifiers[] = $tagValue; } } return $relatedCommandIdentifiers; } - - /** - * @return \TYPO3\CMS\Extbase\Reflection\MethodReflection - */ - protected function getCommandMethodReflection() - { - if ($this->commandMethodReflection === null) { - $this->commandMethodReflection = $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\MethodReflection::class, $this->controllerClassName, $this->controllerCommandName . 'Command'); - } - return $this->commandMethodReflection; - } } diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php index c97960da0bcb..ebc0bba86111 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php +++ b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php @@ -235,7 +235,7 @@ class ActionController extends AbstractController if ($dataType === null) { throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException('The argument type for parameter $' . $parameterName . ' of method ' . static::class . '->' . $this->actionMethodName . '() could not be detected.', 1253175643); } - $defaultValue = isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : null; + $defaultValue = $parameterInfo['hasDefaultValue'] === true ? $parameterInfo['defaultValue'] : null; $this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue); } } diff --git a/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php b/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php index c02e80ea1254..6d82759185e6 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php +++ b/typo3/sysext/extbase/Classes/Mvc/Dispatcher.php @@ -25,11 +25,6 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface */ protected $objectManager; - /** - * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService - */ - protected $reflectionService; - /** * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher */ @@ -40,14 +35,6 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface */ protected $settings = []; - /** - * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService - */ - public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) - { - $this->reflectionService = $reflectionService; - } - /** * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher */ diff --git a/typo3/sysext/extbase/Classes/Mvc/RequestHandlerResolver.php b/typo3/sysext/extbase/Classes/Mvc/RequestHandlerResolver.php index e85148f773ec..7a8f8e627f1a 100644 --- a/typo3/sysext/extbase/Classes/Mvc/RequestHandlerResolver.php +++ b/typo3/sysext/extbase/Classes/Mvc/RequestHandlerResolver.php @@ -24,11 +24,6 @@ class RequestHandlerResolver */ protected $objectManager; - /** - * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService - */ - protected $reflectionService; - /** * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface */ @@ -42,14 +37,6 @@ class RequestHandlerResolver $this->objectManager = $objectManager; } - /** - * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService - */ - public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) - { - $this->reflectionService = $reflectionService; - } - /** * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager */ diff --git a/typo3/sysext/extbase/Classes/Object/Container/ClassInfo.php b/typo3/sysext/extbase/Classes/Object/Container/ClassInfo.php deleted file mode 100644 index 40cbe8d1d77b..000000000000 --- a/typo3/sysext/extbase/Classes/Object/Container/ClassInfo.php +++ /dev/null @@ -1,170 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Object\Container; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Value object containing the relevant informations for a class, - * this object is build by the classInfoFactory - or could also be restored from a cache - */ -class ClassInfo -{ - /** - * The classname of the class where the infos belong to - * - * @var string - */ - private $className; - - /** - * The constructor Dependencies for the class in the format: - * array( - * 0 => array( <-- parameters for argument 1 - * 'name' => <arg name>, <-- name of argument - * 'dependency' => <classname>, <-- if the argument is a class, the type of the argument - * 'defaultvalue' => <mixed>) <-- if the argument is optional, its default value - * ), - * 1 => ... - * ) - * - * @var array - */ - private $constructorArguments; - - /** - * All setter injections in the format - * array (<nameOfMethod> => <classNameToInject> ) - * - * @var array - */ - private $injectMethods; - - /** - * All setter injections in the format - * array (<nameOfProperty> => <classNameToInject> ) - * - * @var array - */ - private $injectProperties; - - /** - * Indicates if the class is a singleton or not. - * - * @var bool - */ - private $isSingleton = false; - - /** - * Indicates if the class has the method initializeObject - * - * @var bool - */ - private $isInitializeable = false; - - /** - * @param string $className - * @param array $constructorArguments - * @param array $injectMethods - * @param bool $isSingleton - * @param bool $isInitializeable - * @param array $injectProperties - */ - public function __construct($className, array $constructorArguments, array $injectMethods, $isSingleton = false, $isInitializeable = false, array $injectProperties = []) - { - $this->className = $className; - $this->constructorArguments = $constructorArguments; - $this->injectMethods = $injectMethods; - $this->injectProperties = $injectProperties; - $this->isSingleton = $isSingleton; - $this->isInitializeable = $isInitializeable; - } - - /** - * Gets the class name passed to constructor - * - * @return string - */ - public function getClassName() - { - return $this->className; - } - - /** - * Get arguments passed to constructor - * - * @return array - */ - public function getConstructorArguments() - { - return $this->constructorArguments; - } - - /** - * Returns an array with the inject methods. - * - * @return array - */ - public function getInjectMethods() - { - return $this->injectMethods; - } - - /** - * Returns an array with the inject properties - * - * @return array - */ - public function getInjectProperties() - { - return $this->injectProperties; - } - - /** - * Asserts if the class is a singleton or not. - * - * @return bool - */ - public function getIsSingleton() - { - return $this->isSingleton; - } - - /** - * Asserts if the class is initializeable with initializeObject. - * - * @return bool - */ - public function getIsInitializeable() - { - return $this->isInitializeable; - } - - /** - * Asserts if the class has Dependency Injection methods - * - * @return bool - */ - public function hasInjectMethods() - { - return !empty($this->injectMethods); - } - - /** - * @return bool - */ - public function hasInjectProperties() - { - return !empty($this->injectProperties); - } -} diff --git a/typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php b/typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php deleted file mode 100644 index 3dd2920ee0ed..000000000000 --- a/typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Object\Container; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Simple Cache for classInfos - */ -class ClassInfoCache -{ - /** - * @var array - */ - private $level1Cache = []; - - /** - * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend - */ - private $level2Cache; - - /** - * constructor - */ - public function __construct() - { - $this->initializeLevel2Cache(); - } - - /** - * checks if cacheentry exists for id - * - * @param string $id - * @return bool - */ - public function has($id) - { - return isset($this->level1Cache[$id]) || $this->level2Cache->has($id); - } - - /** - * Gets the cache for the id - * - * @param string $id - * @return mixed - */ - public function get($id) - { - if (!isset($this->level1Cache[$id])) { - $this->level1Cache[$id] = $this->level2Cache->get($id); - } - return $this->level1Cache[$id]; - } - - /** - * sets the cache for the id - * - * @param string $id - * @param mixed $value - */ - public function set($id, $value) - { - $this->level1Cache[$id] = $value; - $this->level2Cache->set($id, $value); - } - - /** - * Initialize the TYPO3 second level cache - */ - private function initializeLevel2Cache() - { - $this->level2Cache = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('extbase_object'); - } -} diff --git a/typo3/sysext/extbase/Classes/Object/Container/ClassInfoFactory.php b/typo3/sysext/extbase/Classes/Object/Container/ClassInfoFactory.php deleted file mode 100644 index 26980ea12c8a..000000000000 --- a/typo3/sysext/extbase/Classes/Object/Container/ClassInfoFactory.php +++ /dev/null @@ -1,172 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Object\Container; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * TYPO3 Dependency Injection container - */ -class ClassInfoFactory -{ - /** - * Factory metod that builds a ClassInfo Object for the given classname - using reflection - * - * @param string $className The class name to build the class info for - * @throws Exception\UnknownObjectException - * @return \TYPO3\CMS\Extbase\Object\Container\ClassInfo the class info - */ - public function buildClassInfoFromClassName($className) - { - if ($className === 'DateTime') { - return new \TYPO3\CMS\Extbase\Object\Container\ClassInfo($className, [], [], false, false, []); - } - try { - $reflectedClass = new \ReflectionClass($className); - } catch (\Exception $e) { - throw new \TYPO3\CMS\Extbase\Object\Container\Exception\UnknownObjectException('Could not analyse class: "' . $className . '" maybe not loaded or no autoloader? ' . $e->getMessage(), 1289386765, $e); - } - $constructorArguments = $this->getConstructorArguments($reflectedClass); - $injectMethods = $this->getInjectMethods($reflectedClass); - $injectProperties = $this->getInjectProperties($reflectedClass); - $isSingleton = $this->getIsSingleton($className); - $isInitializeable = $this->getIsInitializeable($className); - return new \TYPO3\CMS\Extbase\Object\Container\ClassInfo($className, $constructorArguments, $injectMethods, $isSingleton, $isInitializeable, $injectProperties); - } - - /** - * Build a list of constructor arguments - * - * @param \ReflectionClass $reflectedClass - * @return array of parameter infos for constructor - */ - private function getConstructorArguments(\ReflectionClass $reflectedClass) - { - $reflectionMethod = $reflectedClass->getConstructor(); - if (!is_object($reflectionMethod)) { - return []; - } - $result = []; - foreach ($reflectionMethod->getParameters() as $reflectionParameter) { - /* @var $reflectionParameter \ReflectionParameter */ - $info = []; - $info['name'] = $reflectionParameter->getName(); - if ($reflectionParameter->getClass()) { - $info['dependency'] = $reflectionParameter->getClass()->getName(); - } - - try { - $info['defaultValue'] = $reflectionParameter->getDefaultValue(); - } catch (\ReflectionException $e) { - } - - $result[] = $info; - } - return $result; - } - - /** - * Build a list of inject methods for the given class. - * - * @param \ReflectionClass $reflectedClass - * @throws \Exception - * @return array (nameOfInjectMethod => nameOfClassToBeInjected) - */ - private function getInjectMethods(\ReflectionClass $reflectedClass) - { - $result = []; - $reflectionMethods = $reflectedClass->getMethods(); - if (is_array($reflectionMethods)) { - foreach ($reflectionMethods as $reflectionMethod) { - if ($reflectionMethod->isPublic() && $this->isNameOfInjectMethod($reflectionMethod->getName())) { - $reflectionParameter = $reflectionMethod->getParameters(); - if (isset($reflectionParameter[0])) { - if (!$reflectionParameter[0]->getClass()) { - throw new \Exception( - 'Method "' . $reflectionMethod->getName() . '" of class "' . $reflectedClass->getName() . '" is marked as setter for Dependency Injection, but does not have a type annotation', - 1476108030 - ); - } - $result[$reflectionMethod->getName()] = $reflectionParameter[0]->getClass()->getName(); - } - } - } - } - return $result; - } - - /** - * Build a list of properties to be injected for the given class. - * - * @param \ReflectionClass $reflectedClass - * @return array (nameOfInjectProperty => nameOfClassToBeInjected) - */ - private function getInjectProperties(\ReflectionClass $reflectedClass) - { - $result = []; - $reflectionProperties = $reflectedClass->getProperties(); - if (is_array($reflectionProperties)) { - foreach ($reflectionProperties as $reflectionProperty) { - $reflectedProperty = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Reflection\PropertyReflection::class, $reflectedClass->getName(), $reflectionProperty->getName()); - if ($reflectedProperty->isTaggedWith('inject') && $reflectedProperty->getName() !== 'settings') { - $varValues = $reflectedProperty->getTagValues('var'); - if (count($varValues) === 1) { - $result[$reflectedProperty->getName()] = ltrim($varValues[0], '\\'); - } - } - } - } - return $result; - } - - /** - * This method checks if given method can be used for injection - * - * @param string $methodName - * @return bool - */ - private function isNameOfInjectMethod($methodName) - { - if ( - substr($methodName, 0, 6) === 'inject' - && $methodName[6] === strtoupper($methodName[6]) - && $methodName !== 'injectSettings' - ) { - return true; - } - return false; - } - - /** - * This method is used to determine if a class is a singleton or not. - * - * @param string $classname - * @return bool - */ - private function getIsSingleton($classname) - { - return in_array(\TYPO3\CMS\Core\SingletonInterface::class, class_implements($classname)); - } - - /** - * This method is used to determine of the object is initializeable with the - * method initializeObject. - * - * @param string $classname - * @return bool - */ - private function getIsInitializeable($classname) - { - return method_exists($classname, 'initializeObject'); - } -} diff --git a/typo3/sysext/extbase/Classes/Object/Container/Container.php b/typo3/sysext/extbase/Classes/Object/Container/Container.php index 179b78a07d24..4d7682525385 100644 --- a/typo3/sysext/extbase/Classes/Object/Container/Container.php +++ b/typo3/sysext/extbase/Classes/Object/Container/Container.php @@ -15,8 +15,11 @@ namespace TYPO3\CMS\Extbase\Object\Container; */ use Psr\Log\LoggerInterface; +use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Log\LogManager; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Reflection\ClassSchema; +use TYPO3\CMS\Extbase\Reflection\ReflectionService; /** * Internal TYPO3 Dependency Injection container @@ -26,13 +29,6 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface const SCOPE_PROTOTYPE = 1; const SCOPE_SINGLETON = 2; - /** - * internal cache for classinfos - * - * @var \TYPO3\CMS\Extbase\Object\Container\ClassInfoCache - */ - private $cache = null; - /** * registered alternative implementations of a class * e.g. used to know the class for an AbstractClass or a Dependency @@ -41,13 +37,6 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface */ private $alternativeImplementation; - /** - * reference to the classinfofactory, that analyses dependencys - * - * @var \TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory - */ - private $classInfoFactory = null; - /** * @var \Doctrine\Instantiator\InstantiatorInterface */ @@ -67,6 +56,11 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface */ private $prototypeObjectsWhichAreCurrentlyInstanciated; + /** + * @var ReflectionService + */ + private $reflectionService; + /** * Constructor is protected since container should * be a singleton. @@ -75,32 +69,7 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface */ public function __construct() { - } - - /** - * Internal method to create the classInfoFactory, extracted to be mockable. - * - * @return \TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory - */ - protected function getClassInfoFactory() - { - if ($this->classInfoFactory == null) { - $this->classInfoFactory = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory::class); - } - return $this->classInfoFactory; - } - - /** - * Internal method to create the classInfoCache, extracted to be mockable. - * - * @return \TYPO3\CMS\Extbase\Object\Container\ClassInfoCache - */ - protected function getClassInfoCache() - { - if ($this->cache == null) { - $this->cache = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\Container\ClassInfoCache::class); - } - return $this->cache; + $this->reflectionService = GeneralUtility::makeInstance(ReflectionService::class, GeneralUtility::makeInstance(CacheManager::class)); } /** @@ -139,10 +108,10 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface public function getEmptyObject($className) { $className = $this->getImplementationClassName($className); - $classInfo = $this->getClassInfo($className); + $classSchema = $this->reflectionService->getClassSchema($className); $object = $this->getInstantiator()->instantiate($className); - $this->injectDependencies($object, $classInfo); - $this->initializeObject($object, $classInfo); + $this->injectDependencies($object, $classSchema); + $this->initializeObject($object); return $object; } @@ -174,17 +143,18 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface } return $this->singletonInstances[$className]; } - $classInfo = $this->getClassInfo($className); - $classIsSingleton = $classInfo->getIsSingleton(); + + $classSchema = $this->reflectionService->getClassSchema($className); + $classIsSingleton = $classSchema->isSingleton(); if (!$classIsSingleton) { if (array_key_exists($className, $this->prototypeObjectsWhichAreCurrentlyInstanciated) !== false) { throw new \TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException('Cyclic dependency in prototype object, for class "' . $className . '".', 1295611406); } $this->prototypeObjectsWhichAreCurrentlyInstanciated[$className] = true; } - $instance = $this->instanciateObject($classInfo, $givenConstructorArguments); - $this->injectDependencies($instance, $classInfo); - $this->initializeObject($instance, $classInfo); + $instance = $this->instanciateObject($classSchema, $givenConstructorArguments); + $this->injectDependencies($instance, $classSchema); + $this->initializeObject($instance); if (!$classIsSingleton) { unset($this->prototypeObjectsWhichAreCurrentlyInstanciated[$className]); } @@ -196,19 +166,19 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface * Additionally, directly registers all singletons in the singleton registry, * such that circular references of singletons are correctly instanciated. * - * @param \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo + * @param ClassSchema $classSchema * @param array $givenConstructorArguments * @throws \TYPO3\CMS\Extbase\Object\Exception * @return object the new instance */ - protected function instanciateObject(\TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments) + protected function instanciateObject(ClassSchema $classSchema, array $givenConstructorArguments) { - $className = $classInfo->getClassName(); - $classIsSingleton = $classInfo->getIsSingleton(); + $className = $classSchema->getClassName(); + $classIsSingleton = $classSchema->isSingleton(); if ($classIsSingleton && !empty($givenConstructorArguments)) { throw new \TYPO3\CMS\Extbase\Object\Exception('Object "' . $className . '" has explicit constructor arguments but is a singleton; this is not allowed.', 1292858051); } - $constructorArguments = $this->getConstructorArguments($className, $classInfo, $givenConstructorArguments); + $constructorArguments = $this->getConstructorArguments($className, $classSchema, $givenConstructorArguments); array_unshift($constructorArguments, $className); $instance = call_user_func_array([GeneralUtility::class, 'makeInstance'], $constructorArguments); if ($classIsSingleton) { @@ -221,28 +191,28 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface * Inject setter-dependencies into $instance * * @param object $instance - * @param \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo + * @param ClassSchema $classSchema */ - protected function injectDependencies($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo) + protected function injectDependencies($instance, ClassSchema $classSchema) { - if (!$classInfo->hasInjectMethods() && !$classInfo->hasInjectProperties()) { + if (!$classSchema->hasInjectMethods() && !$classSchema->hasInjectProperties()) { return; } - foreach ($classInfo->getInjectMethods() as $injectMethodName => $classNameToInject) { + foreach ($classSchema->getInjectMethods() as $injectMethodName => $classNameToInject) { $instanceToInject = $this->getInstanceInternal($classNameToInject); - if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) { - $this->getLogger()->notice('The singleton "' . $classInfo->getClassName() . '" needs a prototype in "' . $injectMethodName . '". This is often a bad code smell; often you rather want to inject a singleton.'); + if ($classSchema->isSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) { + $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.'); } if (is_callable([$instance, $injectMethodName])) { $instance->{$injectMethodName}($instanceToInject); } } - foreach ($classInfo->getInjectProperties() as $injectPropertyName => $classNameToInject) { + foreach ($classSchema->getInjectProperties() as $injectPropertyName => $classNameToInject) { $instanceToInject = $this->getInstanceInternal($classNameToInject); - if ($classInfo->getIsSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) { - $this->getLogger()->notice('The singleton "' . $classInfo->getClassName() . '" needs a prototype in "' . $injectPropertyName . '". This is often a bad code smell; often you rather want to inject a singleton.'); + if ($classSchema->isSingleton() && !$instanceToInject instanceof \TYPO3\CMS\Core\SingletonInterface) { + $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.'); } - $propertyReflection = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Reflection\PropertyReflection::class, $instance, $injectPropertyName); + $propertyReflection = new \ReflectionProperty($instance, $injectPropertyName); $propertyReflection->setAccessible(true); $propertyReflection->setValue($instance, $instanceToInject); @@ -253,11 +223,10 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface * Call object initializer if present in object * * @param object $instance - * @param \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo */ - protected function initializeObject($instance, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo) + protected function initializeObject($instance) { - if ($classInfo->getIsInitializeable() && is_callable([$instance, 'initializeObject'])) { + if (method_exists($instance, 'initializeObject') && is_callable([$instance, 'initializeObject'])) { $instance->initializeObject(); } } @@ -278,26 +247,28 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface * gets array of parameter that can be used to call a constructor * * @param string $className - * @param \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo + * @param ClassSchema $classSchema * @param array $givenConstructorArguments * @throws \InvalidArgumentException * @return array */ - private function getConstructorArguments($className, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $classInfo, array $givenConstructorArguments) + private function getConstructorArguments($className, ClassSchema $classSchema, array $givenConstructorArguments) { $parameters = []; - $constructorArgumentInformation = $classInfo->getConstructorArguments(); - foreach ($constructorArgumentInformation as $index => $argumentInformation) { + $constructorArgumentInformation = $classSchema->getConstructorArguments(); + foreach ($constructorArgumentInformation as $constructorArgumentName => $argumentInformation) { + $index = $argumentInformation['position']; + // Constructor argument given AND argument is a simple type OR instance of argument type if (array_key_exists($index, $givenConstructorArguments) && (!isset($argumentInformation['dependency']) || is_a($givenConstructorArguments[$index], $argumentInformation['dependency']))) { $parameter = $givenConstructorArguments[$index]; } else { - if (isset($argumentInformation['dependency']) && !array_key_exists('defaultValue', $argumentInformation)) { + if (isset($argumentInformation['dependency']) && $argumentInformation['hasDefaultValue'] === false) { $parameter = $this->getInstanceInternal($argumentInformation['dependency']); - if ($classInfo->getIsSingleton() && !$parameter instanceof \TYPO3\CMS\Core\SingletonInterface) { + if ($classSchema->isSingleton() && !$parameter instanceof \TYPO3\CMS\Core\SingletonInterface) { $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.'); } - } elseif (array_key_exists('defaultValue', $argumentInformation)) { + } elseif ($argumentInformation['hasDefaultValue'] === true) { $parameter = $argumentInformation['defaultValue']; } else { throw new \InvalidArgumentException('not a correct info array of constructor dependencies was passed!', 1476107941); @@ -326,23 +297,6 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface return $className; } - /** - * Gets Classinfos for the className - using the cache and the factory - * - * @param string $className - * @return \TYPO3\CMS\Extbase\Object\Container\ClassInfo - */ - private function getClassInfo($className) - { - $classNameHash = md5($className); - $classInfo = $this->getClassInfoCache()->get($classNameHash); - if (!$classInfo instanceof \TYPO3\CMS\Extbase\Object\Container\ClassInfo) { - $classInfo = $this->getClassInfoFactory()->buildClassInfoFromClassName($className); - $this->getClassInfoCache()->set($classNameHash, $classInfo); - } - return $classInfo; - } - /** * @param string $className * @@ -350,7 +304,7 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface */ public function isSingleton($className) { - return $this->getClassInfo($className)->getIsSingleton(); + return $this->reflectionService->getClassSchema($className)->isSingleton(); } /** diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php index c70360260de2..cd9a39312675 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php @@ -454,7 +454,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName); foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) { $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName); - if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') { + if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['annotations']['cascade'] === 'remove') { $this->removeEntity($removedObject); } } @@ -1077,7 +1077,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface continue; } $propertyMetaData = $classSchema->getProperty($propertyName); - if ($propertyMetaData['cascade'] === 'remove') { + if ($propertyMetaData['annotations']['cascade'] === 'remove') { if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) { foreach ($propertyValue as $containedObject) { $this->removeEntity($containedObject); diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php index 4adb5bedad8e..2e55f63235c6 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php @@ -349,7 +349,7 @@ class DataMapper implements \TYPO3\CMS\Core\SingletonInterface public function fetchRelated(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '', $enableLazyLoading = true) { $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName); - if ($enableLazyLoading === true && $propertyMetaData['lazy']) { + if ($enableLazyLoading === true && $propertyMetaData['annotations']['lazy']) { if ($propertyMetaData['type'] === \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class) { $result = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage::class, $parentObject, $propertyName, $fieldValue); } else { diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php index 2b56c9872232..ab0351bc7ba0 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Session.php @@ -47,19 +47,6 @@ class Session implements \TYPO3\CMS\Core\SingletonInterface */ protected $identifierMap = []; - /** - * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService - */ - protected $reflectionService; - - /** - * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService - */ - public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) - { - $this->reflectionService = $reflectionService; - } - /** * Constructs a new Session */ diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassReflection.php b/typo3/sysext/extbase/Classes/Reflection/ClassReflection.php deleted file mode 100644 index 8adc6ebb91fa..000000000000 --- a/typo3/sysext/extbase/Classes/Reflection/ClassReflection.php +++ /dev/null @@ -1,187 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Reflection; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Extended version of the ReflectionClass - */ -class ClassReflection extends \ReflectionClass -{ - /** - * @var DocCommentParser Holds an instance of the doc comment parser for this class - */ - protected $docCommentParser; - - /** - * Replacement for the original getMethods() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\MethodReflection objects are returned instead of the - * original ReflectionMethod instances. - * - * @param int|NULL $filter A filter mask - * @return MethodReflection[] Method reflection objects of the methods in this class - */ - public function getMethods($filter = null) - { - $extendedMethods = []; - $methods = $filter === null ? parent::getMethods() : parent::getMethods($filter); - foreach ($methods as $method) { - $extendedMethods[] = new MethodReflection($this->getName(), $method->getName()); - } - return $extendedMethods; - } - - /** - * Replacement for the original getMethod() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\MethodReflection objects are returned instead of the - * original ReflectionMethod instances. - * - * @param string $name - * @return MethodReflection Method reflection object of the named method - */ - public function getMethod($name) - { - $parentMethod = parent::getMethod($name); - if (!is_object($parentMethod)) { - return $parentMethod; - } - return new MethodReflection($this->getName(), $parentMethod->getName()); - } - - /** - * Replacement for the original getConstructor() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\MethodReflection objects are returned instead of the - * original ReflectionMethod instances. - * - * @return MethodReflection Method reflection object of the constructor method - */ - public function getConstructor() - { - $parentConstructor = parent::getConstructor(); - if (!is_object($parentConstructor)) { - return $parentConstructor; - } - return new MethodReflection($this->getName(), $parentConstructor->getName()); - } - - /** - * Replacement for the original getProperties() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\PropertyReflection objects are returned instead of the - * original ReflectionProperty instances. - * - * @param int|NULL $filter A filter mask - * @return PropertyReflection[] Property reflection objects of the properties in this class - */ - public function getProperties($filter = null) - { - $extendedProperties = []; - $properties = $filter === null ? parent::getProperties() : parent::getProperties($filter); - foreach ($properties as $property) { - $extendedProperties[] = new PropertyReflection($this->getName(), $property->getName()); - } - return $extendedProperties; - } - - /** - * Replacement for the original getProperty() method which makes sure - * that a \TYPO3\CMS\Extbase\Reflection\PropertyReflection object is returned instead of the - * original ReflectionProperty instance. - * - * @param string $name Name of the property - * @return PropertyReflection Property reflection object of the specified property in this class - */ - public function getProperty($name) - { - return new PropertyReflection($this->getName(), $name); - } - - /** - * Replacement for the original getInterfaces() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\ClassReflection objects are returned instead of the - * original ReflectionClass instances. - * - * @return ClassReflection[] Class reflection objects of the properties in this class - */ - public function getInterfaces() - { - $extendedInterfaces = []; - $interfaces = parent::getInterfaces(); - foreach ($interfaces as $interface) { - $extendedInterfaces[] = new self($interface->getName()); - } - return $extendedInterfaces; - } - - /** - * Replacement for the original getParentClass() method which makes sure - * that a \TYPO3\CMS\Extbase\Reflection\ClassReflection object is returned instead of the - * original ReflectionClass instance. - * - * @return ClassReflection Reflection of the parent class - if any - */ - public function getParentClass() - { - $parentClass = parent::getParentClass(); - return $parentClass === false ? false : new self($parentClass->getName()); - } - - /** - * Checks if the doc comment of this method is tagged with - * the specified tag - * - * @param string $tag Tag name to check for - * @return bool TRUE if such a tag has been defined, otherwise FALSE - */ - public function isTaggedWith($tag) - { - $result = $this->getDocCommentParser()->isTaggedWith($tag); - return $result; - } - - /** - * Returns an array of tags and their values - * - * @return array Tags and values - */ - public function getTagsValues() - { - return $this->getDocCommentParser()->getTagsValues(); - } - - /** - * Returns the values of the specified tag - * - * @param string $tag - * @return array Values of the given tag - */ - public function getTagValues($tag) - { - return $this->getDocCommentParser()->getTagValues($tag); - } - - /** - * Returns an instance of the doc comment parser and - * runs the parse() method. - * - * @return DocCommentParser - */ - protected function getDocCommentParser() - { - if (!is_object($this->docCommentParser)) { - $this->docCommentParser = new DocCommentParser(); - $this->docCommentParser->parseDocComment($this->getDocComment()); - } - return $this->docCommentParser; - } -} diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php b/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php index 5f8b37d5db01..e7ca5a3581bb 100644 --- a/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php +++ b/typo3/sysext/extbase/Classes/Reflection/ClassSchema.php @@ -14,11 +14,16 @@ namespace TYPO3\CMS\Extbase\Reflection; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\SingletonInterface; +use TYPO3\CMS\Core\Utility\ClassNamingUtility; +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; +use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject; use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility; /** * A class schema * + * @internal * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later */ class ClassSchema @@ -71,14 +76,256 @@ class ClassSchema */ protected $identityProperties = []; + /** + * Indicates if the class is a singleton or not. + * + * @var bool + */ + private $isSingleton; + + /** + * @var array + */ + private $methods; + + /** + * @var array + */ + protected static $ignoredTags = ['package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const']; + + /** + * @var array + */ + private $tags; + + /** + * @var array + */ + private $injectProperties = []; + + /** + * @var array + */ + private $injectMethods = []; + /** * Constructs this class schema * * @param string $className Name of the class this schema is referring to + * @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException + * @throws \ReflectionException */ public function __construct($className) { $this->className = $className; + + $reflectionClass = new \ReflectionClass($className); + + $this->isSingleton = $reflectionClass->implementsInterface(SingletonInterface::class); + + if ($reflectionClass->isSubclassOf(AbstractEntity::class)) { + $this->modelType = static::MODELTYPE_ENTITY; + + $possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className); + if (class_exists($possibleRepositoryClassName)) { + $this->setAggregateRoot(true); + } + } + + if ($reflectionClass->isSubclassOf(AbstractValueObject::class)) { + $this->modelType = static::MODELTYPE_VALUEOBJECT; + } + + $docCommentParser = new DocCommentParser(); + $docCommentParser->parseDocComment($reflectionClass->getDocComment()); + foreach ($docCommentParser->getTagsValues() as $tag => $values) { + if (in_array($tag, static::$ignoredTags, true)) { + continue; + } + + $this->tags[$tag] = $values; + } + + $this->reflectProperties($reflectionClass); + $this->reflectMethods($reflectionClass); + } + + /** + * @param \ReflectionClass $reflectionClass + */ + protected function reflectProperties(\ReflectionClass $reflectionClass) + { + foreach ($reflectionClass->getProperties() as $reflectionProperty) { + $propertyName = $reflectionProperty->getName(); + + $this->properties[$propertyName] = [ + 'default' => $reflectionProperty->isDefault(), + 'private' => $reflectionProperty->isPrivate(), + 'protected' => $reflectionProperty->isProtected(), + 'public' => $reflectionProperty->isPublic(), + 'static' => $reflectionProperty->isStatic(), + 'type' => null, // Extbase + 'elementType' => null, // Extbase + 'annotations' => [], + 'tags' => [] + ]; + + $docCommentParser = new DocCommentParser(); + $docCommentParser->parseDocComment($reflectionProperty->getDocComment()); + foreach ($docCommentParser->getTagsValues() as $tag => $values) { + if (in_array($tag, static::$ignoredTags, true)) { + continue; + } + + $this->properties[$propertyName]['tags'][$tag] = $values; + } + + $this->properties[$propertyName]['annotations']['inject'] = false; + $this->properties[$propertyName]['annotations']['lazy'] = $docCommentParser->isTaggedWith('lazy'); + $this->properties[$propertyName]['annotations']['transient'] = $docCommentParser->isTaggedWith('transient'); + $this->properties[$propertyName]['annotations']['type'] = null; + $this->properties[$propertyName]['annotations']['cascade'] = null; + $this->properties[$propertyName]['annotations']['dependency'] = null; + + if ($propertyName !== 'settings' && $docCommentParser->isTaggedWith('inject')) { + try { + $varValues = $docCommentParser->getTagValues('var'); + $this->properties[$propertyName]['annotations']['inject'] = true; + $this->properties[$propertyName]['annotations']['type'] = ltrim($varValues[0], '\\'); + $this->properties[$propertyName]['annotations']['dependency'] = ltrim($varValues[0], '\\'); + + $this->injectProperties[] = $propertyName; + } catch (\Exception $e) { + } + } + + if ($docCommentParser->isTaggedWith('var') && !$docCommentParser->isTaggedWith('transient')) { + try { + $cascadeAnnotationValues = $docCommentParser->getTagValues('cascade'); + $this->properties[$propertyName]['annotations']['cascade'] = $cascadeAnnotationValues[0]; + } catch (\Exception $e) { + } + + try { + $type = TypeHandlingUtility::parseType(implode(' ', $docCommentParser->getTagValues('var'))); + } catch (\Exception $e) { + $type = [ + 'type' => null, + 'elementType' => null + ]; + } + + $this->properties[$propertyName]['type'] = $type['type'] ? ltrim($type['type'], '\\') : null; + $this->properties[$propertyName]['elementType'] = $type['elementType'] ? ltrim($type['elementType'], '\\') : null; + } + + if ($docCommentParser->isTaggedWith('uuid')) { + $this->setUuidPropertyName($propertyName); + } + + if ($docCommentParser->isTaggedWith('identity')) { + $this->markAsIdentityProperty($propertyName); + } + } + } + + /** + * @param \ReflectionClass $reflectionClass + */ + protected function reflectMethods(\ReflectionClass $reflectionClass) + { + foreach ($reflectionClass->getMethods() as $reflectionMethod) { + $methodName = $reflectionMethod->getName(); + + $this->methods[$methodName] = []; + $this->methods[$methodName]['private'] = $reflectionMethod->isPrivate(); + $this->methods[$methodName]['protected'] = $reflectionMethod->isProtected(); + $this->methods[$methodName]['public'] = $reflectionMethod->isPublic(); + $this->methods[$methodName]['static'] = $reflectionMethod->isStatic(); + $this->methods[$methodName]['abstract'] = $reflectionMethod->isAbstract(); + $this->methods[$methodName]['params'] = []; + $this->methods[$methodName]['tags'] = []; + + $docCommentParser = new DocCommentParser(); + $docCommentParser->parseDocComment($reflectionMethod->getDocComment()); + foreach ($docCommentParser->getTagsValues() as $tag => $values) { + if (in_array($tag, static::$ignoredTags, true)) { + continue; + } + + $this->methods[$methodName]['tags'][$tag] = $values; + } + + $this->methods[$methodName]['description'] = $docCommentParser->getDescription(); + + foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) { + /* @var $reflectionParameter \ReflectionParameter */ + + $parameterName = $reflectionParameter->getName(); + + $this->methods[$methodName]['params'][$parameterName] = []; + $this->methods[$methodName]['params'][$parameterName]['position'] = $parameterPosition; // compat + $this->methods[$methodName]['params'][$parameterName]['byReference'] = $reflectionParameter->isPassedByReference(); // compat + $this->methods[$methodName]['params'][$parameterName]['array'] = $reflectionParameter->isArray(); // compat + $this->methods[$methodName]['params'][$parameterName]['optional'] = $reflectionParameter->isOptional(); + $this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionParameter->allowsNull(); // compat + $this->methods[$methodName]['params'][$parameterName]['class'] = null; // compat + $this->methods[$methodName]['params'][$parameterName]['type'] = null; + $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionParameter->allowsNull(); + $this->methods[$methodName]['params'][$parameterName]['default'] = null; + $this->methods[$methodName]['params'][$parameterName]['hasDefaultValue'] = $reflectionParameter->isDefaultValueAvailable(); + $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = null; // compat + $this->methods[$methodName]['params'][$parameterName]['dependency'] = null; // Extbase DI + + if ($reflectionParameter->isDefaultValueAvailable()) { + $this->methods[$methodName]['params'][$parameterName]['default'] = $reflectionParameter->getDefaultValue(); + $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = $reflectionParameter->getDefaultValue(); // compat + } + + if (($reflectionType = $reflectionParameter->getType()) instanceof \ReflectionType) { + $this->methods[$methodName]['params'][$parameterName]['type'] = (string)$reflectionType; + $this->methods[$methodName]['params'][$parameterName]['nullable'] = $reflectionType->allowsNull(); + } + + if (($parameterClass = $reflectionParameter->getClass()) instanceof \ReflectionClass) { + $this->methods[$methodName]['params'][$parameterName]['class'] = $parameterClass->getName(); + $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($parameterClass->getName(), '\\'); + } else { + $methodTagsAndValues = $this->methods[$methodName]['tags']; + if (isset($methodTagsAndValues['param'], $methodTagsAndValues['param'][$parameterPosition])) { + $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]); + if (count($explodedParameters) >= 2) { + if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) { + // ensure that short names of simple types are resolved correctly to the long form + // this is important for all kinds of type checks later on + $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]); + + $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($typeInfo['type'], '\\'); + } else { + $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($explodedParameters[0], '\\'); + } + } + } + } + + // Extbase DI + if ($reflectionParameter->getClass() instanceof \ReflectionClass + && ($reflectionMethod->isConstructor() || $this->hasInjectMethodName($reflectionMethod)) + ) { + $this->methods[$methodName]['params'][$parameterName]['dependency'] = $reflectionParameter->getClass()->getName(); + } + } + + // Extbase + $this->methods[$methodName]['injectMethod'] = false; + if ($this->hasInjectMethodName($reflectionMethod) + && count($this->methods[$methodName]['params']) === 1 + && reset($this->methods[$methodName]['params'])['dependency'] !== null + ) { + $this->methods[$methodName]['injectMethod'] = true; + $this->injectMethods[] = $methodName; + } + } } /** @@ -86,7 +333,7 @@ class ClassSchema * * @return string The class name */ - public function getClassName() + public function getClassName(): string { return $this->className; } @@ -98,9 +345,14 @@ class ClassSchema * @param string $type Type of the property * @param bool $lazy Whether the property should be lazy-loaded when reconstituting * @param string $cascade Strategy to cascade the object graph. + * @deprecated */ public function addProperty($name, $type, $lazy = false, $cascade = '') { + trigger_error( + 'This method will be removed in TYPO3 v10.0, properties will be automatically added on ClassSchema construction.', + E_USER_DEPRECATED + ); $type = TypeHandlingUtility::parseType($type); $this->properties[$name] = [ 'type' => $type['type'], @@ -137,9 +389,14 @@ class ClassSchema * * @param int $modelType The model type, one of the MODELTYPE_* constants. * @throws \InvalidArgumentException + * @deprecated */ public function setModelType($modelType) { + trigger_error( + 'This method will be removed in TYPO3 v10.0, modelType will be automatically set on ClassSchema construction.', + E_USER_DEPRECATED + ); if ($modelType < self::MODELTYPE_ENTITY || $modelType > self::MODELTYPE_VALUEOBJECT) { throw new \InvalidArgumentException('"' . $modelType . '" is an invalid model type.', 1212519195); } @@ -150,9 +407,14 @@ class ClassSchema * Returns the model type of the class this schema is referring to. * * @return int The model type, one of the MODELTYPE_* constants. + * @deprecated */ public function getModelType() { + trigger_error( + 'This method will be removed in TYPO3 v10.0.', + E_USER_DEPRECATED + ); return $this->modelType; } @@ -173,7 +435,7 @@ class ClassSchema * * @return bool TRUE if it is managed */ - public function isAggregateRoot() + public function isAggregateRoot(): bool { return $this->aggregateRoot; } @@ -184,7 +446,7 @@ class ClassSchema * @param string $propertyName Name of the property * @return bool */ - public function hasProperty($propertyName) + public function hasProperty($propertyName): bool { return array_key_exists($propertyName, $this->properties); } @@ -194,9 +456,14 @@ class ClassSchema * * @param string $propertyName * @throws \InvalidArgumentException + * @deprecated */ public function setUuidPropertyName($propertyName) { + trigger_error( + 'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.', + E_USER_DEPRECATED + ); if (!array_key_exists($propertyName, $this->properties)) { throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as UUID property.', 1233863842); } @@ -207,9 +474,14 @@ class ClassSchema * Gets the name of the property marked as uuid of an object * * @return string + * @deprecated */ public function getUuidPropertyName() { + trigger_error( + 'Tagging properties with @uuid is deprecated and will be removed in TYPO3 v10.0.', + E_USER_DEPRECATED + ); return $this->uuidPropertyName; } @@ -220,13 +492,18 @@ class ClassSchema * * @param string $propertyName * @throws \InvalidArgumentException + * @deprecated */ public function markAsIdentityProperty($propertyName) { + trigger_error( + 'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.', + E_USER_DEPRECATED + ); if (!array_key_exists($propertyName, $this->properties)) { throw new \InvalidArgumentException('Property "' . $propertyName . '" must be added to the class schema before it can be marked as identity property.', 1233775407); } - if ($this->properties[$propertyName]['lazy'] === true) { + if ($this->properties[$propertyName]['annotations']['lazy'] === true) { throw new \InvalidArgumentException('Property "' . $propertyName . '" must not be makred for lazy loading to be marked as identity property.', 1239896904); } $this->identityProperties[$propertyName] = $this->properties[$propertyName]['type']; @@ -237,9 +514,165 @@ class ClassSchema * * @return array * @see markAsIdentityProperty() + * @deprecated */ public function getIdentityProperties() { + trigger_error( + 'Tagging properties with @identity is deprecated and will be removed in TYPO3 v10.0.', + E_USER_DEPRECATED + ); return $this->identityProperties; } + + /** + * @return bool + */ + public function hasConstructor(): bool + { + return isset($this->methods['__construct']); + } + + /** + * @param string $name + * @return array + */ + public function getMethod(string $name): array + { + return $this->methods[$name] ?? []; + } + + /** + * @return array + */ + public function getMethods(): array + { + return $this->methods; + } + + /** + * @param \ReflectionMethod $reflectionMethod + * @return bool + */ + protected function hasInjectMethodName(\ReflectionMethod $reflectionMethod): bool + { + $methodName = $reflectionMethod->getName(); + if ($methodName === 'injectSettings' || !$reflectionMethod->isPublic()) { + return false; + } + + if ( + strpos($reflectionMethod->getName(), 'inject') === 0 + ) { + return true; + } + + return false; + } + + /** + * @return bool + * @internal + */ + public function isModel(): bool + { + return $this->isEntity() || $this->isValueObject(); + } + + /** + * @return bool + * @internal + */ + public function isEntity(): bool + { + return $this->modelType === static::MODELTYPE_ENTITY; + } + + /** + * @return bool + * @internal + */ + public function isValueObject(): bool + { + return $this->modelType === static::MODELTYPE_VALUEOBJECT; + } + + /** + * @return bool + */ + public function isSingleton(): bool + { + return $this->isSingleton; + } + + /** + * @param string $methodName + * @return bool + */ + public function hasMethod(string $methodName): bool + { + return isset($this->methods[$methodName]); + } + + /** + * @return array + */ + public function getTags(): array + { + return $this->tags; + } + + /** + * @return bool + */ + public function hasInjectProperties(): bool + { + return count($this->injectProperties) > 0; + } + + /** + * @return bool + */ + public function hasInjectMethods(): bool + { + return count($this->injectMethods) > 0; + } + + /** + * @return array + */ + public function getInjectMethods(): array + { + $injectMethods = []; + foreach ($this->injectMethods as $injectMethodName) { + $injectMethods[$injectMethodName] = reset($this->methods[$injectMethodName]['params'])['dependency']; + } + + return $injectMethods; + } + + /** + * @return array + */ + public function getInjectProperties(): array + { + $injectProperties = []; + foreach ($this->injectProperties as $injectPropertyName) { + $injectProperties[$injectPropertyName] = $this->properties[$injectPropertyName]['annotations']['dependency']; + } + + return $injectProperties; + } + + /** + * @return array + */ + public function getConstructorArguments(): array + { + if (!$this->hasConstructor()) { + return []; + } + + return $this->methods['__construct']['params']; + } } diff --git a/typo3/sysext/extbase/Classes/Reflection/MethodReflection.php b/typo3/sysext/extbase/Classes/Reflection/MethodReflection.php deleted file mode 100644 index fbd00f506148..000000000000 --- a/typo3/sysext/extbase/Classes/Reflection/MethodReflection.php +++ /dev/null @@ -1,111 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Reflection; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Extended version of the ReflectionMethod - */ -class MethodReflection extends \ReflectionMethod -{ - /** - * @var DocCommentParser An instance of the doc comment parser - */ - protected $docCommentParser; - - /** - * Returns the declaring class - * - * @return ClassReflection The declaring class - */ - public function getDeclaringClass() - { - return new ClassReflection(parent::getDeclaringClass()->getName()); - } - - /** - * Replacement for the original getParameters() method which makes sure - * that \TYPO3\CMS\Extbase\Reflection\ParameterReflection objects are returned instead of the - * original ReflectionParameter instances. - * - * @return ParameterReflection[] Parameter reflection objects of the parameters of this method - */ - public function getParameters() - { - $extendedParameters = []; - foreach (parent::getParameters() as $parameter) { - $extendedParameters[] = new ParameterReflection([$this->getDeclaringClass()->getName(), $this->getName()], $parameter->getName()); - } - return $extendedParameters; - } - - /** - * Checks if the doc comment of this method is tagged with - * the specified tag - * - * @param string $tag Tag name to check for - * @return bool TRUE if such a tag has been defined, otherwise FALSE - */ - public function isTaggedWith($tag) - { - $result = $this->getDocCommentParser()->isTaggedWith($tag); - return $result; - } - - /** - * Returns an array of tags and their values - * - * @return array Tags and values - */ - public function getTagsValues() - { - return $this->getDocCommentParser()->getTagsValues(); - } - - /** - * Returns the values of the specified tag - * - * @param string $tag Tag name to check for - * @return array Values of the given tag - */ - public function getTagValues($tag) - { - return $this->getDocCommentParser()->getTagValues($tag); - } - - /** - * Returns the description part of the doc comment - * - * @return string Doc comment description - */ - public function getDescription() - { - return $this->getDocCommentParser()->getDescription(); - } - - /** - * Returns an instance of the doc comment parser and - * runs the parse() method. - * - * @return DocCommentParser - */ - protected function getDocCommentParser() - { - if (!is_object($this->docCommentParser)) { - $this->docCommentParser = new DocCommentParser(); - $this->docCommentParser->parseDocComment($this->getDocComment()); - } - return $this->docCommentParser; - } -} diff --git a/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php b/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php index 3152ff5c4b57..1d9c50082758 100644 --- a/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php +++ b/typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php @@ -96,7 +96,11 @@ class ObjectAccess } elseif (is_object($subject)) { if ($forceDirectAccess) { if (property_exists($subject, $propertyName)) { - $propertyReflection = new PropertyReflection($subject, $propertyName); + $propertyReflection = new \ReflectionProperty($subject, $propertyName); + if ($propertyReflection->isPublic()) { + return $propertyReflection->getValue($subject); + } + $propertyReflection->setAccessible(true); return $propertyReflection->getValue($subject); } throw new Exception\PropertyNotAccessibleException('The property "' . $propertyName . '" on the subject does not exist.', 1302855001); @@ -183,7 +187,7 @@ class ObjectAccess $result = true; if ($forceDirectAccess) { if (property_exists($subject, $propertyName)) { - $propertyReflection = new PropertyReflection($subject, $propertyName); + $propertyReflection = new \ReflectionProperty($subject, $propertyName); $propertyReflection->setAccessible(true); $propertyReflection->setValue($subject, $propertyValue); } else { @@ -195,7 +199,7 @@ class ObjectAccess if (is_callable([$subject, $setterMethodName])) { $subject->{$setterMethodName}($propertyValue); } elseif (property_exists($subject, $propertyName)) { - $reflection = new PropertyReflection($subject, $propertyName); + $reflection = new \ReflectionProperty($subject, $propertyName); if ($reflection->isPublic()) { $subject->{$propertyName} = $propertyValue; } else { @@ -348,7 +352,7 @@ class ObjectAccess return true; } if (property_exists($object, $propertyName)) { - $propertyReflection = new PropertyReflection($object, $propertyName); + $propertyReflection = new \ReflectionProperty($object, $propertyName); return $propertyReflection->isPublic(); } return false; diff --git a/typo3/sysext/extbase/Classes/Reflection/ParameterReflection.php b/typo3/sysext/extbase/Classes/Reflection/ParameterReflection.php deleted file mode 100644 index 7dc7fd41c4ef..000000000000 --- a/typo3/sysext/extbase/Classes/Reflection/ParameterReflection.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Reflection; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Extended version of the ReflectionParameter - */ -class ParameterReflection extends \ReflectionParameter -{ - /** - * Returns the declaring class - * - * @return ClassReflection The declaring class - */ - public function getDeclaringClass() - { - return new ClassReflection(parent::getDeclaringClass()->getName()); - } - - /** - * Returns the parameter class - * - * @return ClassReflection The parameter class - */ - public function getClass() - { - try { - $class = parent::getClass(); - } catch (\Exception $e) { - return null; - } - return is_object($class) ? new ClassReflection($class->getName()) : null; - } -} diff --git a/typo3/sysext/extbase/Classes/Reflection/PropertyReflection.php b/typo3/sysext/extbase/Classes/Reflection/PropertyReflection.php deleted file mode 100644 index d107dbb16d06..000000000000 --- a/typo3/sysext/extbase/Classes/Reflection/PropertyReflection.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Reflection; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -/** - * Extended version of the ReflectionProperty - */ -class PropertyReflection extends \ReflectionProperty -{ - /** - * @var DocCommentParser An instance of the doc comment parser - */ - protected $docCommentParser; - - /** - * Checks if the doc comment of this property is tagged with - * the specified tag - * - * @param string $tag Tag name to check for - * @return bool TRUE if such a tag has been defined, otherwise FALSE - */ - public function isTaggedWith($tag) - { - $result = $this->getDocCommentParser()->isTaggedWith($tag); - return $result; - } - - /** - * Returns an array of tags and their values - * - * @return array Tags and values - */ - public function getTagsValues() - { - return $this->getDocCommentParser()->getTagsValues(); - } - - /** - * Returns the values of the specified tag - * - * @param string $tag - * @return array Values of the given tag - */ - public function getTagValues($tag) - { - return $this->getDocCommentParser()->getTagValues($tag); - } - - /** - * Returns the value of the reflected property - even if it is protected. - * - * @param object $object Instance of the declaring class \TYPO3\CMS\Extbase\Reflection to read the value from - * @return mixed Value of the property - * @throws Exception - * @todo Maybe support private properties as well, as of PHP 5.3.0 we can do - */ - public function getValue($object = null) - { - if (!is_object($object)) { - throw new Exception('$object is of type ' . gettype($object) . ', instance of class ' . $this->class . ' expected.', 1210859212); - } - if ($this->isPublic()) { - return parent::getValue($object); - } - if ($this->isPrivate()) { - throw new Exception('Cannot return value of private property "' . $this->name . '.', 1210859206); - } - parent::setAccessible(true); - return parent::getValue($object); - } - - /** - * Returns an instance of the doc comment parser and - * runs the parse() method. - * - * @return DocCommentParser - */ - protected function getDocCommentParser() - { - if (!is_object($this->docCommentParser)) { - $this->docCommentParser = new DocCommentParser(); - $this->docCommentParser->parseDocComment($this->getDocComment()); - } - return $this->docCommentParser; - } -} diff --git a/typo3/sysext/extbase/Classes/Reflection/ReflectionService.php b/typo3/sysext/extbase/Classes/Reflection/ReflectionService.php index 06a284282f04..14957f4f2d13 100644 --- a/typo3/sysext/extbase/Classes/Reflection/ReflectionService.php +++ b/typo3/sysext/extbase/Classes/Reflection/ReflectionService.php @@ -14,114 +14,31 @@ namespace TYPO3\CMS\Extbase\Reflection; * The TYPO3 project - inspiring people to share! */ -use TYPO3\CMS\Core\Utility\ClassNamingUtility; -use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility; +use TYPO3\CMS\Core\Cache\CacheManager; +use TYPO3\CMS\Core\SingletonInterface; /** - * A backport of the TYPO3.Flow reflection service for acquiring reflection based information. - * Most of the code is based on the TYPO3.Flow reflection service. + * Reflection service for acquiring reflection based information. + * Originally based on the TYPO3.Flow reflection service. * * @api */ -class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface +class ReflectionService implements SingletonInterface { - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface - */ - protected $objectManager; - - /** - * Whether this service has been initialized. - * - * @var bool - */ - protected $initialized = false; + const CACHE_IDENTIFIER = 'extbase_reflection'; + const CACHE_ENTRY_IDENTIFIER = 'ClassSchematas'; /** * @var \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend */ protected $dataCache; - /** - * Whether class alterations should be detected on each initialization. - * - * @var bool - */ - protected $detectClassChanges = false; - - /** - * All available class names to consider. Class name = key, value is the - * UNIX timestamp the class was reflected. - * - * @var array - */ - protected $reflectedClassNames = []; - - /** - * Array of tags and the names of classes which are tagged with them. - * - * @var array - */ - protected $taggedClasses = []; - - /** - * Array of class names and their tags and values. - * - * @var array - */ - protected $classTagsValues = []; - - /** - * Array of class names, method names and their tags and values. - * - * @var array - */ - protected $methodTagsValues = []; - - /** - * Array of class names, method names, their parameters and additional - * information about the parameters. - * - * @var array - */ - protected $methodParameters = []; - - /** - * Array of class names and names of their properties. - * - * @var array - */ - protected $classPropertyNames = []; - - /** - * Array of class names and names of their methods. - * - * @var array - */ - protected $classMethodNames = []; - - /** - * Array of class names, property names and their tags and values. - * - * @var array - */ - protected $propertyTagsValues = []; - - /** - * List of tags which are ignored while reflecting class and method annotations. - * - * @var array - */ - protected $ignoredTags = ['package', 'subpackage', 'license', 'copyright', 'author', 'version', 'const']; - /** * Indicates whether the Reflection cache needs to be updated. * * This flag needs to be set as soon as new Reflection information was * created. * - * @see reflectClass() - * @see getMethodReflection() * @var bool */ protected $dataCacheNeedsUpdate = false; @@ -134,85 +51,33 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface protected $classSchemata = []; /** - * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface - */ - protected $configurationManager; - - /** - * @var string - */ - protected $cacheIdentifier; - - /** - * Internal runtime cache of method reflection objects - * - * @var array - */ - protected $methodReflections = []; - - /** - * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager + * @var bool */ - public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) - { - $this->objectManager = $objectManager; - } + private $cachingEnabled = false; /** - * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager - */ - public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) - { - $this->configurationManager = $configurationManager; - } - - /** - * Sets the data cache. - * - * The cache must be set before initializing the Reflection Service. + * If not $cacheManager is injected, the reflection service does not + * cache any data, useful for testing this service in unit tests. * - * @param \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $dataCache Cache for the Reflection service + * @param CacheManager $cacheManager */ - public function setDataCache(\TYPO3\CMS\Core\Cache\Frontend\VariableFrontend $dataCache) + public function __construct(\TYPO3\CMS\Core\Cache\CacheManager $cacheManager = null) { - $this->dataCache = $dataCache; - } + if ($cacheManager instanceof CacheManager && $cacheManager->hasCache(static::CACHE_IDENTIFIER)) { + $this->cachingEnabled = true; + $this->dataCache = $cacheManager->getCache(static::CACHE_IDENTIFIER); - /** - * Initializes this service - * - * @throws Exception - */ - public function initialize() - { - if ($this->initialized) { - throw new Exception('The Reflection Service can only be initialized once.', 1232044696); + if (($classSchemata = $this->dataCache->has(static::CACHE_ENTRY_IDENTIFIER)) !== false) { + $this->classSchemata = $this->dataCache->get(static::CACHE_ENTRY_IDENTIFIER); + } } - $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); - $this->cacheIdentifier = 'ReflectionData_' . $frameworkConfiguration['extensionName']; - $this->loadFromCache(); - $this->initialized = true; } - /** - * Returns whether the Reflection Service is initialized. - * - * @return bool true if the Reflection Service is initialized, otherwise false - */ - public function isInitialized() - { - return $this->initialized; - } - - /** - * Shuts the Reflection Service down. - */ - public function shutdown() + public function __destruct() { - if ($this->dataCacheNeedsUpdate) { - $this->saveToCache(); + if ($this->dataCacheNeedsUpdate && $this->cachingEnabled) { + $this->dataCache->set(static::CACHE_ENTRY_IDENTIFIER, $this->classSchemata); } - $this->initialized = false; } /** @@ -221,15 +86,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $className Name of the class * @return array An array of tags and their values or an empty array if no tags were found */ - public function getClassTagsValues($className) + public function getClassTagsValues($className): array { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->classTagsValues[$className])) { - return []; - } - return isset($this->classTagsValues[$className]) ? $this->classTagsValues[$className] : []; + return $this->getClassSchema($className)->getTags(); } /** @@ -239,15 +98,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $tag Tag to return the values of * @return array An array of values or an empty array if the tag was not found */ - public function getClassTagValues($className, $tag) + public function getClassTagValues($className, $tag): array { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->classTagsValues[$className])) { - return []; - } - return isset($this->classTagsValues[$className][$tag]) ? $this->classTagsValues[$className][$tag] : []; + return $this->getClassSchema($className)->getTags()[$tag]; } /** @@ -256,12 +109,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $className Name of the class to return the property names of * @return array An array of property names or an empty array if none exist */ - public function getClassPropertyNames($className) + public function getClassPropertyNames($className): array { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - return isset($this->classPropertyNames[$className]) ? $this->classPropertyNames[$className] : []; + return array_keys($this->getClassSchema($className)->getProperties()); } /** @@ -269,13 +119,16 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * * @param mixed $classNameOrObject The class name or an object * @return ClassSchema + * @throws \TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException + * @throws \ReflectionException */ - public function getClassSchema($classNameOrObject) + public function getClassSchema($classNameOrObject): ClassSchema { $className = is_object($classNameOrObject) ? get_class($classNameOrObject) : $classNameOrObject; if (isset($this->classSchemata[$className])) { return $this->classSchemata[$className]; } + return $this->buildClassSchema($className); } @@ -286,18 +139,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $methodName Name of the method * @return bool */ - public function hasMethod($className, $methodName) + public function hasMethod($className, $methodName): bool { - try { - if (!array_key_exists($className, $this->classMethodNames) || !array_key_exists($methodName, $this->classMethodNames[$className])) { - $this->getMethodReflection($className, $methodName); - $this->classMethodNames[$className][$methodName] = true; - } - } catch (\ReflectionException $e) { - // Method does not exist. Store this information in cache. - $this->classMethodNames[$className][$methodName] = null; - } - return isset($this->classMethodNames[$className][$methodName]); + return $this->getClassSchema($className)->hasMethod($methodName); } /** @@ -307,18 +151,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $methodName Name of the method to return the tags and values of * @return array An array of tags and their values or an empty array of no tags were found */ - public function getMethodTagsValues($className, $methodName) + public function getMethodTagsValues($className, $methodName): array { - if (!isset($this->methodTagsValues[$className][$methodName])) { - $method = $this->getMethodReflection($className, $methodName); - $this->methodTagsValues[$className][$methodName] = []; - foreach ($method->getTagsValues() as $tag => $values) { - if (array_search($tag, $this->ignoredTags) === false) { - $this->methodTagsValues[$className][$methodName][$tag] = $values; - } - } - } - return $this->methodTagsValues[$className][$methodName]; + return $this->getClassSchema($className)->getMethod($methodName)['tags']; } /** @@ -329,16 +164,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $methodName Name of the method to return parameter information of * @return array An array of parameter names and additional information or an empty array of no parameters were found */ - public function getMethodParameters($className, $methodName) + public function getMethodParameters($className, $methodName): array { - if (!isset($this->methodParameters[$className][$methodName])) { - $method = $this->getMethodReflection($className, $methodName); - $this->methodParameters[$className][$methodName] = []; - foreach ($method->getParameters() as $parameterPosition => $parameter) { - $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method); - } - } - return $this->methodParameters[$className][$methodName]; + return $this->getClassSchema($className)->getMethod($methodName)['params']; } /** @@ -348,15 +176,9 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $propertyName Name of the property to return the tags and values of * @return array An array of tags and their values or an empty array of no tags were found */ - public function getPropertyTagsValues($className, $propertyName) + public function getPropertyTagsValues($className, $propertyName): array { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->propertyTagsValues[$className])) { - return []; - } - return isset($this->propertyTagsValues[$className][$propertyName]) ? $this->propertyTagsValues[$className][$propertyName] : []; + return $this->getClassSchema($className)->getProperty($propertyName)['tags']; } /** @@ -367,27 +189,13 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $tag Tag to return the values of * @return array An array of values or an empty array if the tag was not found */ - public function getPropertyTagValues($className, $propertyName, $tag) + public function getPropertyTagValues($className, $propertyName, $tag): array { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->propertyTagsValues[$className][$propertyName])) { + if (!$this->isPropertyTaggedWith($className, $propertyName, $tag)) { return []; } - return isset($this->propertyTagsValues[$className][$propertyName][$tag]) ? $this->propertyTagsValues[$className][$propertyName][$tag] : []; - } - /** - * Tells if the specified class is known to this reflection service and - * reflection information is available. - * - * @param string $className Name of the class - * @return bool If the class is reflected by this service - */ - public function isClassReflected($className) - { - return isset($this->reflectedClassNames[$className]); + return $this->getClassSchema($className)->getProperty($propertyName)['tags'][$tag]; } /** @@ -397,18 +205,15 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $tag Tag to check for * @return bool TRUE if the class is tagged with $tag, otherwise FALSE */ - public function isClassTaggedWith($className, $tag) + public function isClassTaggedWith($className, $tag): bool { - if ($this->initialized === false) { - return false; - } - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->classTagsValues[$className])) { - return false; + foreach (array_keys($this->getClassSchema($className)->getTags()) as $tagName) { + if ($tagName === $tag) { + return true; + } } - return isset($this->classTagsValues[$className][$tag]); + + return false; } /** @@ -419,57 +224,21 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $tag Tag to check for * @return bool TRUE if the class property is tagged with $tag, otherwise FALSE */ - public function isPropertyTaggedWith($className, $propertyName, $tag) + public function isPropertyTaggedWith($className, $propertyName, $tag): bool { - if (!isset($this->reflectedClassNames[$className])) { - $this->reflectClass($className); - } - if (!isset($this->propertyTagsValues[$className])) { + try { + $classSchema = $this->getClassSchema($className); + } catch (\Exception $e) { return false; } - if (!isset($this->propertyTagsValues[$className][$propertyName])) { + + $property = $classSchema->getProperty($propertyName); + + if (empty($property)) { return false; } - return isset($this->propertyTagsValues[$className][$propertyName][$tag]); - } - /** - * Reflects the given class and stores the results in this service's properties. - * - * @param string $className Full qualified name of the class to reflect - */ - protected function reflectClass($className) - { - $class = new ClassReflection($className); - $this->reflectedClassNames[$className] = time(); - foreach ($class->getTagsValues() as $tag => $values) { - if (array_search($tag, $this->ignoredTags) === false) { - $this->taggedClasses[$tag][] = $className; - $this->classTagsValues[$className][$tag] = $values; - } - } - foreach ($class->getProperties() as $property) { - $propertyName = $property->getName(); - $this->classPropertyNames[$className][] = $propertyName; - foreach ($property->getTagsValues() as $tag => $values) { - if (array_search($tag, $this->ignoredTags) === false) { - $this->propertyTagsValues[$className][$propertyName][$tag] = $values; - } - } - } - foreach ($class->getMethods() as $method) { - $methodName = $method->getName(); - foreach ($method->getTagsValues() as $tag => $values) { - if (array_search($tag, $this->ignoredTags) === false) { - $this->methodTagsValues[$className][$methodName][$tag] = $values; - } - } - foreach ($method->getParameters() as $parameterPosition => $parameter) { - $this->methodParameters[$className][$methodName][$parameter->getName()] = $this->convertParameterReflectionToArray($parameter, $parameterPosition, $method); - } - } - ksort($this->reflectedClassNames); - $this->dataCacheNeedsUpdate = true; + return isset($property['tags'][$tag]); } /** @@ -478,140 +247,17 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface * @param string $className * @throws Exception\UnknownClassException * @return ClassSchema The class schema + * @throws \ReflectionException */ - protected function buildClassSchema($className) + protected function buildClassSchema($className): ClassSchema { if (!class_exists($className)) { throw new Exception\UnknownClassException('The classname "' . $className . '" was not found and thus can not be reflected.', 1278450972); } - $classSchema = $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, $className); - if (is_subclass_of($className, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class)) { - $classSchema->setModelType(ClassSchema::MODELTYPE_ENTITY); - $possibleRepositoryClassName = ClassNamingUtility::translateModelNameToRepositoryName($className); - if (class_exists($possibleRepositoryClassName)) { - $classSchema->setAggregateRoot(true); - } - } elseif (is_subclass_of($className, \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class)) { - $classSchema->setModelType(ClassSchema::MODELTYPE_VALUEOBJECT); - } - foreach ($this->getClassPropertyNames($className) as $propertyName) { - if (!$this->isPropertyTaggedWith($className, $propertyName, 'transient') && $this->isPropertyTaggedWith($className, $propertyName, 'var')) { - $cascadeTagValues = $this->getPropertyTagValues($className, $propertyName, 'cascade'); - $classSchema->addProperty($propertyName, implode(' ', $this->getPropertyTagValues($className, $propertyName, 'var')), $this->isPropertyTaggedWith($className, $propertyName, 'lazy'), $cascadeTagValues[0]); - } - if ($this->isPropertyTaggedWith($className, $propertyName, 'uuid')) { - $classSchema->setUuidPropertyName($propertyName); - } - if ($this->isPropertyTaggedWith($className, $propertyName, 'identity')) { - $classSchema->markAsIdentityProperty($propertyName); - } - } + + $classSchema = new ClassSchema($className); $this->classSchemata[$className] = $classSchema; $this->dataCacheNeedsUpdate = true; return $classSchema; } - - /** - * Converts the given parameter reflection into an information array - * - * @param ParameterReflection $parameter The parameter to reflect - * @param int $parameterPosition - * @param MethodReflection|NULL $method - * @return array Parameter information array - */ - protected function convertParameterReflectionToArray(ParameterReflection $parameter, $parameterPosition, MethodReflection $method = null) - { - $parameterInformation = [ - 'position' => $parameterPosition, - 'byReference' => $parameter->isPassedByReference(), - 'array' => $parameter->isArray(), - 'optional' => $parameter->isOptional(), - 'allowsNull' => $parameter->allowsNull() - ]; - $parameterClass = $parameter->getClass(); - $parameterInformation['class'] = $parameterClass !== null ? $parameterClass->getName() : null; - if ($parameter->isDefaultValueAvailable()) { - $parameterInformation['defaultValue'] = $parameter->getDefaultValue(); - } - if ($parameterClass !== null) { - $parameterInformation['type'] = $parameterClass->getName(); - } elseif ($method !== null) { - $methodTagsAndValues = $this->getMethodTagsValues($method->getDeclaringClass()->getName(), $method->getName()); - if (isset($methodTagsAndValues['param']) && isset($methodTagsAndValues['param'][$parameterPosition])) { - $explodedParameters = explode(' ', $methodTagsAndValues['param'][$parameterPosition]); - if (count($explodedParameters) >= 2) { - if (TypeHandlingUtility::isSimpleType($explodedParameters[0])) { - // ensure that short names of simple types are resolved correctly to the long form - // this is important for all kinds of type checks later on - $typeInfo = TypeHandlingUtility::parseType($explodedParameters[0]); - $parameterInformation['type'] = $typeInfo['type']; - } else { - $parameterInformation['type'] = $explodedParameters[0]; - } - } - } - } - if (isset($parameterInformation['type']) && $parameterInformation['type'][0] === '\\') { - $parameterInformation['type'] = substr($parameterInformation['type'], 1); - } - return $parameterInformation; - } - - /** - * Returns the Reflection of a method. - * - * @param string $className Name of the class containing the method - * @param string $methodName Name of the method to return the Reflection for - * @return MethodReflection the method Reflection object - */ - protected function getMethodReflection($className, $methodName) - { - $this->dataCacheNeedsUpdate = true; - if (!isset($this->methodReflections[$className][$methodName])) { - $this->methodReflections[$className][$methodName] = new MethodReflection($className, $methodName); - } - return $this->methodReflections[$className][$methodName]; - } - - /** - * Tries to load the reflection data from this service's cache. - */ - protected function loadFromCache() - { - $data = $this->dataCache->get($this->cacheIdentifier); - if ($data !== false) { - foreach ($data as $propertyName => $propertyValue) { - $this->{$propertyName} = $propertyValue; - } - } - } - - /** - * Exports the internal reflection data into the ReflectionData cache. - * - * @throws Exception - */ - protected function saveToCache() - { - if (!is_object($this->dataCache)) { - throw new Exception('A cache must be injected before initializing the Reflection Service.', 1232044697); - } - $data = []; - $propertyNames = [ - 'reflectedClassNames', - 'classPropertyNames', - 'classMethodNames', - 'classTagsValues', - 'methodTagsValues', - 'methodParameters', - 'propertyTagsValues', - 'taggedClasses', - 'classSchemata' - ]; - foreach ($propertyNames as $propertyName) { - $data[$propertyName] = $this->{$propertyName}; - } - $this->dataCache->set($this->cacheIdentifier, $data); - $this->dataCacheNeedsUpdate = false; - } } diff --git a/typo3/sysext/extbase/Classes/Scheduler/TaskExecutor.php b/typo3/sysext/extbase/Classes/Scheduler/TaskExecutor.php index cace85720345..85d38be94039 100644 --- a/typo3/sysext/extbase/Classes/Scheduler/TaskExecutor.php +++ b/typo3/sysext/extbase/Classes/Scheduler/TaskExecutor.php @@ -108,12 +108,6 @@ class TaskExecutor implements \TYPO3\CMS\Core\SingletonInterface } } } - // initialize reflection - $reflectionService = $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); - $reflectionService->setDataCache(\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('extbase_reflection')); - if (!$reflectionService->isInitialized()) { - $reflectionService->initialize(); - } } /** @@ -144,10 +138,7 @@ class TaskExecutor implements \TYPO3\CMS\Core\SingletonInterface */ protected function shutdown() { - // shutdown $persistenceManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class); $persistenceManager->persistAll(); - $reflectionService = $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); - $reflectionService->shutdown(); } } diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/CommandTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/CommandTest.php index c6bb34dcf4d4..eb0471690786 100644 --- a/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/CommandTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/CommandTest.php @@ -21,35 +21,32 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\Cli; * The TYPO3 project - inspiring people to share! * * */ +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Cli\Command; +use TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Tests\Unit\Mvc\Cli\Fixture\Command\MockCCommandController; + /** * Test case */ class CommandTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase { /** - * @var \TYPO3\CMS\Extbase\Mvc\Cli\Command|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface - */ - protected $command; - - /** - * @var \TYPO3\CMS\Extbase\Reflection\MethodReflection + * @var array */ - protected $mockMethodReflection; + protected $singletonInstances; - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface - */ - protected $mockObjectManager; - - /** - */ protected function setUp() { - $this->command = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Mvc\Cli\Command::class, ['getCommandMethodReflection'], [], '', false); - $this->mockMethodReflection = $this->createMock(\TYPO3\CMS\Extbase\Reflection\MethodReflection::class); - $this->command->expects($this->any())->method('getCommandMethodReflection')->will($this->returnValue($this->mockMethodReflection)); - $this->mockObjectManager = $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class); - $this->command->_set('objectManager', $this->mockObjectManager); + $this->singletonInstances = GeneralUtility::getSingletonInstances(); + GeneralUtility::purgeInstances(); + } + + protected function tearDown() + { + GeneralUtility::resetSingletonInstances($this->singletonInstances); + parent::tearDown(); } /** @@ -101,52 +98,167 @@ class CommandTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase new \TYPO3\CMS\Extbase\Mvc\Cli\Command($controllerClassName, 'foo'); } - /** - * @test - */ - public function hasArgumentsReturnsFalseIfCommandExpectsNoArguments() + public function testIsInternal() { - $this->mockMethodReflection->expects($this->atLeastOnce())->method('getParameters')->will($this->returnValue([])); - $this->assertFalse($this->command->hasArguments()); + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertFalse($commandController->isInternal()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'internal' + ); + + static::assertTrue($commandController->isInternal()); } - /** - * @test - */ - public function hasArgumentsReturnsTrueIfCommandExpectsArguments() + public function testIsCliOnly() { - $mockParameterReflection = $this->createMock(\TYPO3\CMS\Extbase\Reflection\ParameterReflection::class); - $this->mockMethodReflection->expects($this->atLeastOnce())->method('getParameters')->will($this->returnValue([$mockParameterReflection])); - $this->assertTrue($this->command->hasArguments()); + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertFalse($commandController->isCliOnly()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'cliOnly' + ); + + static::assertTrue($commandController->isCliOnly()); } - /** - * @test - */ - public function getArgumentDefinitionsReturnsEmptyArrayIfCommandExpectsNoArguments() + public function testIsFlushinCaches() { - $this->mockMethodReflection->expects($this->atLeastOnce())->method('getParameters')->will($this->returnValue([])); - $this->assertSame([], $this->command->getArgumentDefinitions()); + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertFalse($commandController->isFlushingCaches()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'flushingCaches' + ); + + static::assertTrue($commandController->isFlushingCaches()); } - /** - * @test - */ - public function getArgumentDefinitionsReturnsArrayOfArgumentDefinitionIfCommandExpectsArguments() + public function testHasArguments() + { + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertFalse($commandController->hasArguments()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'withArguments' + ); + + static::assertTrue($commandController->hasArguments()); + } + + public function testGetArgumentDefinitions() + { + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertSame([], $commandController->getArgumentDefinitions()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'withArguments' + ); + + $expected = [ + new CommandArgumentDefinition('foo', true, 'FooParamDescription'), + new CommandArgumentDefinition('bar', false, 'BarParamDescription'), + ]; + + static::assertEquals($expected, $commandController->getArgumentDefinitions()); + } + + public function testGetDescription() + { + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertSame('', $commandController->getDescription()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'withDescription' + ); + + $expected = 'Longer Description' . PHP_EOL . + 'Multine' . PHP_EOL . PHP_EOL . + 'Much Multiline'; + + static::assertEquals($expected, $commandController->getDescription()); + } + + public function testGetShortDescription() + { + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertSame('', $commandController->getShortDescription()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'withDescription' + ); + + $expected = 'Short Description'; + + static::assertEquals($expected, $commandController->getShortDescription()); + } + + public function testGetRelatedCommandIdentifiers() { - $mockParameterReflection = $this->createMock(\TYPO3\CMS\Extbase\Reflection\ParameterReflection::class); - $mockReflectionService = $this->createMock(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); - $mockMethodParameters = ['argument1' => ['optional' => false], 'argument2' => ['optional' => true]]; - $mockReflectionService->expects($this->atLeastOnce())->method('getMethodParameters')->will($this->returnValue($mockMethodParameters)); - $this->command->_set('reflectionService', $mockReflectionService); - $this->mockMethodReflection->expects($this->atLeastOnce())->method('getParameters')->will($this->returnValue([$mockParameterReflection])); - $this->mockMethodReflection->expects($this->atLeastOnce())->method('getTagsValues')->will($this->returnValue(['param' => ['@param $argument1 argument1 description', '@param $argument2 argument2 description']])); - $mockCommandArgumentDefinition1 = $this->createMock(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class); - $mockCommandArgumentDefinition2 = $this->createMock(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class); - $this->mockObjectManager->expects($this->at(0))->method('get')->with(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class, 'argument1', true, 'argument1 description')->will($this->returnValue($mockCommandArgumentDefinition1)); - $this->mockObjectManager->expects($this->at(1))->method('get')->with(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition::class, 'argument2', false, 'argument2 description')->will($this->returnValue($mockCommandArgumentDefinition2)); - $expectedResult = [$mockCommandArgumentDefinition1, $mockCommandArgumentDefinition2]; - $actualResult = $this->command->getArgumentDefinitions(); - $this->assertSame($expectedResult, $actualResult); + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'empty' + ); + + static::assertSame([], $commandController->getRelatedCommandIdentifiers()); + + $commandController = GeneralUtility::makeInstance(ObjectManager::class)->get( + Command::class, + MockCCommandController::class, + 'relatedCommandIdentifiers' + ); + + $expected = ['Foo:Bar:Baz']; + static::assertEquals($expected, $commandController->getRelatedCommandIdentifiers()); } } diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/Fixture/Command/MockCCommandController.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/Fixture/Command/MockCCommandController.php new file mode 100644 index 000000000000..463c0b416c80 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Cli/Fixture/Command/MockCCommandController.php @@ -0,0 +1,60 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Mvc\Cli\Fixture\Command; + +/** + * Another mock CLI Command + */ +class MockCCommandController extends \TYPO3\CMS\Extbase\Mvc\Cli\Command +{ + public function emptyCommand() + { + } + + /** + * @internal + */ + public function internalCommand() + { + } + + /** + * @cli + */ + public function cliOnlyCommand() + { + } + + /** + * @flushesCaches + */ + public function flushingCachesCommand() + { + } + + /** + * @param string $foo FooParamDescription + * @param string $bar BarParamDescription + */ + public function withArgumentsCommand($foo, $bar = 'baz') + { + } + + /** + * Short Description + * + * Longer Description + * Multine + * + * Much Multiline + */ + public function withDescriptionCommand() + { + } + + /** + * @see Foo:Bar:Baz + */ + public function relatedCommandIdentifiersCommand() + { + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php b/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php index 6c8de904e19b..783b31365c6d 100644 --- a/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php @@ -256,7 +256,8 @@ class ActionControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCas 'array' => false, 'optional' => false, 'allowsNull' => false, - 'type' => 'string' + 'type' => 'string', + 'hasDefaultValue' => false ], 'arg2' => [ 'position' => 1, @@ -264,7 +265,8 @@ class ActionControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCas 'array' => true, 'optional' => true, 'defaultValue' => [21], - 'allowsNull' => false + 'allowsNull' => false, + 'hasDefaultValue' => true ], 'arg3' => [ 'position' => 2, @@ -273,7 +275,8 @@ class ActionControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCas 'optional' => true, 'defaultValue' => 42, 'allowsNull' => false, - 'type' => 'string' + 'type' => 'string', + 'hasDefaultValue' => true ] ]; $mockReflectionService = $this->createMock(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); diff --git a/typo3/sysext/extbase/Tests/Unit/Object/Container/ClassInfoFactoryTest.php b/typo3/sysext/extbase/Tests/Unit/Object/Container/ClassInfoFactoryTest.php deleted file mode 100644 index d5582594fc42..000000000000 --- a/typo3/sysext/extbase/Tests/Unit/Object/Container/ClassInfoFactoryTest.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -namespace TYPO3\CMS\Extbase\Tests\Unit\Object\Container; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ -use TYPO3\CMS\Extbase\Object\Container\Exception\UnknownObjectException; - -/** - * Test case - */ -class ClassInfoFactoryTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory - */ - protected $classInfoFactory; - - /** - * Set up - */ - protected function setUp() - { - $this->classInfoFactory = new \TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory(); - } - - /** - * @test - */ - public function buildClassInfoFromClassNameThrowsExceptionIfGivenClassNameCantBeReflected() - { - $this->expectException(UnknownObjectException::class); - $this->expectExceptionCode(1289386765); - $this->classInfoFactory->buildClassInfoFromClassName('SomeNonExistingClass'); - } - - /** - * @test - */ - public function buildClassInfoDoesNotIncludeInjectSettingsMethodInListOfInjectMethods() - { - $classInfo = $this->classInfoFactory->buildClassInfoFromClassName('t3lib_object_tests_class_with_injectsettings'); - $this->assertEquals(['injectFoo' => 't3lib_object_tests_resolveablecyclic1'], $classInfo->getInjectMethods()); - } - - /** - * @test - */ - public function buildClassInfoDetectsPropertiesToInjectByAnnotation() - { - $classInfo = $this->classInfoFactory->buildClassInfoFromClassName(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithInjectProperties::class); - $this->assertEquals(['secondDummyClass' => \TYPO3\CMS\Extbase\Tests\Fixture\SecondDummyClass::class], $classInfo->getInjectProperties()); - } - - /** - * @test - */ - public function buildClassInfoReturnsCustomClassInfoForDateTime() - { - /** @var \PHPUnit_Framework_MockObject_MockObject | \TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory $classInfoFactory */ - $classInfoFactory = $this->getMockBuilder(\TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory::class) - ->setMethods(['getConstructorArguments']) - ->getMock(); - $classInfoFactory->expects($this->never())->method('getConstructorArguments'); - - $classInfo = $classInfoFactory->buildClassInfoFromClassName('DateTime'); - $this->assertEquals( - new \TYPO3\CMS\Extbase\Object\Container\ClassInfo('DateTime', [], [], false, false, []), - $classInfo - ); - } -} diff --git a/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php b/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php index 144a761d6080..91ef94bb8a03 100644 --- a/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php @@ -17,6 +17,7 @@ use Psr\Log\LoggerInterface; use TYPO3\CMS\Core\Log\Logger; use TYPO3\CMS\Extbase\Object\Exception; use TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException; +use TYPO3\CMS\Extbase\Reflection\Exception\UnknownClassException; /** * Test case @@ -194,33 +195,11 @@ class ContainerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase */ public function getInstanceThrowsExceptionIfClassWasNotFound() { - $this->expectException(Exception::class); - $this->expectExceptionCode(1289386765); + $this->expectException(UnknownClassException::class); + $this->expectExceptionCode(1278450972); $this->container->getInstance('nonextistingclass_bla'); } - /** - * @test - */ - public function getInstanceUsesClassNameMd5AsCacheKey() - { - $className = \TYPO3\CMS\Extbase\Tests\Unit\Object\Container\Fixtures\NamespacedClass::class; - $classNameHash = md5($className); - $mockedCache = $this->getMockBuilder(\TYPO3\CMS\Extbase\Object\Container\ClassInfoCache::class) - ->setMethods(['has', 'set', 'get']) - ->getMock(); - $container = $this->getMockBuilder(\TYPO3\CMS\Extbase\Object\Container\Container::class) - ->setMethods(['log', 'getClassInfoCache']) - ->getMock(); - $container->expects($this->any())->method('getClassInfoCache')->will($this->returnValue($mockedCache)); - $mockedCache->expects($this->never())->method('has'); - $mockedCache->expects($this->once())->method('get')->with($classNameHash)->will($this->returnValue(false)); - $mockedCache->expects($this->once())->method('set')->with($classNameHash, $this->anything())->will($this->returnCallback([$this, 'setClassInfoCacheCallback'])); - $container->getInstance($className); - $this->assertInstanceOf(\TYPO3\CMS\Extbase\Object\Container\ClassInfo::class, $this->cachedClassInfo); - $this->assertEquals($className, $this->cachedClassInfo->getClassName()); - } - /** * @test */ @@ -230,17 +209,6 @@ class ContainerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $this->assertTrue($instance->isInitialized()); } - /** - * Callback for getInstanceUsesClassNameSha1AsCacheKey - * - * @param string $id - * @param \TYPO3\CMS\Extbase\Object\Container\ClassInfo $value - */ - public function setClassInfoCacheCallback($id, \TYPO3\CMS\Extbase\Object\Container\ClassInfo $value) - { - $this->cachedClassInfo = $value; - } - /** * @test */ diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php index 88e3fb4166c0..d8041a2fea38 100644 --- a/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php @@ -18,6 +18,7 @@ use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface; use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException; use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap; use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; +use TYPO3\CMS\Extbase\Reflection\ClassSchema; use TYPO3\TestingFramework\Core\AccessibleObjectInterface; /** @@ -64,7 +65,26 @@ class DataMapperTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $className = $this->getUniqueId('Class'); $classNameWithNS = __NAMESPACE__ . '\\' . $className; eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' { - public $firstProperty; public $secondProperty; public $thirdProperty; public $fourthProperty; + + /** + * @var string + */ + public $firstProperty; + + /** + * @var int + */ + public $secondProperty; + + /** + * @var float + */ + public $thirdProperty; + + /** + * @var bool + */ + public $fourthProperty; }' ); $object = new $classNameWithNS(); @@ -88,13 +108,7 @@ class DataMapperTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $classNameWithNS => $dataMap ]; /** @var AccessibleObjectInterface|\TYPO3\CMS\Extbase\Reflection\ClassSchema $classSchema */ - $classSchema = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, ['dummy'], [$classNameWithNS]); - $classSchema->addProperty('pid', 'integer'); - $classSchema->addProperty('uid', 'integer'); - $classSchema->addProperty('firstProperty', 'string'); - $classSchema->addProperty('secondProperty', 'integer'); - $classSchema->addProperty('thirdProperty', 'float'); - $classSchema->addProperty('fourthProperty', 'boolean'); + $classSchema = new ClassSchema($classNameWithNS); $mockReflectionService = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class) ->setMethods(['getClassSchema']) ->getMock(); @@ -189,17 +203,21 @@ class DataMapperTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $className = $this->getUniqueId('Class1'); $classNameWithNS = __NAMESPACE__ . '\\' . $className; - eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' { public $relationProperty; }'); - $object = new $classNameWithNS(); $className2 = $this->getUniqueId('Class2'); $className2WithNS = __NAMESPACE__ . '\\' . $className2; eval('namespace ' . __NAMESPACE__ . '; class ' . $className2 . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' { }'); + eval('namespace ' . __NAMESPACE__ . '; class ' . $className . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' { + /** + * @var ' . $className2WithNS . ' + */ + public $relationProperty; + }'); + $object = new $classNameWithNS(); $child = new $className2WithNS(); /** @var \TYPO3\CMS\Extbase\Reflection\ClassSchema|AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject $classSchema1 */ - $classSchema1 = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, ['dummy'], [$classNameWithNS]); - $classSchema1->addProperty('relationProperty', $className2WithNS); + $classSchema1 = new ClassSchema($classNameWithNS); $identifier = 1; $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session(); diff --git a/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/Fixtures/Query.php b/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/Fixtures/Query.php new file mode 100644 index 000000000000..7b893c229bb8 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/Fixtures/Query.php @@ -0,0 +1,22 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Property\TypeConverter\Fixtures; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Fixture Query + */ +class Query +{ +} diff --git a/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php b/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php index 689f4417eded..a2890570c686 100644 --- a/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php @@ -255,7 +255,7 @@ class PersistentObjectConverterTest extends \TYPO3\TestingFramework\Core\Unit\Un public function setupMockQuery($numberOfResults, $howOftenIsGetFirstCalled) { $mockClassSchema = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class) - ->setConstructorArgs(['Dummy']) + ->setConstructorArgs([\TYPO3\CMS\Extbase\Tests\Unit\Property\TypeConverter\Fixtures\Query::class]) ->getMock(); $mockClassSchema->expects($this->any())->method('getIdentityProperties')->will($this->returnValue(['key1' => 'someType'])); $this->mockReflectionService->expects($this->any())->method('getClassSchema')->with('SomeType')->will($this->returnValue($mockClassSchema)); diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php b/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php index eb93ceb1cbe7..7a2f97c9d03e 100644 --- a/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php @@ -14,6 +14,11 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Persistence\ObjectStorage; +use TYPO3\CMS\Extbase\Reflection\ClassSchema; +use TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyModel; + /** * Test case */ @@ -24,49 +29,173 @@ class ClassSchemaTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase */ public function classSchemaForModelIsSetAggregateRootIfRepositoryClassIsFoundForNamespacedClasses() { - $className = $this->getUniqueId('BazFixture'); - eval(' - namespace Foo\\Bar\\Domain\\Model; - class ' . $className . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' {} - '); - eval(' - namespace Foo\\Bar\\Domain\\Repository; - class ' . $className . 'Repository {} - '); - - /** @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject $objectManager */ - $objectManager = $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManager::class); - $mockClassSchema = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, ['dummy'], ['Foo\\Bar\\Domain\\Model\\' . $className]); - $objectManager->expects($this->once())->method('get')->will($this->returnValue($mockClassSchema)); - /** @var \TYPO3\CMS\Extbase\Reflection\ReflectionService $service */ - $service = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class, ['dummy']); - $service->_set('objectManager', $objectManager); - $classSchema = $service->getClassSchema('Foo\\Bar\\Domain\\Model\\' . $className); + $service = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class); + $classSchema = $service->getClassSchema(DummyModel::class); $this->assertTrue($classSchema->isAggregateRoot()); } - /** - * @test - */ - public function classSchemaForModelIsSetAggregateRootIfRepositoryClassIsFoundForNotNamespacedClasses() - { - $className = $this->getUniqueId('BazFixture'); - eval(' - class Foo_Bar_Domain_Model_' . $className . ' extends \\' . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . ' {} - '); - eval(' - class Foo_Bar_Domain_Repository_' . $className . 'Repository {} - '); - - /** @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject $objectManager */ - $objectManager = $this->createMock(\TYPO3\CMS\Extbase\Object\ObjectManager::class); - $mockClassSchema = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, ['dummy'], ['Foo_Bar_Domain_Model_' . $className]); - $objectManager->expects($this->once())->method('get')->will($this->returnValue($mockClassSchema)); - - $service = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class, ['dummy']); - $service->_set('objectManager', $objectManager); - $classSchema = $service->getClassSchema('Foo_Bar_Domain_Model_' . $className); - $this->assertTrue($classSchema->isAggregateRoot()); + public function testClassSchemaHasConstructor() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithConstructorAndConstructorArguments::class); + static::assertTrue($classSchema->hasConstructor()); + } + + public function testClassSchemaDetectsConstructorArguments() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithConstructorAndConstructorArguments::class); + static::assertTrue($classSchema->hasConstructor()); + + $methodDefinition = $classSchema->getMethod('__construct'); + static::assertArrayHasKey('foo', $methodDefinition['params']); + static::assertArrayHasKey('bar', $methodDefinition['params']); + } + + public function testClassSchemaDetectsConstructorArgumentsWithDependencies() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithConstructorAndConstructorArgumentsWithDependencies::class); + static::assertTrue($classSchema->hasConstructor()); + + $methodDefinition = $classSchema->getMethod('__construct'); + static::assertArrayHasKey('foo', $methodDefinition['params']); + static::assertSame(Fixture\DummyClassWithGettersAndSetters::class, $methodDefinition['params']['foo']['dependency']); + } + + public function testClassSchemaDetectsMethodVisibility() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('publicMethod'); + static::assertTrue($methodDefinition['public']); + static::assertFalse($methodDefinition['protected']); + static::assertFalse($methodDefinition['private']); + + $methodDefinition = $classSchema->getMethod('protectedMethod'); + static::assertFalse($methodDefinition['public']); + static::assertTrue($methodDefinition['protected']); + static::assertFalse($methodDefinition['private']); + + $methodDefinition = $classSchema->getMethod('privateMethod'); + static::assertFalse($methodDefinition['public']); + static::assertFalse($methodDefinition['protected']); + static::assertTrue($methodDefinition['private']); + } + + public function testClassSchemaDetectsInjectMethods() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('injectSettings'); + static::assertFalse($methodDefinition['injectMethod']); + + $methodDefinition = $classSchema->getMethod('injectMethodWithoutParam'); + static::assertFalse($methodDefinition['injectMethod']); + + $methodDefinition = $classSchema->getMethod('injectMethodThatIsProtected'); + static::assertFalse($methodDefinition['injectMethod']); + + $methodDefinition = $classSchema->getMethod('injectFoo'); + static::assertTrue($methodDefinition['injectMethod']); + } + + public function testClassSchemaDetectsStaticMethods() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('staticMethod'); + static::assertTrue($methodDefinition['static']); + } + + public function testClassSchemaDetectsMandatoryParams() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('methodWithMandatoryParam'); + static::assertFalse($methodDefinition['params']['param']['optional']); + } + + public function testClassSchemaDetectsNullableParams() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('methodWithNullableParam'); + static::assertTrue($methodDefinition['params']['param']['nullable']); + } + + public function testClassSchemaDetectsDefaultValueParams() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('methodWithDefaultValueParam'); + static::assertSame('foo', $methodDefinition['params']['param']['default']); + } + + public function testClassSchemaDetectsParamTypeFromTypeHint() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class); + + $methodDefinition = $classSchema->getMethod('methodWithTypeHintedParam'); + static::assertSame('string', $methodDefinition['params']['param']['type']); + } + + public function testClassSchemaDetectsPropertyVisibility() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('publicProperty'); + static::assertTrue($propertyDefinition['public']); + static::assertFalse($propertyDefinition['protected']); + static::assertFalse($propertyDefinition['private']); + + $propertyDefinition = $classSchema->getProperty('protectedProperty'); + static::assertFalse($propertyDefinition['public']); + static::assertTrue($propertyDefinition['protected']); + static::assertFalse($propertyDefinition['private']); + + $propertyDefinition = $classSchema->getProperty('privateProperty'); + static::assertFalse($propertyDefinition['public']); + static::assertFalse($propertyDefinition['protected']); + static::assertTrue($propertyDefinition['private']); + } + + public function testClassSchemaDetectsInjectProperty() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('propertyWithInjectAnnotation'); + static::assertTrue($propertyDefinition['annotations']['inject']); + } + + public function testClassSchemaDetectsTransientProperty() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('propertyWithTransientAnnotation'); + static::assertTrue($propertyDefinition['annotations']['transient']); + } + + public function testClassSchemaDetectsCascadeProperty() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('propertyWithCascadeAnnotation'); + static::assertSame('remove', $propertyDefinition['annotations']['cascade']); + } + + public function testClassSchemaDetectsCascadePropertyOnlyWithVarAnnotation() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('propertyWithCascadeAnnotationWithoutVarAnnotation'); + static::assertNull($propertyDefinition['annotations']['cascade']); + } + + public function testClassSchemaDetectsTypeAndElementType() + { + $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class); + + $propertyDefinition = $classSchema->getProperty('propertyWithObjectStorageAnnotation'); + static::assertSame(ObjectStorage::class, $propertyDefinition['type']); + static::assertSame(Fixture\DummyClassWithAllTypesOfProperties::class, $propertyDefinition['elementType']); } } diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php new file mode 100644 index 000000000000..e34f09b4a74c --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php @@ -0,0 +1,75 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Fixture class with getters and setters + * + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later + */ +class DummyClassWithAllTypesOfMethods +{ + public function publicMethod() + { + } + + protected function protectedMethod() + { + } + + private function privateMethod() + { + } + + public function injectSettings() + { + // Will fail, as injectSettings is blacklisted + } + + public function injectMethodWithoutParam() + { + // Will fail, as there is no param + } + + protected function injectMethodThatIsProtected() + { + // Will fail, as method is protected + } + + public function injectFoo(DummyClassWithAllTypesOfMethods $foo) + { + // Will succeed + } + + public static function staticMethod() + { + } + + public static function methodWithMandatoryParam($param) + { + } + + public static function methodWithNullableParam($param = null) + { + } + + public static function methodWithDefaultValueParam($param = 'foo') + { + } + + public static function methodWithTypeHintedParam(string $param) + { + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php new file mode 100644 index 000000000000..6f336c282ed2 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php @@ -0,0 +1,56 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Fixture class with getters and setters + * + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later + */ +class DummyClassWithAllTypesOfProperties +{ + public $publicProperty; + + protected $protectedProperty; + + private $privateProperty; + + /** + * @inject + * @var DummyClassWithAllTypesOfProperties + */ + public $propertyWithInjectAnnotation; + + /** + * @transient + */ + public $propertyWithTransientAnnotation; + + /** + * @var DummyClassWithAllTypesOfProperties + * @cascade remove + */ + public $propertyWithCascadeAnnotation; + + /** + * @cascade remove + */ + public $propertyWithCascadeAnnotationWithoutVarAnnotation; + + /** + * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyClassWithAllTypesOfProperties> + */ + public $propertyWithObjectStorageAnnotation; +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArguments.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArguments.php new file mode 100644 index 000000000000..5b835f77438f --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArguments.php @@ -0,0 +1,27 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Fixture class with getters and setters + * + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later + */ +class DummyClassWithConstructorAndConstructorArguments +{ + public function __construct(int $foo, $bar = 'baz') + { + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArgumentsWithDependencies.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArgumentsWithDependencies.php new file mode 100644 index 000000000000..db405c9c3777 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArgumentsWithDependencies.php @@ -0,0 +1,27 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +/** + * Fixture class with getters and setters + * + * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later + */ +class DummyClassWithConstructorAndConstructorArgumentsWithDependencies +{ + public function __construct(DummyClassWithGettersAndSetters $foo) + { + } +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModel.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModel.php new file mode 100644 index 000000000000..bb933c412649 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModel.php @@ -0,0 +1,24 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Extbase\DomainObject\AbstractEntity; + +/** + * Fixture model + */ +class DummyModel extends AbstractEntity +{ +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModelRepository.php b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModelRepository.php new file mode 100644 index 000000000000..5acd0117e099 --- /dev/null +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModelRepository.php @@ -0,0 +1,24 @@ +<?php +namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture; + +/* + * This file is part of the TYPO3 CMS project. + * + * It is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, either version 2 + * of the License, or any later version. + * + * For the full copyright and license information, please read the + * LICENSE.txt file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\CMS\Extbase\Persistence\Repository; + +/** + * Fixture repository + */ +class DummyModelRepository extends Repository +{ +} diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php b/typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php index 830700eaa5db..9f12e29cc81e 100644 --- a/typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php @@ -14,6 +14,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Reflection\ReflectionService; /** @@ -45,7 +46,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function getClassTagsValues() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $classValues = $service->getClassTagsValues(static::class); $this->assertEquals([ 'firsttest' => ['test for reflection'], @@ -58,7 +59,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function getClassTagValues() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $classValues = $service->getClassTagValues(static::class, 'firsttest'); $this->assertEquals([ 'test for reflection', @@ -70,7 +71,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function hasMethod() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $this->assertTrue($service->hasMethod(static::class, 'fixtureMethodForMethodTagsValues')); $this->assertFalse($service->hasMethod(static::class, 'notExistentMethod')); } @@ -80,7 +81,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function getMethodTagsValues() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $tagsValues = $service->getMethodTagsValues(static::class, 'fixtureMethodForMethodTagsValues'); $this->assertEquals([ 'param' => ['array $foo The foo parameter'], @@ -93,7 +94,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function getMethodParameters() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $parameters = $service->getMethodParameters(static::class, 'fixtureMethodForMethodTagsValues'); $this->assertSame([ 'foo' => [ @@ -103,7 +104,12 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa 'optional' => false, 'allowsNull' => false, 'class' => null, - 'type' => 'array' + 'type' => 'array', + 'nullable' => false, + 'default' => null, + 'hasDefaultValue' => false, + 'defaultValue' => null, + 'dependency' => null, ] ], $parameters); } @@ -113,7 +119,7 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa */ public function getMethodParametersWithShortTypeNames() { - $service = new ReflectionService(); + $service = GeneralUtility::makeInstance(ReflectionService::class); $parameters = $service->getMethodParameters(static::class, 'fixtureMethodForMethodTagsValuesWithShortTypes'); $this->assertSame([ 'dummy' => [ @@ -123,7 +129,12 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa 'optional' => false, 'allowsNull' => true, 'class' => null, - 'type' => 'boolean' + 'type' => 'boolean', + 'nullable' => true, + 'default' => null, + 'hasDefaultValue' => false, + 'defaultValue' => null, + 'dependency' => null, ], 'foo' => [ 'position' => 1, @@ -132,7 +143,12 @@ class ReflectionServiceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCa 'optional' => false, 'allowsNull' => true, 'class' => null, - 'type' => 'integer' + 'type' => 'integer', + 'nullable' => true, + 'default' => null, + 'hasDefaultValue' => false, + 'defaultValue' => null, + 'dependency' => null, ] ], $parameters); } diff --git a/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php index fddd525f2368..6fa42c1895c2 100644 --- a/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php +++ b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php @@ -26,7 +26,6 @@ use TYPO3\CMS\Extbase\Mvc\Web\Request; use TYPO3\CMS\Extbase\Mvc\Web\Response; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; use TYPO3\CMS\Extbase\Property\Exception as PropertyException; -use TYPO3\CMS\Extbase\Reflection\PropertyReflection; use TYPO3\CMS\Form\Domain\Exception\RenderingException; use TYPO3\CMS\Form\Domain\Finishers\FinisherContext; use TYPO3\CMS\Form\Domain\Model\FormDefinition; @@ -700,7 +699,7 @@ class FormRuntime implements RootRenderableInterface, \ArrayAccess return true; } if (property_exists($this, $identifier)) { - $propertyReflection = new PropertyReflection($this, $identifier); + $propertyReflection = new \ReflectionProperty($this, $identifier); return $propertyReflection->isPublic(); } diff --git a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcPreset.php b/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcPreset.php deleted file mode 100644 index e2eba039387c..000000000000 --- a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcPreset.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -namespace TYPO3\CMS\Install\Configuration\ExtbaseObjectCache; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -use TYPO3\CMS\Install\Configuration; - -/** - * APC preset - */ -class ApcPreset extends Configuration\AbstractPreset -{ - /** - * @var string Name of preset - */ - protected $name = 'Apc'; - - /** - * @var int Priority of preset - */ - protected $priority = 80; - - /** - * @var array Configuration values handled by this preset - */ - protected $configurationValues = [ - 'SYS/caching/cacheConfigurations/extbase_object' => [ - 'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, - 'backend' => \TYPO3\CMS\Core\Cache\Backend\ApcBackend::class, - 'options' => [ - 'defaultLifetime' => 0, - ], - 'groups' => ['system'] - ] - ]; - - /** - * APC preset is available if extension is loaded and at least ~5MB are free. - * - * @return bool TRUE - */ - public function isAvailable() - { - $result = false; - if (extension_loaded('apc')) { - $memoryInfo = @apc_sma_info(); - $availableMemory = $memoryInfo['avail_mem']; - - // If more than 5MB free - if ($availableMemory > (5 * 1024 * 1024)) { - $result = true; - } - } - return $result; - } -} diff --git a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcuPreset.php b/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcuPreset.php deleted file mode 100644 index d595059e9f9d..000000000000 --- a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcuPreset.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -namespace TYPO3\CMS\Install\Configuration\ExtbaseObjectCache; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -use TYPO3\CMS\Install\Configuration; - -/** - * APCu preset - */ -class ApcuPreset extends Configuration\AbstractPreset -{ - /** - * @var string Name of preset - */ - protected $name = 'Apcu'; - - /** - * @var int Priority of preset - */ - protected $priority = 90; - - /** - * @var array Configuration values handled by this preset - */ - protected $configurationValues = [ - 'SYS/caching/cacheConfigurations/extbase_object' => [ - 'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, - 'backend' => \TYPO3\CMS\Core\Cache\Backend\ApcuBackend::class, - 'options' => [ - 'defaultLifetime' => 0, - ], - 'groups' => ['system'] - ] - ]; - - /** - * APC preset is available if extension is loaded and at least ~5MB are free. - * - * @return bool TRUE - */ - public function isAvailable() - { - $result = false; - if (extension_loaded('apcu')) { - $memoryInfo = @apcu_sma_info(); - $availableMemory = $memoryInfo['avail_mem']; - - // If more than 5MB free - if ($availableMemory > (5 * 1024 * 1024)) { - $result = true; - } - } - return $result; - } -} diff --git a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/DatabasePreset.php b/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/DatabasePreset.php deleted file mode 100644 index 5c3aa1c1fb76..000000000000 --- a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/DatabasePreset.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php -namespace TYPO3\CMS\Install\Configuration\ExtbaseObjectCache; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -use TYPO3\CMS\Install\Configuration; - -/** - * Database preset - */ -class DatabasePreset extends Configuration\AbstractPreset -{ - /** - * @var string Name of preset - */ - protected $name = 'Database'; - - /** - * @var int Priority of preset - */ - protected $priority = 50; - - /** - * @var array Configuration values handled by this preset - */ - protected $configurationValues = [ - 'SYS/caching/cacheConfigurations/extbase_object' => [ - 'frontend' => \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend::class, - 'backend' => \TYPO3\CMS\Core\Cache\Backend\Typo3DatabaseBackend::class, - 'options' => [ - 'defaultLifetime' => 0, - ], - 'groups' => ['system'] - ] - ]; - - /** - * Database preset is always available - * - * @return bool TRUE - */ - public function isAvailable() - { - return true; - } -} diff --git a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ExtbaseObjectCacheFeature.php b/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ExtbaseObjectCacheFeature.php deleted file mode 100644 index fcc8c813e79a..000000000000 --- a/typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ExtbaseObjectCacheFeature.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -namespace TYPO3\CMS\Install\Configuration\ExtbaseObjectCache; - -/* - * This file is part of the TYPO3 CMS project. - * - * It is free software; you can redistribute it and/or modify it under - * the terms of the GNU General Public License, either version 2 - * of the License, or any later version. - * - * For the full copyright and license information, please read the - * LICENSE.txt file that was distributed with this source code. - * - * The TYPO3 project - inspiring people to share! - */ - -use TYPO3\CMS\Install\Configuration; - -/** - * Extbase object cache configuration - */ -class ExtbaseObjectCacheFeature extends Configuration\AbstractFeature implements Configuration\FeatureInterface -{ - /** - * @var string Name of feature - */ - protected $name = 'ExtbaseObjectCache'; - - /** - * @var array List of preset classes - */ - protected $presetRegistry = [ - DatabasePreset::class, - ApcPreset::class, - ApcuPreset::class, - ]; -} diff --git a/typo3/sysext/install/Classes/Configuration/FeatureManager.php b/typo3/sysext/install/Classes/Configuration/FeatureManager.php index 7698305953a4..567b883e065f 100644 --- a/typo3/sysext/install/Classes/Configuration/FeatureManager.php +++ b/typo3/sysext/install/Classes/Configuration/FeatureManager.php @@ -16,7 +16,6 @@ namespace TYPO3\CMS\Install\Configuration; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Install\Configuration\Context\ContextFeature; -use TYPO3\CMS\Install\Configuration\ExtbaseObjectCache\ExtbaseObjectCacheFeature; use TYPO3\CMS\Install\Configuration\Image\ImageFeature; use TYPO3\CMS\Install\Configuration\Mail\MailFeature; @@ -31,7 +30,6 @@ class FeatureManager protected $featureRegistry = [ ContextFeature::class, ImageFeature::class, - ExtbaseObjectCacheFeature::class, MailFeature::class, ]; diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php index a2028be9f7ae..414ee5298b94 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php @@ -404,6 +404,39 @@ return [ 'Deprecation-82744-RenameExtlowlevelViewToLowlevelController.rst', ], ], + 'TYPO3\CMS\Extbase\Object\Container\ClassInfo' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + 'Feature-57594-OptimizeReflectionServiceCacheHandling.rst' + ], + ], + 'TYPO3\CMS\Extbase\Object\Container\ClassInfoCache' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + 'Feature-57594-OptimizeReflectionServiceCacheHandling.rst' + ], + ], + 'TYPO3\CMS\Extbase\Object\Container\ClassInfoFactory' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + 'Feature-57594-OptimizeReflectionServiceCacheHandling.rst' + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassReflection' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\MethodReflection' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ParameterReflection' => [ + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], // Removed interfaces 'TYPO3\CMS\Backend\Form\DatabaseFileIconsHookInterface' => [ diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index eac2fdafcc95..ee98199074f2 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -1289,4 +1289,74 @@ return [ 'Deprecation-81217-TSFE-relatedLanguageMethods.rst', ], ], + 'TYPO3\CMS\Extbase\Reflection\PropertyReflection->isTaggedWith' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\PropertyReflection->getTagsValues' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\PropertyReflection->getTagValues' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Breaking-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->addProperty' => [ + 'numberOfMandatoryArguments' => 2, + 'maximumNumberOfArguments' => 4, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->setModelType' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->getModelType' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->setUuidPropertyName' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->getUuidPropertyName' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->markAsIdentityProperty' => [ + 'numberOfMandatoryArguments' => 1, + 'maximumNumberOfArguments' => 1, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], + 'TYPO3\CMS\Extbase\Reflection\ClassSchema->getIdentityProperties' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst', + ], + ], ]; diff --git a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache.html b/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache.html deleted file mode 100644 index fd3b8269ac2c..000000000000 --- a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache.html +++ /dev/null @@ -1,27 +0,0 @@ -<div class="panel panel-default"> - <div class="panel-heading" role="tab" id="headingThree"> - <h4 class="panel-title"> - <a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseThree" aria-expanded="true" aria-controls="collapseThree" class="collapsed"> - <span class="caret"></span> - Extbase object cache - </a> - </h4> - </div> - <div id="collapseThree" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingThree"> - <div class="panel-body"> - <p> - To speed up object instantiation, Extbase stores some cache data. This can lead - to lots of SELECT database queries on each page hit if the cache is configured - to use the default database cache backend. - </p> - <p> - This cache is limited in size, uses no tagging, and is used multiple times - for one request. These characteristics make it well suited to be used in - combination with a APC cache backend. - </p> - <f:for each="{feature.presetsOrderedByPriority}" as="preset"> - <f:render partial="Settings/Presets/{feature.name}/{preset.name}" arguments="{_all}" /> - </f:for> - </div> - </div> -</div> \ No newline at end of file diff --git a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apc.html b/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apc.html deleted file mode 100644 index c2ce15692880..000000000000 --- a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apc.html +++ /dev/null @@ -1,37 +0,0 @@ -<div class="alert {f:if(condition:'{preset.isAvailable}', then:'alert-success', else:'alert-warning')}"> - <div class="header-container"> - <div class="message-header"> - <input - type="radio" - class="t3-install-tool-configuration-radio" - id="t3-install-tool-configuration-extbaseobjectcache-apc" - name="install[values][{feature.name}][enable]" - value="{preset.name}" - {f:if(condition:'{preset.isAvailable}', then:'', else:'disabled="disabled"')} - {f:if(condition: preset.isActive, then:'checked="checked"')} - /> - <label - for="t3-install-tool-configuration-extbaseobjectcache-apc" - class="t3-install-tool-configuration-radio-label" - > - <strong> - APC cache backend - </strong> - {f:if(condition: preset.isActive, then:' [Active]')} - </label> - </div> - </div> - <div class="message-body>"> - <f:if condition="{preset.isAvailable}"> - <f:then> - Use APC cache backend. This reduces your MySQL load and - speeds up lots of TYPO3 CMS requests. Use if available. - </f:then> - <f:else> - APCu 4.x is not loaded or not enough memory is left. APC should - have at least 5MB free memory. - </f:else> - </f:if> - </div> -</div> -<p></p> diff --git a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apcu.html b/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apcu.html deleted file mode 100644 index c59e654a4a98..000000000000 --- a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apcu.html +++ /dev/null @@ -1,37 +0,0 @@ -<div class="alert {f:if(condition:'{preset.isAvailable}', then:'alert-success', else:'alert-warning')}"> - <div class="header-container"> - <div class="message-header"> - <input - type="radio" - class="t3-install-tool-configuration-radio" - id="t3-install-tool-configuration-extbaseobjectcache-apc" - name="install[values][{feature.name}][enable]" - value="{preset.name}" - {f:if(condition:'{preset.isAvailable}', then:'', else:'disabled="disabled"')} - {f:if(condition: preset.isActive, then:'checked="checked"')} - /> - <label - for="t3-install-tool-configuration-extbaseobjectcache-apc" - class="t3-install-tool-configuration-radio-label" - > - <strong> - APCu cache backend - </strong> - {f:if(condition: preset.isActive, then:' [Active]')} - </label> - </div> - </div> - <div class="message-body>"> - <f:if condition="{preset.isAvailable}"> - <f:then> - Use APCu (APC Userland) cache backend. This reduces your MySQL load and - speeds up lots of TYPO3 CMS requests. Use if available. - </f:then> - <f:else> - APCu 5+ is not loaded or not enough memory is left. APCu should - have at least 5MB free memory. - </f:else> - </f:if> - </div> -</div> -<p></p> diff --git a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Database.html b/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Database.html deleted file mode 100644 index ed764522ee23..000000000000 --- a/typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Database.html +++ /dev/null @@ -1,28 +0,0 @@ -<div class="alert alert-success"> - <div class="header-container"> - <div class="message-header"> - <input - type="radio" - class="t3-install-tool-configuration-radio" - id="t3-install-tool-configuration-extbaseobjectcache-database" - name="install[values][{feature.name}][enable]" - value="{preset.name}" - {f:if(condition: preset.isActive, then:'checked="checked"')} - /> - <label - for="t3-install-tool-configuration-extbaseobjectcache-database" - class="t3-install-tool-configuration-radio-label" - > - <strong> - Database cache backend - </strong> - {f:if(condition: preset.isActive, then:' [Active]')} - </label> - </div> - </div> - <div class="message-body>"> - This default cache backend is always available as a - fallback if APC is not available. - </div> -</div> -<p></p> -- GitLab