8e28b7aff2da9234820a353d12d2c92c2438410c
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Validation / ValidatorResolver.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Validation;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Log\LogManager;
18 use TYPO3\CMS\Core\Utility\ClassNamingUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
21 use TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException;
22 use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
23
24 /**
25 * Validator resolver to automatically find an appropriate validator for a given subject
26 */
27 class ValidatorResolver implements \TYPO3\CMS\Core\SingletonInterface
28 {
29 /**
30 * Match validator names and options
31 * @todo: adjust [a-z0-9_:.\\\\] once Tx_Extbase_Foo syntax is outdated.
32 * @deprecated and will be removed in TYPO3 v10.0.
33 *
34 * @var string
35 */
36 const PATTERN_MATCH_VALIDATORS = '/
37 (?:^|,\s*)
38 (?P<validatorName>[a-z0-9_:.\\\\]+)
39 \s*
40 (?:\(
41 (?P<validatorOptions>(?:\s*[a-z0-9]+\s*=\s*(?:
42 "(?:\\\\"|[^"])*"
43 |\'(?:\\\\\'|[^\'])*\'
44 |(?:\s|[^,"\']*)
45 )(?:\s|,)*)*)
46 \))?
47 /ixS';
48
49 /**
50 * Match validator options (to parse actual options)
51 * @var string
52 * @deprecated and will be removed in TYPO3 v10.0.
53 */
54 const PATTERN_MATCH_VALIDATOROPTIONS = '/
55 \s*
56 (?P<optionName>[a-z0-9]+)
57 \s*=\s*
58 (?P<optionValue>
59 "(?:\\\\"|[^"])*"
60 |\'(?:\\\\\'|[^\'])*\'
61 |(?:\s|[^,"\']*)
62 )
63 /ixS';
64
65 /**
66 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
67 */
68 protected $objectManager;
69
70 /**
71 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
72 */
73 protected $reflectionService;
74
75 /**
76 * @var array
77 */
78 protected $baseValidatorConjunctions = [];
79
80 /**
81 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
82 */
83 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
84 {
85 $this->objectManager = $objectManager;
86 }
87
88 /**
89 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
90 */
91 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
92 {
93 $this->reflectionService = $reflectionService;
94 }
95
96 /**
97 * Get a validator for a given data type. Returns a validator implementing
98 * the \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface or NULL if no validator
99 * could be resolved.
100 *
101 * @param string $validatorType Either one of the built-in data types or fully qualified validator class name
102 * @param array $validatorOptions Options to be passed to the validator
103 * @return \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface Validator or NULL if none found.
104 */
105 public function createValidator($validatorType, array $validatorOptions = [])
106 {
107 try {
108 /**
109 * @todo remove throwing Exceptions in resolveValidatorObjectName
110 */
111 $validatorObjectName = $this->resolveValidatorObjectName($validatorType);
112
113 $validator = $this->objectManager->get($validatorObjectName, $validatorOptions);
114
115 // Move this check into ClassSchema
116 if (!($validator instanceof \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface)) {
117 throw new Exception\NoSuchValidatorException('The validator "' . $validatorObjectName . '" does not implement TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface!', 1300694875);
118 }
119
120 return $validator;
121 } catch (NoSuchValidatorException $e) {
122 GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__)->debug($e->getMessage());
123 return null;
124 }
125 }
126
127 /**
128 * Resolves and returns the base validator conjunction for the given data type.
129 *
130 * If no validator could be resolved (which usually means that no validation is necessary),
131 * NULL is returned.
132 *
133 * @param string $targetClassName The data type to search a validator for. Usually the fully qualified object name
134 * @return ConjunctionValidator The validator conjunction or NULL
135 */
136 public function getBaseValidatorConjunction($targetClassName)
137 {
138 if (!array_key_exists($targetClassName, $this->baseValidatorConjunctions)) {
139 $this->buildBaseValidatorConjunction($targetClassName, $targetClassName);
140 }
141
142 return $this->baseValidatorConjunctions[$targetClassName];
143 }
144
145 /**
146 * Detects and registers any validators for arguments:
147 * - by the data type specified in the param annotations
148 * - additional validators specified in the validate annotations of a method
149 *
150 * @param string $className
151 * @param string $methodName
152 * @param array $methodParameters Optional pre-compiled array of method parameters
153 * @param array $methodValidateAnnotations Optional pre-compiled array of validate annotations (as array)
154 * @return ConjunctionValidator[] An Array of ValidatorConjunctions for each method parameters.
155 * @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationConfigurationException
156 * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
157 * @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidTypeHintException
158 * @deprecated
159 */
160 public function buildMethodArgumentsValidatorConjunctions($className, $methodName, array $methodParameters = null, array $methodValidateAnnotations = null)
161 {
162 trigger_error(
163 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
164 E_USER_DEPRECATED
165 );
166
167 /** @var ConjunctionValidator[] $validatorConjunctions */
168 $validatorConjunctions = [];
169
170 if ($methodParameters === null) {
171 $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName);
172 }
173 if (empty($methodParameters)) {
174 return $validatorConjunctions;
175 }
176
177 foreach ($methodParameters as $parameterName => $methodParameter) {
178 /** @var ConjunctionValidator $validatorConjunction */
179 $validatorConjunction = $this->createValidator(ConjunctionValidator::class);
180
181 if (!array_key_exists('type', $methodParameter)) {
182 throw new Exception\InvalidTypeHintException('Missing type information, probably no @param annotation for parameter "$' . $parameterName . '" in ' . $className . '->' . $methodName . '()', 1281962564);
183 }
184
185 // @todo: remove check for old underscore model name syntax once it's possible
186 if (strpbrk($methodParameter['type'], '_\\') === false) {
187 $typeValidator = $this->createValidator($methodParameter['type']);
188 } else {
189 $typeValidator = null;
190 }
191
192 if ($typeValidator !== null) {
193 $validatorConjunction->addValidator($typeValidator);
194 }
195 $validatorConjunctions[$parameterName] = $validatorConjunction;
196 }
197
198 if ($methodValidateAnnotations === null) {
199 $validateAnnotations = $this->getMethodValidateAnnotations($className, $methodName);
200 $methodValidateAnnotations = array_map(function ($validateAnnotation) {
201 return [
202 'type' => $validateAnnotation['validatorName'],
203 'options' => $validateAnnotation['validatorOptions'],
204 'argumentName' => $validateAnnotation['argumentName'],
205 ];
206 }, $validateAnnotations);
207 }
208
209 foreach ($methodValidateAnnotations as $annotationParameters) {
210 $newValidator = $this->createValidator($annotationParameters['type'], $annotationParameters['options']);
211 if ($newValidator === null) {
212 throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $annotationParameters['type'] . '".', 1239853109);
213 }
214 if (isset($validatorConjunctions[$annotationParameters['argumentName']])) {
215 $validatorConjunctions[$annotationParameters['argumentName']]->addValidator($newValidator);
216 } elseif (strpos($annotationParameters['argumentName'], '.') !== false) {
217 $objectPath = explode('.', $annotationParameters['argumentName']);
218 $argumentName = array_shift($objectPath);
219 $validatorConjunctions[$argumentName]->addValidator($this->buildSubObjectValidator($objectPath, $newValidator));
220 } else {
221 throw new Exception\InvalidValidationConfigurationException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Validator specified for argument name "' . $annotationParameters['argumentName'] . '", but this argument does not exist.', 1253172726);
222 }
223 }
224
225 return $validatorConjunctions;
226 }
227
228 /**
229 * Builds a chain of nested object validators by specification of the given
230 * object path.
231 *
232 * @param array $objectPath The object path
233 * @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator The validator which should be added to the property specified by objectPath
234 * @return \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator
235 * @deprecated
236 */
237 protected function buildSubObjectValidator(array $objectPath, \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator)
238 {
239 trigger_error(
240 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
241 E_USER_DEPRECATED
242 );
243
244 $rootObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
245 $parentObjectValidator = $rootObjectValidator;
246
247 while (count($objectPath) > 1) {
248 $subObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
249 $subPropertyName = array_shift($objectPath);
250 $parentObjectValidator->addPropertyValidator($subPropertyName, $subObjectValidator);
251 $parentObjectValidator = $subObjectValidator;
252 }
253
254 $parentObjectValidator->addPropertyValidator(array_shift($objectPath), $propertyValidator);
255
256 return $rootObjectValidator;
257 }
258
259 /**
260 * Builds a base validator conjunction for the given data type.
261 *
262 * The base validation rules are those which were declared directly in a class (typically
263 * a model) through some validate annotations on properties.
264 *
265 * If a property holds a class for which a base validator exists, that property will be
266 * checked as well, regardless of a validate annotation
267 *
268 * Additionally, if a custom validator was defined for the class in question, it will be added
269 * to the end of the conjunction. A custom validator is found if it follows the naming convention
270 * "Replace '\Model\' by '\Validator\' and append 'Validator'".
271 *
272 * Example: $targetClassName is TYPO3\Foo\Domain\Model\Quux, then the validator will be found if it has the
273 * name TYPO3\Foo\Domain\Validator\QuuxValidator
274 *
275 * @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups
276 * @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name.
277 * @param array $validationGroups The validation groups to build the validator for
278 * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
279 * @throws \InvalidArgumentException
280 */
281 protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups = [])
282 {
283 $conjunctionValidator = new ConjunctionValidator();
284 $this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator;
285
286 // note: the simpleType check reduces lookups to the class loader
287 if (!TypeHandlingUtility::isSimpleType($targetClassName) && class_exists($targetClassName)) {
288 $classSchema = $this->reflectionService->getClassSchema($targetClassName);
289
290 // Model based validator
291 /** @var \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator $objectValidator */
292 $objectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
293 foreach ($classSchema->getProperties() as $classPropertyName => $classPropertyDefinition) {
294 /** @var array|array[] $classPropertyDefinition */
295 $classPropertyTagsValues = $classPropertyDefinition['tags'];
296
297 if (!isset($classPropertyTagsValues['var'])) {
298 throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
299 }
300
301 $propertyTargetClassName = $classPropertyDefinition['type'];
302 // note: the outer simpleType check reduces lookups to the class loader
303 if (!TypeHandlingUtility::isSimpleType($propertyTargetClassName)) {
304 if (TypeHandlingUtility::isCollectionType($propertyTargetClassName)) {
305 $collectionValidator = $this->createValidator(
306 \TYPO3\CMS\Extbase\Validation\Validator\CollectionValidator::class,
307 [
308 'elementType' => $classPropertyDefinition['elementType'],
309 'validationGroups' => $validationGroups
310 ]
311 );
312 $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
313 } elseif (class_exists($propertyTargetClassName) && !TypeHandlingUtility::isCoreType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\CMS\Extbase\Object\Container\Container::SCOPE_PROTOTYPE) {
314 $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName);
315 if ($validatorForProperty !== null && $validatorForProperty->count() > 0) {
316 $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
317 }
318 }
319 }
320
321 foreach ($classPropertyDefinition['validators'] as $validatorDefinition) {
322 // @todo: Respect validationGroups
323
324 // @todo: At this point we already have the class name of the validator, thus there is not need
325 // @todo: calling \TYPO3\CMS\Extbase\Validation\ValidatorResolver::resolveValidatorObjectName inside
326 // @todo: \TYPO3\CMS\Extbase\Validation\ValidatorResolver::createValidator once again. However, to
327 // @todo: keep things simple for now, we still use the method createValidator here. In the future,
328 // @todo: createValidator must only accept FQCN's.
329 $newValidator = $this->createValidator($validatorDefinition['className'], $validatorDefinition['options']);
330 if ($newValidator === null) {
331 throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorDefinition['className'] . '".', 1241098027);
332 }
333 $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
334 }
335 }
336
337 if (!empty($objectValidator->getPropertyValidators())) {
338 $conjunctionValidator->addValidator($objectValidator);
339 }
340 }
341
342 $this->addCustomValidators($targetClassName, $conjunctionValidator);
343 }
344
345 /**
346 * This adds custom validators to the passed $conjunctionValidator.
347 *
348 * A custom validator is found if it follows the naming convention "Replace '\Model\' by '\Validator\' and
349 * append 'Validator'". If found, it will be added to the $conjunctionValidator.
350 *
351 * In addition canValidate() will be called on all implementations of the ObjectValidatorInterface to find
352 * all validators that could validate the target. The one with the highest priority will be added as well.
353 * If multiple validators have the same priority, which one will be added is not deterministic.
354 *
355 * @param string $targetClassName
356 * @param ConjunctionValidator $conjunctionValidator
357 */
358 protected function addCustomValidators($targetClassName, ConjunctionValidator &$conjunctionValidator)
359 {
360 // @todo: get rid of ClassNamingUtility usage once we dropped underscored class name support
361 $possibleValidatorClassName = ClassNamingUtility::translateModelNameToValidatorName($targetClassName);
362
363 $customValidator = $this->createValidator($possibleValidatorClassName);
364 if ($customValidator !== null) {
365 $conjunctionValidator->addValidator($customValidator);
366 }
367
368 // @todo: find polytype validator for class
369 }
370
371 /**
372 * Parses the validator options given in @validate annotations.
373 *
374 * @param string $validateValue
375 * @return array
376 * @internal
377 * @deprecated Helper, remove in v10 together with the deprecated consumer methods
378 */
379 public function parseValidatorAnnotation($validateValue)
380 {
381 trigger_error(
382 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
383 E_USER_DEPRECATED
384 );
385
386 $matches = [];
387 if ($validateValue[0] === '$') {
388 $parts = explode(' ', $validateValue, 2);
389 $validatorConfiguration = ['argumentName' => ltrim($parts[0], '$'), 'validators' => []];
390 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $parts[1], $matches, PREG_SET_ORDER);
391 } else {
392 $validatorConfiguration = ['validators' => []];
393 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $validateValue, $matches, PREG_SET_ORDER);
394 }
395 foreach ($matches as $match) {
396 $validatorOptions = [];
397 if (isset($match['validatorOptions'])) {
398 $validatorOptions = $this->parseValidatorOptions($match['validatorOptions']);
399 }
400 $validatorConfiguration['validators'][] = ['validatorName' => $match['validatorName'], 'validatorOptions' => $validatorOptions];
401 }
402 return $validatorConfiguration;
403 }
404
405 /**
406 * Parses $rawValidatorOptions not containing quoted option values.
407 * $rawValidatorOptions will be an empty string afterwards (pass by ref!).
408 *
409 * @param string $rawValidatorOptions
410 * @return array An array of optionName/optionValue pairs
411 * @deprecated
412 */
413 protected function parseValidatorOptions($rawValidatorOptions)
414 {
415 trigger_error(
416 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
417 E_USER_DEPRECATED
418 );
419
420 $validatorOptions = [];
421 $parsedValidatorOptions = [];
422 preg_match_all(self::PATTERN_MATCH_VALIDATOROPTIONS, $rawValidatorOptions, $validatorOptions, PREG_SET_ORDER);
423 foreach ($validatorOptions as $validatorOption) {
424 $parsedValidatorOptions[trim($validatorOption['optionName'])] = trim($validatorOption['optionValue']);
425 }
426 array_walk($parsedValidatorOptions, [$this, 'unquoteString']);
427 return $parsedValidatorOptions;
428 }
429
430 /**
431 * Removes escapings from a given argument string and trims the outermost
432 * quotes.
433 *
434 * This method is meant as a helper for regular expression results.
435 *
436 * @param string &$quotedValue Value to unquote
437 * @deprecated
438 */
439 protected function unquoteString(&$quotedValue)
440 {
441 trigger_error(
442 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
443 E_USER_DEPRECATED
444 );
445
446 switch ($quotedValue[0]) {
447 case '"':
448 $quotedValue = str_replace('\\"', '"', trim($quotedValue, '"'));
449 break;
450 case '\'':
451 $quotedValue = str_replace('\\\'', '\'', trim($quotedValue, '\''));
452 break;
453 }
454 $quotedValue = str_replace('\\\\', '\\', $quotedValue);
455 }
456
457 /**
458 * Returns an object of an appropriate validator for the given class. If no validator is available
459 * FALSE is returned
460 *
461 * @param string $validatorName Either the fully qualified class name of the validator or the short name of a built-in validator
462 *
463 * @throws Exception\NoSuchValidatorException
464 * @return string Name of the validator object
465 * @internal
466 */
467 public function resolveValidatorObjectName($validatorName)
468 {
469 if (strpos($validatorName, ':') !== false) {
470 // Found shorthand validator, either extbase or foreign extension
471 // NotEmpty or Acme.MyPck.Ext:MyValidator
472 list($extensionName, $extensionValidatorName) = explode(':', $validatorName);
473
474 if ($validatorName !== $extensionName && $extensionValidatorName !== '') {
475 // Shorthand custom
476 if (strpos($extensionName, '.') !== false) {
477 $extensionNameParts = explode('.', $extensionName);
478 $extensionName = array_pop($extensionNameParts);
479 $vendorName = implode('\\', $extensionNameParts);
480 $possibleClassName = $vendorName . '\\' . $extensionName . '\\Validation\\Validator\\' . $extensionValidatorName;
481 } else {
482 $possibleClassName = 'Tx_' . $extensionName . '_Validation_Validator_' . $extensionValidatorName;
483 }
484 } else {
485 // Shorthand built in
486 $possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
487 }
488 } elseif (strpbrk($validatorName, '_\\') === false) {
489 // Shorthand built in
490 $possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
491 } else {
492 // Full qualified
493 // Tx_MyExt_Validation_Validator_MyValidator or \Acme\Ext\Validation\Validator\FooValidator
494 $possibleClassName = $validatorName;
495 if (!empty($possibleClassName) && $possibleClassName[0] === '\\') {
496 $possibleClassName = substr($possibleClassName, 1);
497 }
498 }
499
500 if (substr($possibleClassName, - strlen('Validator')) !== 'Validator') {
501 $possibleClassName .= 'Validator';
502 }
503
504 if (class_exists($possibleClassName)) {
505 $possibleClassNameInterfaces = class_implements($possibleClassName);
506 if (!in_array(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface::class, $possibleClassNameInterfaces)) {
507 // The guessed validatorname is a valid class name, but does not implement the ValidatorInterface
508 throw new NoSuchValidatorException('Validator class ' . $validatorName . ' must implement \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface', 1365776838);
509 }
510 $resolvedValidatorName = $possibleClassName;
511 } else {
512 throw new NoSuchValidatorException('Validator class ' . $validatorName . ' does not exist', 1365799920);
513 }
514
515 return $resolvedValidatorName;
516 }
517
518 /**
519 * Used to map PHP types to validator types.
520 *
521 * @param string $type Data type to unify
522 * @return string unified data type
523 */
524 protected function getValidatorType($type)
525 {
526 switch ($type) {
527 case 'int':
528 $type = 'Integer';
529 break;
530 case 'bool':
531 $type = 'Boolean';
532 break;
533 case 'double':
534 $type = 'Float';
535 break;
536 case 'numeric':
537 $type = 'Number';
538 break;
539 case 'mixed':
540 $type = 'Raw';
541 break;
542 default:
543 $type = ucfirst($type);
544 }
545 return $type;
546 }
547
548 /**
549 * Temporary replacement for $this->reflectionService->getMethodAnnotations()
550 *
551 * @param string $className
552 * @param string $methodName
553 *
554 * @return array
555 * @deprecated Helper, remove in v10 together with buildMethodArgumentsValidatorConjunctions()
556 */
557 public function getMethodValidateAnnotations($className, $methodName)
558 {
559 $validateAnnotations = [];
560 $methodTagsValues = $this->reflectionService->getMethodTagsValues($className, $methodName);
561 if (isset($methodTagsValues['validate']) && is_array($methodTagsValues['validate'])) {
562 foreach ($methodTagsValues['validate'] as $validateValue) {
563 $parsedAnnotations = $this->parseValidatorAnnotation($validateValue);
564
565 foreach ($parsedAnnotations['validators'] as $validator) {
566 array_push($validateAnnotations, [
567 'argumentName' => $parsedAnnotations['argumentName'],
568 'validatorName' => $validator['validatorName'],
569 'validatorOptions' => $validator['validatorOptions']
570 ]);
571 }
572 }
573 }
574
575 return $validateAnnotations;
576 }
577 }