[!!!][TASK] Replace ClassInfo with ClassSchema 81/54381/23
authorAlexander Schnitzler <git@alexanderschnitzler.de>
Thu, 12 Oct 2017 14:58:33 +0000 (16:58 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Tue, 17 Oct 2017 15:35:52 +0000 (17:35 +0200)
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>
52 files changed:
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-57594-OptimizeReflectionServiceCacheHandling.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Deprecation-57594-OptimizeReflectionServiceCacheHandling.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-57594-OptimizeReflectionServiceCacheHandling.rst [new file with mode: 0644]
typo3/sysext/extbase/Classes/Core/Bootstrap.php
typo3/sysext/extbase/Classes/Mvc/Cli/Command.php
typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
typo3/sysext/extbase/Classes/Mvc/Dispatcher.php
typo3/sysext/extbase/Classes/Mvc/RequestHandlerResolver.php
typo3/sysext/extbase/Classes/Object/Container/ClassInfo.php [deleted file]
typo3/sysext/extbase/Classes/Object/Container/ClassInfoCache.php [deleted file]
typo3/sysext/extbase/Classes/Object/Container/ClassInfoFactory.php [deleted file]
typo3/sysext/extbase/Classes/Object/Container/Container.php
typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/Persistence/Generic/Session.php
typo3/sysext/extbase/Classes/Reflection/ClassReflection.php [deleted file]
typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
typo3/sysext/extbase/Classes/Reflection/MethodReflection.php [deleted file]
typo3/sysext/extbase/Classes/Reflection/ObjectAccess.php
typo3/sysext/extbase/Classes/Reflection/ParameterReflection.php [deleted file]
typo3/sysext/extbase/Classes/Reflection/PropertyReflection.php [deleted file]
typo3/sysext/extbase/Classes/Reflection/ReflectionService.php
typo3/sysext/extbase/Classes/Scheduler/TaskExecutor.php
typo3/sysext/extbase/Tests/Unit/Mvc/Cli/CommandTest.php
typo3/sysext/extbase/Tests/Unit/Mvc/Cli/Fixture/Command/MockCCommandController.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Mvc/Controller/ActionControllerTest.php
typo3/sysext/extbase/Tests/Unit/Object/Container/ClassInfoFactoryTest.php [deleted file]
typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php
typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapperTest.php
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/Fixtures/Query.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfMethods.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithAllTypesOfProperties.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArguments.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyClassWithConstructorAndConstructorArgumentsWithDependencies.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModel.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/Fixture/DummyModelRepository.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/ReflectionServiceTest.php
typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php
typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcPreset.php [deleted file]
typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ApcuPreset.php [deleted file]
typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/DatabasePreset.php [deleted file]
typo3/sysext/install/Classes/Configuration/ExtbaseObjectCache/ExtbaseObjectCacheFeature.php [deleted file]
typo3/sysext/install/Classes/Configuration/FeatureManager.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/ClassNameMatcher.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache.html [deleted file]
typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apc.html [deleted file]
typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Apcu.html [deleted file]
typo3/sysext/install/Resources/Private/Partials/Settings/Presets/ExtbaseObjectCache/Database.html [deleted file]

index 7bb1f4c..526a705 100644 (file)
@@ -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 (file)
index 0000000..6d3b893
--- /dev/null
@@ -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 (file)
index 0000000..f2b5d19
--- /dev/null
@@ -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 (file)
index 0000000..32cc9ac
--- /dev/null
@@ -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
index 39c84f5..6b9d942 100644 (file)
@@ -31,13 +31,6 @@ class Bootstrap implements \TYPO3\CMS\Extbase\Core\BootstrapInterface
     public $cObj;
 
     /**
-     * The application context
-     *
-     * @var string
-     */
-    protected $context;
-
-    /**
      * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManager
      */
     protected $configurationManager;
@@ -48,16 +41,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
      */
     protected $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();
     }
 
@@ -138,30 +119,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
      *
      * @see initialize()
@@ -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();
     }
 
     /**
index 7653a2e..aa3a51e 100644 (file)
@@ -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
  *
@@ -42,11 +44,6 @@ class Command
     protected $commandIdentifier;
 
     /**
-     * @var \TYPO3\CMS\Extbase\Reflection\MethodReflection
-     */
-    protected $commandMethodReflection;
-
-    /**
      * Name of the extension to which this command belongs
      *
      * @var string
@@ -59,6 +56,16 @@ class Command
     protected $reflectionService;
 
     /**
+     * @var ClassSchema
+     */
+    protected $classSchema;
+
+    /**
+     * @var string
+     */
+    protected $controllerCommandMethod;
+
+    /**
      * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
      */
     public function injectObjectManager(\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;
-    }
 }
index c97960d..ebc0bba 100644 (file)
@@ -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);
         }
     }
