[FEATURE] Improve ClassSchema properties api 82/59382/9
authorAlexander Schnitzler <git@alexanderschnitzler.de>
Wed, 9 Jan 2019 11:35:16 +0000 (12:35 +0100)
committerBenni Mack <benni@typo3.org>
Sat, 12 Jan 2019 20:30:54 +0000 (21:30 +0100)
This patch introduces a new Property class to have an
easier and more stable api to get information about class
properties from ClassSchema objects

The reflection data fetched and stored by/in the ClassSchema
objects is no longer publicly available as an array. Instead,
Property objects are returned for the following calls:

- getProperty
- getProperties

Releases: master
Resolves: #87377
Change-Id: Ica09e7f2df42638497cbde1aea35d62c42332d3f
Reviewed-on: https://review.typo3.org/59382
Tested-by: TYPO3com <noreply@typo3.com>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
14 files changed:
typo3/sysext/core/Documentation/Changelog/master/Feature-86964-AllowGettingClassPropertyDefaultValue.rst
typo3/sysext/extbase/Classes/Object/Container/Container.php
typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapFactory.php
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/Property/TypeConverter/PersistentObjectConverter.php
typo3/sysext/extbase/Classes/Reflection/ClassSchema.php
typo3/sysext/extbase/Classes/Reflection/ClassSchema/Exception/NoSuchPropertyException.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Reflection/ClassSchema/Property.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Validation/ValidatorResolver.php
typo3/sysext/extbase/Tests/Unit/Persistence/Generic/Mapper/DataMapFactoryTest.php
typo3/sysext/extbase/Tests/Unit/Property/TypeConverter/PersistentObjectConverterTest.php
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchema/PropertyTest.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchemaTest.php

