‪TYPO3CMS  ‪main
ClassSchema.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 Doctrine\Common\Annotations\AnnotationReader;
21 use phpDocumentor\Reflection\DocBlock\Tags\Param;
22 use phpDocumentor\Reflection\DocBlockFactory;
23 use phpDocumentor\Reflection\DocBlockFactoryInterface;
24 use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
25 use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
26 use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
27 use Symfony\Component\PropertyInfo\Type;
44 
50 {
51  private const ‪BIT_CLASS_IS_CONTROLLER = 1 << 3;
52 
56  private ‪$bitSet;
57 
61  private static ‪$propertyObjects = [];
62 
66  private static ‪$methodObjects = [];
67 
73  protected ‪$className;
74 
80  protected ‪$properties = [];
81 
85  private ‪$methods = [];
86 
87  private static ?PropertyInfoExtractor ‪$propertyInfoExtractor = null;
88  private static ?DocBlockFactoryInterface ‪$docBlockFactory = null;
89 
98  public function ‪__construct(string ‪$className)
99  {
101  $this->className = ‪$className;
102  $this->bitSet = new ‪BitSet();
103 
104  $reflectionClass = new \ReflectionClass(‪$className);
105 
106  if ($reflectionClass->implementsInterface(ControllerInterface::class)) {
107  $this->bitSet->set(self::BIT_CLASS_IS_CONTROLLER);
108  }
109 
110  if (self::$propertyInfoExtractor === null) {
111  ‪$docBlockFactory = DocBlockFactory::createInstance();
112  $phpDocExtractor = new PhpDocExtractor(‪$docBlockFactory);
113 
114  $reflectionExtractor = new ReflectionExtractor();
115 
116  self::$propertyInfoExtractor = new PropertyInfoExtractor(
117  [],
118  [$phpDocExtractor, $reflectionExtractor]
119  );
120  }
121 
122  if (self::$docBlockFactory === null) {
123  self::$docBlockFactory = DocBlockFactory::createInstance([
124  'author' => Null_::class,
125  'covers' => Null_::class,
126  'deprecated' => Null_::class,
127  'link' => Null_::class,
128  'method' => Null_::class,
129  'property-read' => Null_::class,
130  'property' => Null_::class,
131  'property-write' => Null_::class,
132  'return' => Null_::class,
133  'see' => Null_::class,
134  'since' => Null_::class,
135  'source' => Null_::class,
136  'throw' => Null_::class,
137  'throws' => Null_::class,
138  'uses' => Null_::class,
139  'var' => Null_::class,
140  'version' => Null_::class,
141  ]);
142  }
143 
144  $this->‪reflectProperties($reflectionClass);
145  $this->‪reflectMethods($reflectionClass);
146  }
147 
152  protected function ‪reflectProperties(\ReflectionClass $reflectionClass): void
153  {
154  $annotationReader = new AnnotationReader();
155 
156  foreach ($reflectionClass->getProperties() as $reflectionProperty) {
157  if ($reflectionProperty->isStatic()) {
158  continue;
159  }
160 
161  $propertyName = $reflectionProperty->getName();
162 
163  $propertyCharacteristicsBit = 0;
164  $propertyCharacteristicsBit += $reflectionProperty->isPrivate() ? ‪PropertyCharacteristics::VISIBILITY_PRIVATE : 0;
165  $propertyCharacteristicsBit += $reflectionProperty->isProtected() ? ‪PropertyCharacteristics::VISIBILITY_PROTECTED : 0;
166  $propertyCharacteristicsBit += $reflectionProperty->isPublic() ? ‪PropertyCharacteristics::VISIBILITY_PUBLIC : 0;
167 
168  $this->properties[$propertyName] = [
169  'c' => null, // cascade
170  't' => null, // type
171  'v' => [], // validators
172  ];
173 
174  $validateAttributes = [];
175  foreach ($reflectionProperty->getAttributes() as $attribute) {
176  match ($attribute->getName()) {
177  Validate::class => $validateAttributes[] = $attribute,
178  Lazy::class => $propertyCharacteristicsBit += ‪PropertyCharacteristics::ANNOTATED_LAZY,
179  Transient::class => $propertyCharacteristicsBit += ‪PropertyCharacteristics::ANNOTATED_TRANSIENT,
180  Cascade::class => $this->properties[$propertyName]['c'] = ($attribute->newInstance())->value,
181  default => '' // non-extbase attributes
182  };
183  }
184  foreach ($validateAttributes as $attribute) {
185  ‪$validator = $attribute->newInstance();
186  $validatorObjectName = ‪ValidatorClassNameResolver::resolve(‪$validator->validator);
187 
188  $this->properties[$propertyName]['v'][] = [
189  'name' => ‪$validator->validator,
190  'options' => ‪$validator->options,
191  'className' => $validatorObjectName,
192  ];
193  }
194 
195  $annotations = $annotationReader->getPropertyAnnotations($reflectionProperty);
196 
198  $validateAnnotations = array_filter(
199  $annotations,
200  static fn(object $annotation): bool => $annotation instanceof ‪Validate
201  );
202 
203  if (count($validateAnnotations) > 0) {
204  foreach ($validateAnnotations as $validateAnnotation) {
205  $validatorObjectName = ‪ValidatorClassNameResolver::resolve($validateAnnotation->validator);
206 
207  $this->properties[$propertyName]['v'][] = [
208  'name' => $validateAnnotation->validator,
209  'options' => $validateAnnotation->options,
210  'className' => $validatorObjectName,
211  ];
212  }
213  }
214 
215  if ($annotationReader->getPropertyAnnotation($reflectionProperty, Lazy::class) instanceof ‪Lazy) {
216  $propertyCharacteristicsBit += ‪PropertyCharacteristics::ANNOTATED_LAZY;
217  }
218 
219  if ($annotationReader->getPropertyAnnotation($reflectionProperty, Transient::class) instanceof ‪Transient) {
220  $propertyCharacteristicsBit += ‪PropertyCharacteristics::ANNOTATED_TRANSIENT;
221  }
222 
223  $this->properties[$propertyName]['propertyCharacteristicsBit'] = $propertyCharacteristicsBit;
224 
226  $types = (array)self::$propertyInfoExtractor->getTypes($this->className, $propertyName, ['reflectionProperty' => $reflectionProperty]);
227 
228  if ($types !== [] && ($annotation = $annotationReader->getPropertyAnnotation($reflectionProperty, Cascade::class)) instanceof ‪Cascade) {
230  $this->properties[$propertyName]['c'] = $annotation->value;
231  }
232 
233  foreach ($types as $type) {
234  $this->properties[$propertyName]['t'][] = $type;
235  }
236  }
237  }
238 
246  protected function ‪reflectMethods(\ReflectionClass $reflectionClass): void
247  {
248  $annotationReader = new AnnotationReader();
249 
250  foreach ($reflectionClass->getMethods() as $reflectionMethod) {
251  if ($reflectionMethod->isStatic()) {
252  continue;
253  }
254 
255  $methodName = $reflectionMethod->getName();
256 
257  $this->methods[$methodName] = [];
258  $this->methods[$methodName]['private'] = $reflectionMethod->isPrivate();
259  $this->methods[$methodName]['protected'] = $reflectionMethod->isProtected();
260  $this->methods[$methodName]['public'] = $reflectionMethod->isPublic();
261  $this->methods[$methodName]['params'] = [];
262  $isAction = $this->bitSet->get(self::BIT_CLASS_IS_CONTROLLER) && str_ends_with($methodName, 'Action');
263 
264  $argumentValidators = [];
265 
266  $validateAttributes = [];
267  $reflectionAttributes = $reflectionMethod->getAttributes();
268  foreach ($reflectionAttributes as $attribute) {
269  match ($attribute->getName()) {
270  Validate::class => $validateAttributes[] = $attribute,
271  default => '' // non-extbase attributes
272  };
273  }
274 
275  $annotations = $annotationReader->getMethodAnnotations($reflectionMethod);
276 
278  $validateAnnotations = array_filter(
279  $annotations,
280  static fn(object $annotation): bool => $annotation instanceof Validate
281  );
282 
283  if ($isAction && (count($validateAnnotations) > 0 || $validateAttributes !== [])) {
284  foreach ($validateAnnotations as $validateAnnotation) {
285  $validatorName = $validateAnnotation->validator;
286  $validatorObjectName = ‪ValidatorClassNameResolver::resolve($validatorName);
287 
288  $argumentValidators[$validateAnnotation->param][] = [
289  'name' => $validatorName,
290  'options' => $validateAnnotation->options,
291  'className' => $validatorObjectName,
292  ];
293  }
294  foreach ($validateAttributes as $attribute) {
295  ‪$validator = $attribute->newInstance();
296  $validatorObjectName = ‪ValidatorClassNameResolver::resolve(‪$validator->validator);
297 
298  $argumentValidators[‪$validator->param][] = [
299  'name' => ‪$validator->validator,
300  'options' => ‪$validator->options,
301  'className' => $validatorObjectName,
302  ];
303  }
304  }
305 
306  $docComment = $reflectionMethod->getDocComment();
307  $docComment = is_string($docComment) ? $docComment : '';
308 
309  foreach ($reflectionMethod->getParameters() as $parameterPosition => $reflectionParameter) {
310  $parameterName = $reflectionParameter->getName();
311 
312  $ignoreValidationParameters = array_filter(
313  $annotations,
314  static fn(object $annotation): bool => $annotation instanceof IgnoreValidation && $annotation->argumentName === $parameterName
315  );
316 
317  $ignoreValidationParametersFromAttribute = array_filter(
318  $reflectionAttributes,
319  static fn(\ReflectionAttribute $attribute): bool
320  => $attribute->getName() === IgnoreValidation::class && $attribute->newInstance()->argumentName === $parameterName
321  );
322 
323  $reflectionType = $reflectionParameter->getType();
324 
325  $this->methods[$methodName]['params'][$parameterName] = [];
326  $this->methods[$methodName]['params'][$parameterName]['array'] = false; // compat
327  $this->methods[$methodName]['params'][$parameterName]['optional'] = $reflectionParameter->isOptional();
328  $this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionParameter->allowsNull();
329  $this->methods[$methodName]['params'][$parameterName]['type'] = null;
330  $this->methods[$methodName]['params'][$parameterName]['hasDefaultValue'] = $reflectionParameter->isDefaultValueAvailable();
331  $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = null;
332  $this->methods[$methodName]['params'][$parameterName]['ignoreValidation'] = $ignoreValidationParameters !== [] || $ignoreValidationParametersFromAttribute !== [];
333  $this->methods[$methodName]['params'][$parameterName]['validators'] = [];
334 
335  if ($reflectionParameter->isDefaultValueAvailable()) {
336  $this->methods[$methodName]['params'][$parameterName]['defaultValue'] = $reflectionParameter->getDefaultValue();
337  }
338 
339  // A ReflectionNamedType means "there is a type specified, and it's not a union type."
340  // (Union types are not handled, currently.)
341  if ($reflectionType instanceof \ReflectionNamedType) {
342  $this->methods[$methodName]['params'][$parameterName]['allowsNull'] = $reflectionType->allowsNull();
343  // A built-in type effectively means "not a class".
344  if ($reflectionType->isBuiltin()) {
345  $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($reflectionType->getName(), '\\');
346  } elseif ($reflectionType->getName() === 'self') {
347  // In addition, self cannot be resolved by "new \ReflectionClass('self')",
348  // so treat this as a reference to the current class
349  $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim($reflectionClass->getName(), '\\');
350  } else {
351  // This is mainly to confirm that the class exists. If it doesn't, a ReflectionException
352  // will be thrown. It's not the ideal way of doing so, but it maintains the existing API
353  // so that the exception can get caught and recast to a TYPO3-specific exception.
355  $classname = $reflectionType->getName();
356  $reflection = new \ReflectionClass($classname);
357  // There's a single type declaration that is a class.
358  $this->methods[$methodName]['params'][$parameterName]['type'] = $reflectionType->getName();
359  }
360  }
361 
362  $typeDetectedViaDocBlock = false;
363  if ($docComment !== '' && $this->methods[$methodName]['params'][$parameterName]['type'] === null) {
364  /*
365  * We create (redundant) instances here in this loop due to the fact that
366  * we do not want to analyse all doc blocks of all available methods. We
367  * use this technique only if we couldn't grasp all necessary data via
368  * reflection.
369  *
370  * Also, if we analyze all method doc blocks, we will trigger numerous errors
371  * due to non PSR-5 compatible tags in the core and in user land code.
372  *
373  * Fetching the data type via doc blocks is deprecated and will be removed in the near future.
374  * Currently, this affects at least fooAction() ActionController methods, which does not
375  * deprecate non-PHP-type-hinted methods.
376  */
377  $params = self::$docBlockFactory->create($docComment)
378  ->getTagsByName('param');
379 
380  if (isset($params[$parameterPosition])) {
382  $param = $params[$parameterPosition];
383  $this->methods[$methodName]['params'][$parameterName]['type'] = ltrim((string)$param->getType(), '\\');
384  $typeDetectedViaDocBlock = true;
385  }
386  }
387 
388  // Extbase Validation
389  if (isset($argumentValidators[$parameterName])) {
390  if ($this->methods[$methodName]['params'][$parameterName]['type'] === null) {
391  throw new InvalidTypeHintException(
392  'Missing type information for parameter "$' . $parameterName . '" in ' . $this->className . '->' . $methodName . '(): Use a type hint.',
393  1515075192
394  );
395  }
396  if ($typeDetectedViaDocBlock) {
397  $parameterType = $this->methods[$methodName]['params'][$parameterName]['type'];
398  $errorMessage = <<<MESSAGE
399 The type ($parameterType) of parameter \$$parameterName of method $this->className::$methodName() is defined via php DocBlock. Use a proper PHP parameter type hint instead:
400 [private|protected|public] ‪function $methodName($parameterType \$$parameterName)
401 MESSAGE;
402  throw new \RuntimeException($errorMessage, 1639224354);
403  }
404 
405  $this->methods[$methodName]['params'][$parameterName]['validators'] = $argumentValidators[$parameterName];
406  unset($argumentValidators[$parameterName]);
407  }
408  }
409 
410  // Extbase Validation
411  foreach ($argumentValidators as $parameterName => $validators) {
412  $validatorNames = array_column($validators, 'name');
413 
414  throw new InvalidValidationConfigurationException(
415  'Invalid validate annotation in ' . $this->className . '->' . $methodName . '(): The following validators have been defined for missing param "$' . $parameterName . '": ' . implode(', ', $validatorNames),
416  1515073585
417  );
418  }
419  }
420  }
421 
425  public function ‪getProperty(string $propertyName): Property
426  {
428 
429  if (!isset(‪$properties[$propertyName])) {
430  throw ‪NoSuchPropertyException::create($this->className, $propertyName);
431  }
432 
433  return ‪$properties[$propertyName];
434  }
435 
439  public function ‪getProperties(): array
440  {
441  return $this->‪buildPropertyObjects();
442  }
443 
450  public function ‪getDomainObjectProperties(): array
451  {
452  return array_filter(
453  $this->‪getProperties(),
454  static fn(Property $property): bool => !str_starts_with($property->getName(), '_')
455  );
456  }
457 
463  public function ‪hasProperty(string $propertyName): bool
464  {
465  return array_key_exists($propertyName, $this->properties);
466  }
467 
471  public function ‪getMethod(string $methodName): ‪Method
472  {
474 
475  if (!isset(‪$methods[$methodName])) {
476  throw ‪NoSuchMethodException::create($this->className, $methodName);
477  }
478 
479  return ‪$methods[$methodName];
480  }
481 
485  public function ‪getMethods(): array
486  {
487  return $this->‪buildMethodObjects();
488  }
489 
490  public function ‪hasMethod(string $methodName): bool
491  {
492  return isset($this->methods[$methodName]);
493  }
494 
498  private function ‪buildPropertyObjects(): array
499  {
500  if (!isset(self::$propertyObjects[$this->className])) {
501  self::$propertyObjects[‪$this->className] = [];
502  foreach ($this->properties as $propertyName => $propertyDefinition) {
503  self::$propertyObjects[‪$this->className][$propertyName] = new ‪Property($propertyName, $propertyDefinition);
504  }
505  }
506 
507  return self::$propertyObjects[‪$this->className];
508  }
509 
513  private function ‪buildMethodObjects(): array
514  {
515  if (!isset(self::$methodObjects[$this->className])) {
516  self::$methodObjects[‪$this->className] = [];
517  foreach ($this->methods as $methodName => $methodDefinition) {
518  self::$methodObjects[‪$this->className][$methodName] = new ‪Method($methodName, $methodDefinition, $this->className);
519  }
520  }
521 
522  return self::$methodObjects[‪$this->className];
523  }
524 }
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException
Definition: NoSuchPropertyException.php:24
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchPropertyException\create
‪static create(string $className, string $propertyName)
Definition: NoSuchPropertyException.php:25
‪TYPO3\CMS\Extbase\Annotation\IgnoreValidation
Definition: IgnoreValidation.php:26
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\buildMethodObjects
‪array Method[] buildMethodObjects()
Definition: ClassSchema.php:507
‪TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException
Definition: InvalidValidationConfigurationException.php:25
‪TYPO3\CMS\Extbase\function
‪static return function(ContainerConfigurator $containerConfigurator, ContainerBuilder $container)
Definition: Services.php:13
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$docBlockFactory
‪static DocBlockFactoryInterface $docBlockFactory
Definition: ClassSchema.php:82
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\buildPropertyObjects
‪array Property[] buildPropertyObjects()
Definition: ClassSchema.php:492
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodException\create
‪static create(string $className, string $methodName)
Definition: NoSuchMethodException.php:25
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics\ANNOTATED_LAZY
‪const ANNOTATED_LAZY
Definition: PropertyCharacteristics.php:30
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getProperty
‪getProperty(string $propertyName)
Definition: ClassSchema.php:419
‪TYPO3\CMS\Extbase\Reflection\DocBlock\Tags\Null_
Definition: Null_.php:28
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics\VISIBILITY_PUBLIC
‪const VISIBILITY_PUBLIC
Definition: PropertyCharacteristics.php:29
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$bitSet
‪BitSet $bitSet
Definition: ClassSchema.php:55
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics\VISIBILITY_PRIVATE
‪const VISIBILITY_PRIVATE
Definition: PropertyCharacteristics.php:27
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics
Definition: PropertyCharacteristics.php:26
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\hasMethod
‪hasMethod(string $methodName)
Definition: ClassSchema.php:484
‪TYPO3\CMS\Extbase\Annotation\ORM\Lazy
Definition: Lazy.php:25
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$propertyInfoExtractor
‪static PropertyInfoExtractor $propertyInfoExtractor
Definition: ClassSchema.php:81
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$propertyObjects
‪static array $propertyObjects
Definition: ClassSchema.php:59
‪TYPO3\CMS\Core\Type\BitSet
Definition: BitSet.php:66
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Property
Definition: Property.php:30
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Exception\NoSuchMethodException
Definition: NoSuchMethodException.php:24
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getMethods
‪array Method[] getMethods()
Definition: ClassSchema.php:479
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\hasProperty
‪hasProperty(string $propertyName)
Definition: ClassSchema.php:457
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getMethod
‪getMethod(string $methodName)
Definition: ClassSchema.php:465
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\reflectMethods
‪reflectMethods(\ReflectionClass $reflectionClass)
Definition: ClassSchema.php:240
‪TYPO3\CMS\Extbase\Annotation\ORM\Cascade
Definition: Cascade.php:26
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$className
‪string $className
Definition: ClassSchema.php:69
‪$validator
‪if(isset($args['d'])) $validator
Definition: validateRstFiles.php:262
‪TYPO3\CMS\Extbase\Reflection
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$properties
‪array $properties
Definition: ClassSchema.php:75
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getProperties
‪array Property[] getProperties()
Definition: ClassSchema.php:433
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\reflectProperties
‪reflectProperties(\ReflectionClass $reflectionClass)
Definition: ClassSchema.php:146
‪TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException
Definition: InvalidTypeHintException.php:25
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$methodObjects
‪static array $methodObjects
Definition: ClassSchema.php:63
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Method
Definition: Method.php:27
‪TYPO3\CMS\Extbase\Validation\ValidatorClassNameResolver\resolve
‪static resolve(string $validatorIdentifier)
Definition: ValidatorClassNameResolver.php:44
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\$methods
‪array $methods
Definition: ClassSchema.php:79
‪TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface
Definition: ControllerInterface.php:25
‪TYPO3\CMS\Extbase\Reflection\ClassSchema
Definition: ClassSchema.php:50
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics\ANNOTATED_TRANSIENT
‪const ANNOTATED_TRANSIENT
Definition: PropertyCharacteristics.php:31
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\Property\getName
‪getName()
Definition: Property.php:62
‪TYPO3\CMS\Extbase\Annotation\ORM\Transient
Definition: Transient.php:25
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\BIT_CLASS_IS_CONTROLLER
‪const BIT_CLASS_IS_CONTROLLER
Definition: ClassSchema.php:51
‪TYPO3\CMS\Extbase\Annotation\Validate
Definition: Validate.php:28
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\__construct
‪__construct(string $className)
Definition: ClassSchema.php:92
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\PropertyCharacteristics\VISIBILITY_PROTECTED
‪const VISIBILITY_PROTECTED
Definition: PropertyCharacteristics.php:28
‪TYPO3\CMS\Extbase\Validation\ValidatorClassNameResolver
Definition: ValidatorClassNameResolver.php:27
‪TYPO3\CMS\Extbase\Reflection\ClassSchema\getDomainObjectProperties
‪Property[] getDomainObjectProperties()
Definition: ClassSchema.php:444