‪TYPO3CMS  ‪main
ValidatorResolver.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Symfony\Component\PropertyInfo\Type;
31 
38 {
39  protected array ‪$baseValidatorConjunctions = [];
40 
41  public function ‪__construct(protected readonly ‪ReflectionService $reflectionService) {}
42 
50  public function ‪createValidator(string $validatorType, array $validatorOptions = []): ?‪ValidatorInterface
51  {
52  try {
53  $validatorObjectName = ‪ValidatorClassNameResolver::resolve($validatorType);
55  ‪$validator = GeneralUtility::makeInstance($validatorObjectName);
56  ‪$validator->setOptions($validatorOptions);
57  return ‪$validator;
58  } catch (‪NoSuchValidatorException $e) {
59  GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__)->debug($e->getMessage());
60  return null;
61  }
62  }
63 
70  public function ‪getBaseValidatorConjunction(string $targetClassName): ‪ConjunctionValidator
71  {
72  if (!isset($this->baseValidatorConjunctions[$targetClassName])) {
73  $conjunctionValidator = GeneralUtility::makeInstance(ConjunctionValidator::class);
74  $this->baseValidatorConjunctions[$targetClassName] = $conjunctionValidator;
75  // The simpleType check reduces lookups to the class loader
76  if (!‪TypeHandlingUtility::isSimpleType($targetClassName) && class_exists($targetClassName)) {
77  $this->‪buildBaseValidatorConjunction($conjunctionValidator, $targetClassName);
78  }
79  }
80  return $this->baseValidatorConjunctions[$targetClassName];
81  }
82 
103  protected function ‪buildBaseValidatorConjunction(‪ConjunctionValidator $conjunctionValidator, string $targetClassName): void
104  {
105  $classSchema = $this->reflectionService->getClassSchema($targetClassName);
106 
107  // Model based validator
109  $objectValidator = $this->‪createValidator(GenericObjectValidator::class);
110  foreach ($classSchema->getProperties() as $property) {
111  $primaryType = $property->getPrimaryType();
112  if (!$primaryType instanceof Type) {
113  // @todo: The type is only necessary here for further analyzing whether it's a simple type or
114  // a collection. If this is evaluated in the ClassSchema, this whole code part is not needed
115  // any longer and can be removed.
116  throw new \InvalidArgumentException(
117  sprintf('There is no @var annotation or type declaration for property "%s" in class "%s".', $property->getName(), $targetClassName),
118  1363778104
119  );
120  }
121 
122  $propertyTargetClassName = $primaryType->getClassName() ?? $primaryType->getBuiltinType();
123 
124  if (!‪TypeHandlingUtility::isSimpleType($propertyTargetClassName)) {
125  // The outer simpleType check reduces lookups to the class loader
126  // @todo: Whether the property holds a simple type or not and whether it holds a collection is known in
127  // in the ClassSchema. The information could be made available and not evaluated here again.
128  $primaryCollectionValueType = $property->getPrimaryCollectionValueType();
129  if ($primaryType->isCollection() && $primaryCollectionValueType instanceof Type) {
131  $collectionValidator = $this->‪createValidator(
132  CollectionValidator::class,
133  [
134  'elementType' => $primaryCollectionValueType->getClassName() ?? $primaryCollectionValueType->getBuiltinType(),
135  ]
136  );
137  $objectValidator->addPropertyValidator($property->getName(), $collectionValidator);
138  } elseif (class_exists($propertyTargetClassName)
139  && !‪TypeHandlingUtility::isCoreType($propertyTargetClassName)
140  && !in_array(SingletonInterface::class, class_implements($propertyTargetClassName) ?: [], true)
141  ) {
142  // class_exists($propertyTargetClassName) checks, if the type of the property is an object
143  // instead of a simple type. Like DateTime or another model.
144  //
145  // !TypeHandlingUtility::isCoreType($propertyTargetClassName) checks if the type of the property
146  // is not a core type, which are Enums and File objects for example.
147  // @todo: check why these types shouldn't be validated.
148  //
149  // !in_array(SingletonInterface::class, class_implements($propertyTargetClassName, true), true)
150  // checks if the class is an instance of a Singleton
151  // @todo: check why Singletons shouldn't be validated.
152  //
153  // (Alexander Schnitzler) By looking at this code I assume that this is the path for 1:1
154  // relations in models. Still, the question remains why it excludes core types and singletons.
155  // It makes sense on a theoretical level, but I don't see a technical issue allowing these as
156  // well.
157  $validatorForProperty = $this->‪getBaseValidatorConjunction($propertyTargetClassName);
158  if ($validatorForProperty->count() > 0) {
159  $objectValidator->addPropertyValidator($property->getName(), $validatorForProperty);
160  }
161  }
162  }
163 
164  foreach ($property->getValidators() as $validatorDefinition) {
165  // @todo: At this point we already have the class name of the validator, thus there is not need
166  // calling ValidatorClassNameResolver::resolve inside
167  // \TYPO3\CMS\Extbase\Validation\ValidatorResolver::createValidator once again. However, to
168  // keep things simple for now, we still use the method createValidator here. In the future,
169  // createValidator must only accept FQCN's.
170  $newValidator = $this->‪createValidator($validatorDefinition['className'], $validatorDefinition['options']);
171  if ($newValidator === null) {
172  throw new ‪NoSuchValidatorException(
173  'Invalid validate annotation in ' . $targetClassName . '::' . $property->getName() . ': ' .
174  'Could not resolve class name for validator "' . $validatorDefinition['className'] . '".',
175  1241098027
176  );
177  }
178  $objectValidator->addPropertyValidator($property->getName(), $newValidator);
179  }
180  }
181 
182  if (!empty($objectValidator->getPropertyValidators())) {
183  $conjunctionValidator->‪addValidator($objectValidator);
184  }
185  }
186 }
‪TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator
Definition: ConjunctionValidator.php:26
‪TYPO3\CMS\Extbase\Utility\TypeHandlingUtility\isSimpleType
‪static isSimpleType(string $type)
Definition: TypeHandlingUtility.php:107
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver\getBaseValidatorConjunction
‪getBaseValidatorConjunction(string $targetClassName)
Definition: ValidatorResolver.php:70
‪TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
Definition: NoSuchValidatorException.php:25
‪TYPO3\CMS\Extbase\Validation\Validator\CollectionValidator
Definition: CollectionValidator.php:29
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver\$baseValidatorConjunctions
‪array $baseValidatorConjunctions
Definition: ValidatorResolver.php:39
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver
Definition: ValidatorResolver.php:38
‪TYPO3\CMS\Extbase\Utility\TypeHandlingUtility\isCoreType
‪static isCoreType($type)
Definition: TypeHandlingUtility.php:117
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver\createValidator
‪createValidator(string $validatorType, array $validatorOptions=[])
Definition: ValidatorResolver.php:50
‪TYPO3\CMS\Extbase\Reflection\ReflectionService
Definition: ReflectionService.php:28
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver\__construct
‪__construct(protected readonly ReflectionService $reflectionService)
Definition: ValidatorResolver.php:41
‪TYPO3\CMS\Extbase\Utility\TypeHandlingUtility
Definition: TypeHandlingUtility.php:29
‪TYPO3\CMS\Extbase\Validation\Validator\AbstractCompositeValidator\addValidator
‪addValidator(ValidatorInterface $validator)
Definition: AbstractCompositeValidator.php:54
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:262
‪TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator
Definition: GenericObjectValidator.php:24
‪TYPO3\CMS\Extbase\Validation\ValidatorResolver\buildBaseValidatorConjunction
‪buildBaseValidatorConjunction(ConjunctionValidator $conjunctionValidator, string $targetClassName)
Definition: ValidatorResolver.php:103
‪TYPO3\CMS\Extbase\Validation
Definition: Error.php:16
‪TYPO3\CMS\Extbase\Validation\ValidatorClassNameResolver\resolve
‪static resolve(string $validatorIdentifier)
Definition: ValidatorClassNameResolver.php:44
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:33
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface
Definition: ValidatorInterface.php:26