index 736d998..16a117c 100644 (file)
@@ -22,6 +22,6 @@ It is now possible to get the default value of a class property when using the `
            ->getClassSchema(MyClass::class)
            ->getProperty('myProperty');
 
-       $defaultValue = $property['defaultValue']; // "foo"
+       $defaultValue = $property->getDefaultValue(); // "foo"
 
 .. index:: PHP-API, ext:extbase, NotScanned
index af36ce3..6d21196 100644 (file)
@@ -203,7 +203,7 @@ class Container implements \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.');
             }
 
-            if ($classSchema->getProperty($injectPropertyName)['public']) {
+            if ($classSchema->getProperty($injectPropertyName)->isPublic()) {
                 $instance->{$injectPropertyName} = $instanceToInject;
             } else {
                 $propertyReflection = new \ReflectionProperty($instance, $injectPropertyName);
index 8a90169..e72376d 100644 (file)
@@ -445,10 +445,10 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
         $dataMapper = $objectManager->get(DataMapper::class);
         $columnMap = $this->dataMapFactory->buildDataMap($className)->getColumnMap($propertyName);
-        $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
+        $property = $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['annotations']['cascade'] === 'remove') {
+            if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $property->getAnnotationValue('cascade') === 'remove') {
                 $this->removeEntity($removedObject);
             }
         }
@@ -1071,8 +1071,8 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
             if ($columnMap === null) {
                 continue;
             }
-            $propertyMetaData = $classSchema->getProperty($propertyName);
-            if ($propertyMetaData['annotations']['cascade'] === 'remove') {
+            $property = $classSchema->getProperty($propertyName);
+            if ($property->getAnnotationValue('cascade') === 'remove') {
                 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
                     foreach ($propertyValue as $containedObject) {
                         $this->removeEntity($containedObject);
index beab144..22d3ca7 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
  */
 
 use TYPO3\CMS\Core\Database\Query\QueryHelper;
+use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException;
 
 /**
  * A factory for a data map to map a single table configured in $TCA on a domain object.
@@ -156,12 +157,17 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface
             } else {
                 $propertyName = \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToLowerCamelCase($columnName);
             }
-            // if (in_array($propertyName, $classPropertyNames)) {
-            // @todo Enable check for property existence
+            // @todo: shall we really create column maps for non existing properties?
+            // @todo: check why this could happen in the first place. TCA definitions for non existing model properties?
             $columnMap = $this->createColumnMap($columnName, $propertyName);
-            $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
+            try {
+                $property = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
+                [$type, $elementType] = [$property->getType(), $property->getElementType()];
+            } catch (NoSuchPropertyException $e) {
+                [$type, $elementType] = [null, null];
+            }
             $columnMap = $this->setType($columnMap, $columnDefinition['config']);
-            $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $propertyMetaData);
+            $columnMap = $this->setRelations($columnMap, $columnDefinition['config'], $type, $elementType);
             $columnMap = $this->setFieldEvaluations($columnMap, $columnDefinition['config']);
             $dataMap->addColumnMap($columnMap);
         }
@@ -311,17 +317,19 @@ class DataMapFactory implements \TYPO3\CMS\Core\SingletonInterface
      *
      * @param ColumnMap $columnMap The column map
      * @param array|null $columnConfiguration The column configuration from $TCA
-     * @param array $propertyMetaData The property metadata as delivered by the reflection service
+     * @param string|null $type
+     * @param string|null $elementType
      * @return ColumnMap
      */
-    protected function setRelations(ColumnMap $columnMap, $columnConfiguration, $propertyMetaData)
+    protected function setRelations(ColumnMap $columnMap, $columnConfiguration, ?string $type, ?string $elementType)
     {
         if (isset($columnConfiguration)) {
             if (isset($columnConfiguration['MM'])) {
                 $columnMap = $this->setManyToManyRelation($columnMap, $columnConfiguration);
-            } elseif (isset($propertyMetaData['elementType'])) {
+            } elseif ($elementType !== null) {
                 $columnMap = $this->setOneToManyRelation($columnMap, $columnConfiguration);
-            } elseif (isset($propertyMetaData['type']) && strpbrk($propertyMetaData['type'], '_\\') !== false) {
+            } elseif ($type !== null && strpbrk($type, '_\\') !== false) {
+                // @todo: check the strpbrk function call. Seems to be a check for Tx_Foo_Bar style class names
                 $columnMap = $this->setOneToOneRelation($columnMap, $columnConfiguration);
             } elseif (
                 isset($columnConfiguration['type'], $columnConfiguration['renderType'])
index fd2d22c..53b672b 100644 (file)
@@ -20,6 +20,7 @@ use TYPO3\CMS\Extbase\Object\Exception\CannotReconstituteObjectException;
 use TYPO3\CMS\Extbase\Persistence;
 use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
 use TYPO3\CMS\Extbase\Persistence\QueryInterface;
+use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException;
 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
 
 /**
@@ -243,10 +244,10 @@ class DataMapper
             }
             $columnMap = $dataMap->getColumnMap($propertyName);
             $columnName = $columnMap->getColumnName();
-            $propertyData = $classSchema->getProperty($propertyName);
+            $propertyType = $classSchema->getProperty($propertyName)->getType();
             $propertyValue = null;
             if (isset($row[$columnName])) {
-                switch ($propertyData['type']) {
+                switch ($propertyType) {
                     case 'integer':
                         $propertyValue = (int)$row[$columnName];
                         break;
@@ -270,16 +271,16 @@ class DataMapper
                             $this->fetchRelated($object, $propertyName, $row[$columnName])
                         );
                         break;
-                    case is_subclass_of($propertyData['type'], \DateTimeInterface::class):
+                    case is_subclass_of($propertyType, \DateTimeInterface::class):
                             $propertyValue = $this->mapDateTime(
                                 $row[$columnName],
                                 $columnMap->getDateTimeStorageFormat(),
-                                $propertyData['type']
+                                $propertyType
                             );
                         break;
                     default:
-                        if (TypeHandlingUtility::isCoreType($propertyData['type'])) {
-                            $propertyValue = $this->mapCoreType($propertyData['type'], $row[$columnName]);
+                        if (TypeHandlingUtility::isCoreType($propertyType)) {
+                            $propertyValue = $this->mapCoreType($propertyType, $row[$columnName]);
                         } else {
                             $propertyValue = $this->mapObjectToClassProperty(
                                 $object,
@@ -347,9 +348,9 @@ class DataMapper
      */
     public function fetchRelated(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '', $enableLazyLoading = true)
     {
-        $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
-        if ($enableLazyLoading === true && $propertyMetaData['annotations']['lazy']) {
-            if ($propertyMetaData['type'] === Persistence\ObjectStorage::class) {
+        $property = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
+        if ($enableLazyLoading === true && $property->getAnnotationValue('lazy') === true) {
+            if ($property->getType() === Persistence\ObjectStorage::class) {
                 $result = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage::class, $parentObject, $propertyName, $fieldValue, $this);
             } else {
                 if (empty($fieldValue)) {
@@ -523,9 +524,9 @@ class DataMapper
             if ($fieldValue === '') {
                 $propertyValue = $this->getEmptyRelationValue($parentObject, $propertyName);
             } else {
-                $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
-                if ($this->persistenceSession->hasIdentifier($fieldValue, $propertyMetaData['type'])) {
-                    $propertyValue = $this->persistenceSession->getObjectByIdentifier($fieldValue, $propertyMetaData['type']);
+                $property = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
+                if ($this->persistenceSession->hasIdentifier($fieldValue, $property->getType())) {
+                    $propertyValue = $this->persistenceSession->getObjectByIdentifier($fieldValue, $property->getType());
                 } else {
                     $result = $this->fetchRelated($parentObject, $propertyName, $fieldValue);
                     $propertyValue = $this->mapResultToPropertyValue($parentObject, $propertyName, $result);
@@ -563,15 +564,15 @@ class DataMapper
         if ($result instanceof Persistence\Generic\LoadingStrategyInterface) {
             $propertyValue = $result;
         } else {
-            $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
-            if (in_array($propertyMetaData['type'], ['array', 'ArrayObject', 'SplObjectStorage', Persistence\ObjectStorage::class], true)) {
+            $property = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
+            if (in_array($property->getType(), ['array', 'ArrayObject', 'SplObjectStorage', Persistence\ObjectStorage::class], true)) {
                 $objects = [];
                 foreach ($result as $value) {
                     $objects[] = $value;
                 }
-                if ($propertyMetaData['type'] === 'ArrayObject') {
+                if ($property->getType() === 'ArrayObject') {
                     $propertyValue = new \ArrayObject($objects);
-                } elseif ($propertyMetaData['type'] === Persistence\ObjectStorage::class) {
+                } elseif ($property->getType() === Persistence\ObjectStorage::class) {
                     $propertyValue = new Persistence\ObjectStorage();
                     foreach ($objects as $object) {
                         $propertyValue->attach($object);
@@ -580,7 +581,8 @@ class DataMapper
                 } else {
                     $propertyValue = $objects;
                 }
-            } elseif (strpbrk($propertyMetaData['type'], '_\\') !== false) {
+            } elseif (strpbrk($property->getType(), '_\\') !== false) {
+                // @todo: check the strpbrk function call. Seems to be a check for Tx_Foo_Bar style class names
                 if (is_object($result) && $result instanceof Persistence\QueryResultInterface) {
                     $propertyValue = $result->getFirst();
                 } else {
@@ -676,15 +678,20 @@ class DataMapper
      */
     public function getType($parentClassName, $propertyName)
     {
-        $propertyMetaData = $this->reflectionService->getClassSchema($parentClassName)->getProperty($propertyName);
-        if (!empty($propertyMetaData['elementType'])) {
-            $type = $propertyMetaData['elementType'];
-        } elseif (!empty($propertyMetaData['type'])) {
-            $type = $propertyMetaData['type'];
-        } else {
-            throw new UnexpectedTypeException('Could not determine the child object type.', 1251315967);
+        try {
+            $property = $this->reflectionService->getClassSchema($parentClassName)->getProperty($propertyName);
+
+            if ($property->getElementType() !== null) {
+                return $property->getElementType();
+            }
+
+            if ($property->getType() !== null) {
+                return $property->getType();
+            }
+        } catch (NoSuchPropertyException $e) {
         }
-        return $type;
+
+        throw new UnexpectedTypeException('Could not determine the child object type.', 1251315967);
     }
 
     /**
index 04716f4..5192c39 100644 (file)
@@ -120,8 +120,8 @@ class PersistentObjectConverter extends ObjectConverter
         if (!$schema->hasProperty($propertyName)) {
             throw new \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException('Property "' . $propertyName . '" was not found in target object of type "' . $specificTargetType . '".', 1297978366);
         }
-        $propertyInformation = $schema->getProperty($propertyName);
-        return $propertyInformation['type'] . ($propertyInformation['elementType'] !== null ? '<' . $propertyInformation['elementType'] . '>' : '');
+        $property = $schema->getProperty($propertyName);
+        return $property->getType() . ($property->getElementType() !== null ? '<' . $property->getElementType() . '>' : '');
     }
 
     /**
index 555aa9e..be459d2 100644 (file)
@@ -28,6 +28,8 @@ use TYPO3\CMS\Extbase\Annotation\Validate;
 use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
 use TYPO3\CMS\Extbase\DomainObject\AbstractValueObject;
 use TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface;
+use TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException;
+use TYPO3\CMS\Extbase\Reflection\ClassSchema\Property;
 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException;
@@ -40,6 +42,26 @@ use TYPO3\CMS\Extbase\Validation\ValidatorResolver;
 class ClassSchema
 {
     /**
+     * @var array
+     */
+    private static $propertyObjects = [];
+
+    /**
+     * @return array
+     */
+    private function buildPropertyObjects(): array
+    {
+        if (!isset(static::$propertyObjects[$this->className])) {
+            static::$propertyObjects[$this->className] = [];
+            foreach ($this->properties as $propertyName => $propertyDefinition) {
+                static::$propertyObjects[$this->className][$propertyName] = new Property($propertyName, $this->properties[$propertyName]);
+            }
+        }
+
+        return static::$propertyObjects[$this->className];
+    }
+
+    /**
      * Available model types
      */
     const MODELTYPE_ENTITY = 1;
@@ -412,27 +434,28 @@ class ClassSchema
     }
 
     /**
-     * Returns the given property defined in this schema. Check with
-     * hasProperty($propertyName) before!
+     * @throws NoSuchPropertyException
      *
      * @param string $propertyName
-     * @return array
+     * @return Property
      */
-    public function getProperty($propertyName)
+    public function getProperty(string $propertyName): Property
     {
-        return isset($this->properties[$propertyName]) && is_array($this->properties[$propertyName])
-            ? $this->properties[$propertyName]
-            : [];
+        $properties = $this->buildPropertyObjects();
+
+        if (!isset($properties[$propertyName])) {
+            throw NoSuchPropertyException::create($this->className, $propertyName);
+        }
+
+        return $properties[$propertyName];
     }
 
     /**
-     * Returns all properties defined in this schema
-     *
-     * @return array
+     * @return array|Property[]
      */
-    public function getProperties()
+    public function getProperties(): array
     {
-        return $this->properties;
+        return $this->buildPropertyObjects();
     }
 
     /**
diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassSchema/Exception/NoSuchPropertyException.php b/typo3/sysext/extbase/Classes/Reflection/ClassSchema/Exception/NoSuchPropertyException.php
new file mode 100644 (file)
index 0000000..537b578
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception;
+
+/**
+ * Class TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException
+ */
+class NoSuchPropertyException extends \Exception
+{
+    /**
+     * @param string $className
+     * @param string $propertyName
+     * @return NoSuchPropertyException
+     */
+    public static function create(string $className, string $propertyName): NoSuchPropertyException
+    {
+        return new self(
+            'Property ' . $className . '::$' . $propertyName . ' does not exist',
+            1546975326
+        );
+    }
+}
diff --git a/typo3/sysext/extbase/Classes/Reflection/ClassSchema/Property.php b/typo3/sysext/extbase/Classes/Reflection/ClassSchema/Property.php
new file mode 100644 (file)
index 0000000..7682734
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Reflection\ClassSchema;
+
+/**
+ * Class TYPO3\CMS\Extbase\Reflection\ClassSchema\Property
+ * @internal only to be used within Extbase, not part of TYPO3 Core API.
+ */
+class Property
+{
+    /**
+     * @var string
+     */
+    private $name;
+
+    /**
+     * @var array
+     */
+    private $definition;
+
+    /**
+     * @param string $name
+     * @param array $definition
+     */
+    public function __construct(string $name, array $definition)
+    {
+        $this->name = $name;
+
+        $defaults = [
+            'type' => null,
+            'elementType' => null,
+            'public' => false,
+            'protected' => false,
+            'private' => false,
+            'annotations' => [],
+            'validators' => [],
+        ];
+
+        foreach ($defaults as $key => $defaultValue) {
+            if (!isset($definition[$key])) {
+                $definition[$key] = $defaultValue;
+            }
+        }
+
+        $this->definition = $definition;
+    }
+
+    /**
+     * @return string
+     */
+    public function getName(): string
+    {
+        return $this->name;
+    }
+
+    /**
+     * Returns the type (string, integer, ...) set by the @var doc comment
+     *
+     * Returns null if type could not be evaluated
+     *
+     * @return string|null
+     */
+    public function getType(): ?string
+    {
+        return $this->definition['type'];
+    }
+
+    /**
+     * If the property is a collection of one of the types defined in
+     * \TYPO3\CMS\Extbase\Utility\TypeHandlingUtility::$collectionTypes,
+     * the element type is evaluated and represents the type of collection
+     * items inside the collection.
+     *
+     * Returns null if the property is not a collection and therefore no element type is defined.
+     *
+     * @return string|null
+     */
+    public function getElementType(): ?string
+    {
+        return $this->definition['elementType'];
+    }
+
+    /**
+     * @return bool
+     */
+    public function isPublic(): bool
+    {
+        return $this->definition['public'];
+    }
+
+    /**
+     * @return bool
+     */
+    public function isProtected(): bool
+    {
+        return $this->definition['protected'];
+    }
+
+    /**
+     * @return bool
+     */
+    public function isPrivate(): bool
+    {
+        return $this->definition['private'];
+    }
+
+    /**
+     * @param string $annotationKey
+     * @return bool
+     */
+    public function hasAnnotation(string $annotationKey): bool
+    {
+        return isset($this->definition['annotations'][$annotationKey]);
+    }
+
+    /**
+     * @param string $annotationKey
+     * @return mixed
+     */
+    public function getAnnotationValue(string $annotationKey)
+    {
+        return $this->definition['annotations'][$annotationKey] ?? null;
+    }
+
+    /**
+     * @return array
+     */
+    public function getValidators(): array
+    {
+        return $this->definition['validators'];
+    }
+
+    /**
+     * @return mixed
+     */
+    public function getDefaultValue()
+    {
+        return $this->definition['defaultValue'];
+    }
+}
index 14bcff0..32c885d 100644 (file)
@@ -141,35 +141,34 @@ class ValidatorResolver implements \TYPO3\CMS\Core\SingletonInterface
             // Model based validator
             /** @var \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator $objectValidator */
             $objectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
-            foreach ($classSchema->getProperties() as $classPropertyName => $classPropertyDefinition) {
-                /** @var array|array[] $classPropertyDefinition */
-                $classPropertyTagsValues = $classPropertyDefinition['tags'];
-
-                if (!isset($classPropertyTagsValues['var'])) {
-                    throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
+            foreach ($classSchema->getProperties() as $property) {
+                if ($property->getType() === null) {
+                    // todo: maybe we should be more graceful here and simply continue the loop.
+                    // todo: what good does it do to stop the whole execution at this point?
+                    throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $property->getName(), $targetClassName), 1363778104);
                 }
 
-                $propertyTargetClassName = $classPropertyDefinition['type'];
+                $propertyTargetClassName = $property->getType();
                 // note: the outer simpleType check reduces lookups to the class loader
                 if (!TypeHandlingUtility::isSimpleType($propertyTargetClassName)) {
                     if (TypeHandlingUtility::isCollectionType($propertyTargetClassName)) {
                         $collectionValidator = $this->createValidator(
                             \TYPO3\CMS\Extbase\Validation\Validator\CollectionValidator::class,
                             [
-                                'elementType' => $classPropertyDefinition['elementType'],
+                                'elementType' => $property->getElementType(),
                                 'validationGroups' => $validationGroups
                             ]
                         );
-                        $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
+                        $objectValidator->addPropertyValidator($property->getName(), $collectionValidator);
                     } elseif (class_exists($propertyTargetClassName) && !TypeHandlingUtility::isCoreType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\CMS\Extbase\Object\Container\Container::SCOPE_PROTOTYPE) {
                         $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName);
                         if ($validatorForProperty !== null && $validatorForProperty->count() > 0) {
-                            $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
+                            $objectValidator->addPropertyValidator($property->getName(), $validatorForProperty);
                         }
                     }
                 }
 
-                foreach ($classPropertyDefinition['validators'] as $validatorDefinition) {
+                foreach ($property->getValidators() as $validatorDefinition) {
                     // @todo: Respect validationGroups
 
                     // @todo: At this point we already have the class name of the validator, thus there is not need
@@ -179,9 +178,9 @@ class ValidatorResolver implements \TYPO3\CMS\Core\SingletonInterface
                     // @todo: createValidator must only accept FQCN's.
                     $newValidator = $this->createValidator($validatorDefinition['className'], $validatorDefinition['options']);
                     if ($newValidator === null) {
-                        throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorDefinition['className'] . '".', 1241098027);
+                        throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $property->getName() . ': Could not resolve class name for validator "' . $validatorDefinition['className'] . '".', 1241098027);
                     }
-                    $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
+                    $objectValidator->addPropertyValidator($property->getName(), $newValidator);
                 }
             }
 
index 7146014..449c532 100644 (file)
@@ -49,15 +49,13 @@ class DataMapFactoryTest extends UnitTestCase
             'foreign_table' => 'tx_myextension_bar',
             'foreign_field' => 'parentid'
         ];
-        $propertyMetaData = [
-            'type' => $className,
-            'elementType' => null
-        ];
+        $type = $className;
+        $elementType = null;
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->once())->method('setOneToOneRelation')->will($this->returnValue($mockColumnMap));
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->never())->method('setManyToManyRelation');
-        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $propertyMetaData);
+        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $type, $elementType);
     }
 
     /**
@@ -117,15 +115,13 @@ class DataMapFactoryTest extends UnitTestCase
             'foreign_table' => 'tx_myextension_bar',
             'MM' => 'tx_myextension_mm'
         ];
-        $propertyMetaData = [
-            'type' => 'Tx_Myext_Domain_Model_Foo',
-            'elementType' => null
-        ];
+        $type = 'Tx_Myext_Domain_Model_Foo';
+        $elementType = null;
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->once())->method('setManyToManyRelation')->will($this->returnValue($mockColumnMap));
-        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $propertyMetaData);
+        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $type, $elementType);
     }
 
     /**
@@ -140,15 +136,13 @@ class DataMapFactoryTest extends UnitTestCase
             'foreign_field' => 'parentid',
             'foreign_table_field' => 'parenttable'
         ];
-        $propertyMetaData = [
-            'type' => \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class,
-            'elementType' => 'Tx_Myext_Domain_Model_Foo'
-        ];
+        $type = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
+        $elementType = 'Tx_Myext_Domain_Model_Foo';
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->once())->method('setOneToManyRelation')->will($this->returnValue($mockColumnMap));
         $mockDataMapFactory->expects($this->never())->method('setManyToManyRelation');
-        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $propertyMetaData);
+        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $type, $elementType);
     }
 
     /**
@@ -166,12 +160,13 @@ class DataMapFactoryTest extends UnitTestCase
                 ['Three', 3],
             ],
         ];
-        $propertyMetaData = [];
+        $type = null;
+        $elementType = null;
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->never())->method('setManyToManyRelation');
-        $actualColumnMap = $mockDataMapFactory->_callRef('setRelations', $columnMap, $columnConfiguration, $propertyMetaData);
+        $actualColumnMap = $mockDataMapFactory->_callRef('setRelations', $columnMap, $columnConfiguration, $type, $elementType);
         $this->assertSame($columnMap::RELATION_NONE, $actualColumnMap->getTypeOfRelation());
     }
 
@@ -205,12 +200,13 @@ class DataMapFactoryTest extends UnitTestCase
                 'maxitems' => $maxitems
             ];
         }
-        $propertyMetaData = [];
+        $type = null;
+        $elementType = null;
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->never())->method('setManyToManyRelation');
-        $actualColumnMap = $mockDataMapFactory->_callRef('setRelations', $columnMap, $columnConfiguration, $propertyMetaData);
+        $actualColumnMap = $mockDataMapFactory->_callRef('setRelations', $columnMap, $columnConfiguration, $type, $elementType);
         $this->assertSame($relation, $actualColumnMap->getTypeOfRelation());
     }
 
@@ -225,15 +221,13 @@ class DataMapFactoryTest extends UnitTestCase
             'foreign_table' => 'tx_myextension_bar',
             'MM' => 'tx_myextension_mm'
         ];
-        $propertyMetaData = [
-            'type' => \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class,
-            'elementType' => 'Tx_Myext_Domain_Model_Foo'
-        ];
+        $type = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
+        $elementType = 'Tx_Myext_Domain_Model_Foo';
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->once())->method('setManyToManyRelation')->will($this->returnValue($mockColumnMap));
-        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $propertyMetaData);
+        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $type, $elementType);
     }
 
     /**
@@ -247,15 +241,13 @@ class DataMapFactoryTest extends UnitTestCase
             'foreign_table' => 'tx_myextension_righttable',
             'MM' => 'tx_myextension_mm'
         ];
-        $propertyMetaData = [
-            'type' => \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class,
-            'elementType' => 'Tx_Myext_Domain_Model_Foo'
-        ];
+        $type = \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class;
+        $elementType = 'Tx_Myext_Domain_Model_Foo';
         $mockDataMapFactory = $this->getAccessibleMock(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory::class, ['setOneToOneRelation', 'setOneToManyRelation', 'setManyToManyRelation'], [], '', false);
         $mockDataMapFactory->expects($this->never())->method('setOneToOneRelation');
         $mockDataMapFactory->expects($this->never())->method('setOneToManyRelation');
         $mockDataMapFactory->expects($this->once())->method('setManyToManyRelation')->will($this->returnValue($mockColumnMap));
-        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $propertyMetaData);
+        $mockDataMapFactory->_callRef('setRelations', $mockColumnMap, $columnConfiguration, $type, $elementType);
     }
 
     /**
index f02ff06..79c6e50 100644 (file)
@@ -153,10 +153,13 @@ class PersistentObjectConverterTest extends UnitTestCase
 
         $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue('TheTargetType'));
         $mockSchema->expects($this->any())->method('hasProperty')->with('thePropertyName')->will($this->returnValue(true));
-        $mockSchema->expects($this->any())->method('getProperty')->with('thePropertyName')->will($this->returnValue([
-            'type' => 'TheTypeOfSubObject',
-            'elementType' => null
-        ]));
+        $mockSchema->expects($this->any())->method('getProperty')->with('thePropertyName')->will($this->returnValue(new ClassSchema\Property(
+            'thePropertyName',
+            [
+                'type' => 'TheTypeOfSubObject',
+                'elementType' => null
+            ]
+        )));
         $configuration = $this->buildConfiguration([]);
         $this->assertEquals('TheTypeOfSubObject', $this->converter->getTypeOfChildProperty('TheTargetType', 'thePropertyName', $configuration));
     }
diff --git a/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchema/PropertyTest.php b/typo3/sysext/extbase/Tests/Unit/Reflection/ClassSchema/PropertyTest.php
new file mode 100644 (file)
index 0000000..7e84e16
--- /dev/null
@@ -0,0 +1,174 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection\ClassSchema;
+
+/*
+ * 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\ObjectStorage;
+use TYPO3\CMS\Extbase\Reflection\ClassSchema;
+use TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyClassWithAllTypesOfProperties;
+use TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyClassWithLazyDoctrineAnnotation;
+use TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\DummyModel;
+use TYPO3\CMS\Extbase\Tests\Unit\Reflection\Fixture\Validation\Validator\DummyValidator;
+use TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator;
+use TYPO3\CMS\Extbase\Validation\Validator\StringLengthValidator;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Class TYPO3\CMS\Extbase\Tests\Unit\Reflection\PropertyTest
+ */
+class PropertyTest extends UnitTestCase
+{
+    /**
+     * @test
+     */
+    public function classSchemaDetectsPropertiesWithLazyAnnotation(): void
+    {
+        $classSchema = new ClassSchema(DummyClassWithLazyDoctrineAnnotation::class);
+        static::assertTrue($classSchema->getProperty('propertyWithLazyAnnotation')->getAnnotationValue('lazy'));
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsPropertyVisibility(): void
+    {
+        $classSchema = new ClassSchema(DummyClassWithAllTypesOfProperties::class);
+
+        $property = $classSchema->getProperty('publicProperty');
+        static::assertTrue($property->isPublic());
+        static::assertFalse($property->isProtected());
+        static::assertFalse($property->isPrivate());
+
+        $property = $classSchema->getProperty('protectedProperty');
+        static::assertFalse($property->isPublic());
+        static::assertTrue($property->isProtected());
+        static::assertFalse($property->isPrivate());
+
+        $property = $classSchema->getProperty('privateProperty');
+        static::assertFalse($property->isPublic());
+        static::assertFalse($property->isProtected());
+        static::assertTrue($property->isPrivate());
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsInjectProperty(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithInjectAnnotation');
+
+        static::assertTrue($property->hasAnnotation('inject'));
+        static::assertTrue($property->getAnnotationValue('inject'));
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsTransientProperty(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithTransientAnnotation');
+
+        static::assertTrue($property->hasAnnotation('transient'));
+        static::assertTrue($property->getAnnotationValue('transient'));
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsCascadeProperty(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithCascadeAnnotation');
+
+        static::assertTrue($property->hasAnnotation('cascade'));
+        static::assertSame('remove', $property->getAnnotationValue('cascade'));
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsCascadePropertyOnlyWithVarAnnotation(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithCascadeAnnotationWithoutVarAnnotation');
+
+        static::assertFalse($property->hasAnnotation('cascade'));
+        static::assertNull($property->getAnnotationValue('cascade'));
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsTypeAndElementType(): void
+    {
+        $property = (new ClassSchema(DummyClassWithAllTypesOfProperties::class))
+            ->getProperty('propertyWithObjectStorageAnnotation');
+
+        static::assertSame(ObjectStorage::class, $property->getType());
+        static::assertSame(DummyClassWithAllTypesOfProperties::class, $property->getElementType());
+    }
+
+    /**
+     * @test
+     */
+    public function classSchemaDetectsValidateAnnotationsModelProperties(): void
+    {
+        $this->resetSingletonInstances = true;
+        $property = (new ClassSchema(DummyModel::class))
+            ->getProperty('propertyWithValidateAnnotations');
+
+        static::assertSame(
+            [
+                [
+                    'name' => 'StringLength',
+                    'options' => [
+                        'minimum' => 1,
+                        'maximum' => 10,
+                    ],
+                    'className' => StringLengthValidator::class
+                ],
+                [
+                    'name' => 'NotEmpty',
+                    'options' => [],
+                    'className' => NotEmptyValidator::class
+                ],
+                [
+                    'name' => 'TYPO3.CMS.Extbase:NotEmpty',
+                    'options' => [],
+                    'className' => NotEmptyValidator::class
+                ],
+                [
+                    'name' => 'TYPO3.CMS.Extbase.Tests.Unit.Reflection.Fixture:DummyValidator',
+                    'options' => [],
+                    'className' => DummyValidator::class
+                ],
+                [
+                    'name' => '\TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator',
+                    'options' => [],
+                    'className' => NotEmptyValidator::class
+                ],
+                [
+                    'name' => NotEmptyValidator::class,
+                    'options' => [],
+                    'className' => NotEmptyValidator::class
+                ]
+            ],
+            $property->getValidators()
+        );
+    }
+}
index c318c67..f7ee5ee 100644 (file)
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Reflection;
  */
 
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
 use TYPO3\CMS\Extbase\Reflection\ClassSchema;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException;
 use TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException;
@@ -214,15 +213,6 @@ class ClassSchemaTest extends UnitTestCase
     /**
      * @test
      */
-    public function classSchemaDetectsPropertiesWithLazyAnnotation()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithLazyDoctrineAnnotation::class);
-        static::assertTrue($classSchema->getProperty('propertyWithLazyAnnotation')['annotations']['lazy']);
-    }
-
-    /**
-     * @test
-     */
     public function classSchemaDetectsStaticMethods()
     {
         $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfMethods::class);
@@ -283,74 +273,7 @@ class ClassSchemaTest extends UnitTestCase
         $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
 
         $propertyDefinition = $classSchema->getProperty('publicPropertyWithDefaultValue');
-        static::assertSame('foo', $propertyDefinition['defaultValue']);
-    }
-
-    /**
-     * @test
-     */
-    public function classSchemaDetectsPropertyVisibility()
-    {
-        $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']);
-    }
-
-    /**
-     * @test
-     */
-    public function classSchemaDetectsInjectProperty()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
-
-        $propertyDefinition = $classSchema->getProperty('propertyWithInjectAnnotation');
-        static::assertTrue($propertyDefinition['annotations']['inject']);
-    }
-
-    /**
-     * @test
-     */
-    public function classSchemaDetectsTransientProperty()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
-
-        $propertyDefinition = $classSchema->getProperty('propertyWithTransientAnnotation');
-        static::assertTrue($propertyDefinition['annotations']['transient']);
-    }
-
-    /**
-     * @test
-     */
-    public function classSchemaDetectsCascadeProperty()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
-
-        $propertyDefinition = $classSchema->getProperty('propertyWithCascadeAnnotation');
-        static::assertSame('remove', $propertyDefinition['annotations']['cascade']);
-    }
-
-    /**
-     * @test
-     */
-    public function classSchemaDetectsCascadePropertyOnlyWithVarAnnotation()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
-
-        $propertyDefinition = $classSchema->getProperty('propertyWithCascadeAnnotationWithoutVarAnnotation');
-        static::assertNull($propertyDefinition['annotations']['cascade']);
+        static::assertSame('foo', $propertyDefinition->getDefaultValue());
     }
 
     /**
@@ -368,18 +291,6 @@ class ClassSchemaTest extends UnitTestCase
     /**
      * @test
      */
-    public function classSchemaDetectsTypeAndElementType()
-    {
-        $classSchema = new ClassSchema(Fixture\DummyClassWithAllTypesOfProperties::class);
-
-        $propertyDefinition = $classSchema->getProperty('propertyWithObjectStorageAnnotation');
-        static::assertSame(ObjectStorage::class, $propertyDefinition['type']);
-        static::assertSame(Fixture\DummyClassWithAllTypesOfProperties::class, $propertyDefinition['elementType']);
-    }
-
-    /**
-     * @test
-     */
     public function classSchemaDetectsSingletons()
     {
         static::assertTrue((new ClassSchema(Fixture\DummySingleton::class))->isSingleton());
@@ -459,53 +370,6 @@ class ClassSchemaTest extends UnitTestCase
     /**
      * @test
      */
-    public function classSchemaDetectsValidateAnnotationsModelProperties(): void
-    {
-        $this->resetSingletonInstances = true;
-        $classSchema = new ClassSchema(Fixture\DummyModel::class);
-        static::assertSame(
-            [
-                [
-                    'name' => 'StringLength',
-                    'options' => [
-                        'minimum' => 1,
-                        'maximum' => 10,
-                    ],
-                    'className' => StringLengthValidator::class
-                ],
-                [
-                    'name' => 'NotEmpty',
-                    'options' => [],
-                    'className' => NotEmptyValidator::class
-                ],
-                [
-                    'name' => 'TYPO3.CMS.Extbase:NotEmpty',
-                    'options' => [],
-                    'className' => NotEmptyValidator::class
-                ],
-                [
-                    'name' => 'TYPO3.CMS.Extbase.Tests.Unit.Reflection.Fixture:DummyValidator',
-                    'options' => [],
-                    'className' => Fixture\Validation\Validator\DummyValidator::class
-                ],
-                [
-                    'name' => '\TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator',
-                    'options' => [],
-                    'className' => NotEmptyValidator::class
-                ],
-                [
-                    'name' => NotEmptyValidator::class,
-                    'options' => [],
-                    'className' => NotEmptyValidator::class
-                ]
-            ],
-            $classSchema->getProperty('propertyWithValidateAnnotations')['validators']
-        );
-    }
-
-    /**
-     * @test
-     */
     public function classSchemaDetectsValidateAnnotationsOfControllerActions(): void
     {
         $this->resetSingletonInstances = true;