index c02e80e..6d82759 100644 (file)
@@ -26,11 +26,6 @@ class Dispatcher implements \TYPO3\CMS\Core\SingletonInterface
     protected $objectManager;
 
     /**
-     * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
-     */
-    protected $reflectionService;
-
-    /**
      * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
      */
     protected $signalSlotDispatcher;
@@ -41,14 +36,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
      */
     public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
index e85148f..7a8f8e6 100644 (file)
@@ -25,11 +25,6 @@ class RequestHandlerResolver
     protected $objectManager;
 
     /**
-     * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
-     */
-    protected $reflectionService;
-
-    /**
      * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
      */
     protected $configurationManager;
@@ -43,14 +38,6 @@ class RequestHandlerResolver
     }
 
     /**
-     * @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
      */
     public function injectConfigurationManager(\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 (file)
index 40cbe8d..0000000
+++ /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 (file)
index 3dd2920..0000000
+++ /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 (file)
index 26980ea..0000000
+++ /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');
-    }
-}
index 179b78a..4d76825 100644 (file)
@@ -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
@@ -27,13 +30,6 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface
     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
      *
@@ -42,13 +38,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
      */
     protected $instantiator = null;
@@ -68,6 +57,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);
@@ -327,30 +298,13 @@ class Container implements \TYPO3\CMS\Core\SingletonInterface
     }
 
     /**
-     * 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
      *
      * @return bool
      */
     public function isSingleton($className)
     {
-        return $this->getClassInfo($className)->getIsSingleton();
+        return $this->reflectionService->getClassSchema($className)->isSingleton();
     }
 
     /**
index c703602..cd9a393 100644 (file)
@@ -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);
index 4adb5be..2e55f63 100644 (file)
@@ -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 {
index 2b56c98..ab0351b 100644 (file)
@@ -48,19 +48,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
      */
     public function __construct()
diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassReflection.php b/typo3/sysext/extbase/Classes/Reflection/ClassReflection.php
deleted file mode 100644 (file)
index 8adc6eb..0000000
+++ /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;
-    }
-}
index 5f8b37d..e7ca5a3 100644 (file)
@@ -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
@@ -72,13 +77,255 @@ 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 (file)
index fbd00f5..0000000
+++ /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;
-    }
-}
index 3152ff5..1d9c500 100644 (file)
@@ -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 (file)
index 7dc7fd4..0000000
+++ /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 (file)
index d107dbb..0000000
+++ /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;
-    }
-}
index 06a2842..14957f4 100644 (file)
@@ -14,28 +14,19 @@ 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
@@ -43,85 +34,11 @@ class ReflectionService implements \TYPO3\CMS\Core\SingletonInterface
     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;
-    }
 }
index cace857..85d38be 100644 (file)
@@ -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();
     }
 }
index c6bb34d..eb04716 100644 (file)
@@ -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 (file)
index 0000000..463c0b4
--- /dev/null
@@ -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()
+    {
+    }
+}
index 6c8de90..783b313 100644 (file)
@@ -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 (file)
index d558259..0000000
+++ /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
-        );
-    }
-}
index 144a761..91ef94b 100644 (file)
@@ -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,36 +195,14 @@ 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
-     */
     public function getInstanceInitializesObjects()
     {
         $instance = $this->container->getInstance('t3lib_object_tests_initializable');
@@ -231,17 +210,6 @@ class ContainerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
     }
 
     /**
-     * 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
      */
     public function getEmptyObjectReturnsInstanceOfSimpleClass()
index 88e3fb4..d8041a2 100644 (file)
@@ -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 (file)
index 0000000..7b893c2
--- /dev/null
@@ -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
+{
+}
index 689f441..a289057 100644 (file)
@@ -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));
index eb93ceb..7a2f97c 100644 (file)
@@ -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 (file)
index 0000000..e34f09b
--- /dev/null
@@ -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 (file)
index 0000000..6f336c2
--- /dev/null
@@ -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 (file)
index 0000000..5b835f7
--- /dev/null
@@ -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 (file)
index 0000000..db405c9
--- /dev/null
@@ -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 (file)
index 0000000..bb933c4
--- /dev/null
@@ -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 (file)
index 0000000..5acd011
--- /dev/null
@@ -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
+{
+}
index 830700e..9f12e29 100644 (file)
@@ -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);
     }
index fddd525..6fa42c1 100644 (file)
@@ -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 (file)
index e2eba03..0000000
+++ /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 (file)
index d595059..0000000
+++ /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 (file)
index 5c3aa1c..0000000
+++ /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 (file)
index fcc8c81..0000000
+++ /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,
-    ];
-}
index 7698305..567b883 100644 (file)
@@ -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,
     ];
 
index a2028be..414ee52 100644 (file)
@@ -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' => [
index eac2fda..ee98199 100644 (file)
@@ -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 (file)
index fd3b826..0000000
+++ /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 (file)
index c2ce156..0000000
+++ /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 (file)
index c59e654..0000000
+++ /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 (file)
index ed76452..0000000
+++ /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>