06d3dc406c666141df91719f00de537132f5a69d
[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 * @internal only to be used within Extbase, not part of TYPO3 Core API.
27 */
28 class ValidatorResolver implements \TYPO3\CMS\Core\SingletonInterface
29 {
30 /**
31 * Match validator names and options
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 since TYPO3 v9, will be removed in TYPO3 v10.0.
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
172 ->getClassSchema($className)
173 ->getMethod($methodName)['params'] ?? [];
174 }
175 if (empty($methodParameters)) {
176 return $validatorConjunctions;
177 }
178
179 foreach ($methodParameters as $parameterName => $methodParameter) {
180 /** @var ConjunctionValidator $validatorConjunction */
181 $validatorConjunction = $this->createValidator(ConjunctionValidator::class);
182
183 if (!array_key_exists('type', $methodParameter)) {
184 throw new Exception\InvalidTypeHintException('Missing type information, probably no @param annotation for parameter "$' . $parameterName . '" in ' . $className . '->' . $methodName . '()', 1281962564);
185 }
186
187 if (strpbrk($methodParameter['type'], '\\') === false) {
188 $typeValidator = $this->createValidator($methodParameter['type']);
189 } else {
190 $typeValidator = null;
191 }
192
193 if ($typeValidator !== null) {
194 $validatorConjunction->addValidator($typeValidator);
195 }
196 $validatorConjunctions[$parameterName] = $validatorConjunction;
197 }
198
199 if ($methodValidateAnnotations === null) {
200 $validateAnnotations = $this->getMethodValidateAnnotations($className, $methodName);
201 $methodValidateAnnotations = array_map(function ($validateAnnotation) {
202 return [
203 'type' => $validateAnnotation['validatorName'],
204 'options' => $validateAnnotation['validatorOptions'],
205 'argumentName' => $validateAnnotation['argumentName'],
206 ];
207 }, $validateAnnotations);
208 }
209
210 foreach ($methodValidateAnnotations as $annotationParameters) {
211 $newValidator = $this->createValidator($annotationParameters['type'], $annotationParameters['options']);
212 if ($newValidator === null) {
213 throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $annotationParameters['type'] . '".', 1239853109);
214 }
215 if (isset($validatorConjunctions[$annotationParameters['argumentName']])) {
216 $validatorConjunctions[$annotationParameters['argumentName']]->addValidator($newValidator);
217 } elseif (strpos($annotationParameters['argumentName'], '.') !== false) {
218 $objectPath = explode('.', $annotationParameters['argumentName']);
219 $argumentName = array_shift($objectPath);
220 $validatorConjunctions[$argumentName]->addValidator($this->buildSubObjectValidator($objectPath, $newValidator));
221 } else {
222 throw new Exception\InvalidValidationConfigurationException('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Validator specified for argument name "' . $annotationParameters['argumentName'] . '", but this argument does not exist.', 1253172726);
223 }
224 }
225
226 return $validatorConjunctions;
227 }
228
229 /**
230 * Builds a chain of nested object validators by specification of the given
231 * object path.
232 *
233 * @param array $objectPath The object path
234 * @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator The validator which should be added to the property specified by objectPath
235 * @return \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator
236 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
237 */
238 protected function buildSubObjectValidator(array $objectPath, \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $propertyValidator)
239 {
240 trigger_error(
241 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
242 E_USER_DEPRECATED
243 );
244
245 $rootObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
246 $parentObjectValidator = $rootObjectValidator;
247
248 while (count($objectPath) > 1) {
249 $subObjectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
250 $subPropertyName = array_shift($objectPath);
251 $parentObjectValidator->addPropertyValidator($subPropertyName, $subObjectValidator);
252 $parentObjectValidator = $subObjectValidator;
253 }
254
255 $parentObjectValidator->addPropertyValidator(array_shift($objectPath), $propertyValidator);
256
257 return $rootObjectValidator;
258 }
259
260 /**
261 * Builds a base validator conjunction for the given data type.
262 *
263 * The base validation rules are those which were declared directly in a class (typically
264 * a model) through some validate annotations on properties.
265 *
266 * If a property holds a class for which a base validator exists, that property will be
267 * checked as well, regardless of a validate annotation
268 *
269 * Additionally, if a custom validator was defined for the class in question, it will be added
270 * to the end of the conjunction. A custom validator is found if it follows the naming convention
271 * "Replace '\Model\' by '\Validator\' and append 'Validator'".
272 *
273 * Example: $targetClassName is TYPO3\Foo\Domain\Model\Quux, then the validator will be found if it has the
274 * name TYPO3\Foo\Domain\Validator\QuuxValidator
275 *
276 * @param string $indexKey The key to use as index in $this->baseValidatorConjunctions; calculated from target class name and validation groups
277 * @param string $targetClassName The data type to build the validation conjunction for. Needs to be the fully qualified class name.
278 * @param array $validationGroups The validation groups to build the validator for
279 * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
280 * @throws \InvalidArgumentException
281 */
282 protected function buildBaseValidatorConjunction($indexKey, $targetClassName, array $validationGroups = [])
283 {
284 $conjunctionValidator = new ConjunctionValidator();
285 $this->baseValidatorConjunctions[$indexKey] = $conjunctionValidator;
286
287 // note: the simpleType check reduces lookups to the class loader
288 if (!TypeHandlingUtility::isSimpleType($targetClassName) && class_exists($targetClassName)) {
289 $classSchema = $this->reflectionService->getClassSchema($targetClassName);
290
291 // Model based validator
292 /** @var \TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator $objectValidator */
293 $objectValidator = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\Validator\GenericObjectValidator::class, []);
294 foreach ($classSchema->getProperties() as $classPropertyName => $classPropertyDefinition) {
295 /** @var array|array[] $classPropertyDefinition */
296 $classPropertyTagsValues = $classPropertyDefinition['tags'];
297
298 if (!isset($classPropertyTagsValues['var'])) {
299 throw new \InvalidArgumentException(sprintf('There is no @var annotation for property "%s" in class "%s".', $classPropertyName, $targetClassName), 1363778104);
300 }
301
302 $propertyTargetClassName = $classPropertyDefinition['type'];
303 // note: the outer simpleType check reduces lookups to the class loader
304 if (!TypeHandlingUtility::isSimpleType($propertyTargetClassName)) {
305 if (TypeHandlingUtility::isCollectionType($propertyTargetClassName)) {
306 $collectionValidator = $this->createValidator(
307 \TYPO3\CMS\Extbase\Validation\Validator\CollectionValidator::class,
308 [
309 'elementType' => $classPropertyDefinition['elementType'],
310 'validationGroups' => $validationGroups
311 ]
312 );
313 $objectValidator->addPropertyValidator($classPropertyName, $collectionValidator);
314 } elseif (class_exists($propertyTargetClassName) && !TypeHandlingUtility::isCoreType($propertyTargetClassName) && $this->objectManager->isRegistered($propertyTargetClassName) && $this->objectManager->getScope($propertyTargetClassName) === \TYPO3\CMS\Extbase\Object\Container\Container::SCOPE_PROTOTYPE) {
315 $validatorForProperty = $this->getBaseValidatorConjunction($propertyTargetClassName);
316 if ($validatorForProperty !== null && $validatorForProperty->count() > 0) {
317 $objectValidator->addPropertyValidator($classPropertyName, $validatorForProperty);
318 }
319 }
320 }
321
322 foreach ($classPropertyDefinition['validators'] as $validatorDefinition) {
323 // @todo: Respect validationGroups
324
325 // @todo: At this point we already have the class name of the validator, thus there is not need
326 // @todo: calling \TYPO3\CMS\Extbase\Validation\ValidatorResolver::resolveValidatorObjectName inside
327 // @todo: \TYPO3\CMS\Extbase\Validation\ValidatorResolver::createValidator once again. However, to
328 // @todo: keep things simple for now, we still use the method createValidator here. In the future,
329 // @todo: createValidator must only accept FQCN's.
330 $newValidator = $this->createValidator($validatorDefinition['className'], $validatorDefinition['options']);
331 if ($newValidator === null) {
332 throw new Exception\NoSuchValidatorException('Invalid validate annotation in ' . $targetClassName . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorDefinition['className'] . '".', 1241098027);
333 }
334 $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
335 }
336 }
337
338 if (!empty($objectValidator->getPropertyValidators())) {
339 $conjunctionValidator->addValidator($objectValidator);
340 }
341 }
342
343 $this->addCustomValidators($targetClassName, $conjunctionValidator);
344 }
345
346 /**
347 * This adds custom validators to the passed $conjunctionValidator.
348 *
349 * A custom validator is found if it follows the naming convention "Replace '\Model\' by '\Validator\' and
350 * append 'Validator'". If found, it will be added to the $conjunctionValidator.
351 *
352 * In addition canValidate() will be called on all implementations of the ObjectValidatorInterface to find
353 * all validators that could validate the target. The one with the highest priority will be added as well.
354 * If multiple validators have the same priority, which one will be added is not deterministic.
355 *
356 * @param string $targetClassName
357 * @param ConjunctionValidator $conjunctionValidator
358 */
359 protected function addCustomValidators($targetClassName, ConjunctionValidator &$conjunctionValidator)
360 {
361 // @todo: get rid of ClassNamingUtility usage once we dropped underscored class name support
362 $possibleValidatorClassName = ClassNamingUtility::translateModelNameToValidatorName($targetClassName);
363
364 $customValidator = $this->createValidator($possibleValidatorClassName);
365 if ($customValidator !== null) {
366 $conjunctionValidator->addValidator($customValidator);
367 }
368
369 // @todo: find polytype validator for class
370 }
371
372 /**
373 * Parses the validator options given in @validate annotations.
374 *
375 * @param string $validateValue
376 * @return array
377 * @internal
378 * @deprecated Helper, remove in TYPO3 v10.0 together with the deprecated consumer methods
379 */
380 public function parseValidatorAnnotation($validateValue)
381 {
382 trigger_error(
383 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
384 E_USER_DEPRECATED
385 );
386
387 $matches = [];
388 if ($validateValue[0] === '$') {
389 $parts = explode(' ', $validateValue, 2);
390 $validatorConfiguration = ['argumentName' => ltrim($parts[0], '$'), 'validators' => []];
391 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $parts[1], $matches, PREG_SET_ORDER);
392 } else {
393 $validatorConfiguration = ['validators' => []];
394 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $validateValue, $matches, PREG_SET_ORDER);
395 }
396 foreach ($matches as $match) {
397 $validatorOptions = [];
398 if (isset($match['validatorOptions'])) {
399 $validatorOptions = $this->parseValidatorOptions($match['validatorOptions']);
400 }
401 $validatorConfiguration['validators'][] = ['validatorName' => $match['validatorName'], 'validatorOptions' => $validatorOptions];
402 }
403 return $validatorConfiguration;
404 }
405
406 /**
407 * Parses $rawValidatorOptions not containing quoted option values.
408 * $rawValidatorOptions will be an empty string afterwards (pass by ref!).
409 *
410 * @param string $rawValidatorOptions
411 * @return array An array of optionName/optionValue pairs
412 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
413 */
414 protected function parseValidatorOptions($rawValidatorOptions)
415 {
416 trigger_error(
417 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
418 E_USER_DEPRECATED
419 );
420
421 $validatorOptions = [];
422 $parsedValidatorOptions = [];
423 preg_match_all(self::PATTERN_MATCH_VALIDATOROPTIONS, $rawValidatorOptions, $validatorOptions, PREG_SET_ORDER);
424 foreach ($validatorOptions as $validatorOption) {
425 $parsedValidatorOptions[trim($validatorOption['optionName'])] = trim($validatorOption['optionValue']);
426 }
427 array_walk($parsedValidatorOptions, [$this, 'unquoteString']);
428 return $parsedValidatorOptions;
429 }
430
431 /**
432 * Removes escapings from a given argument string and trims the outermost
433 * quotes.
434 *
435 * This method is meant as a helper for regular expression results.
436 *
437 * @param string &$quotedValue Value to unquote
438 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
439 */
440 protected function unquoteString(&$quotedValue)
441 {
442 trigger_error(
443 'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
444 E_USER_DEPRECATED
445 );
446
447 switch ($quotedValue[0]) {
448 case '"':
449 $quotedValue = str_replace('\\"', '"', trim($quotedValue, '"'));
450 break;
451 case '\'':
452 $quotedValue = str_replace('\\\'', '\'', trim($quotedValue, '\''));
453 break;
454 }
455 $quotedValue = str_replace('\\\\', '\\', $quotedValue);
456 }
457
458 /**
459 * Returns an object of an appropriate validator for the given class. If no validator is available
460 * FALSE is returned
461 *
462 * @param string $validatorName Either the fully qualified class name of the validator or the short name of a built-in validator
463 *
464 * @throws Exception\NoSuchValidatorException
465 * @return string Name of the validator object
466 * @internal
467 */
468 public function resolveValidatorObjectName($validatorName)
469 {
470 if (strpos($validatorName, ':') !== false) {
471 // Found shorthand validator, either extbase or foreign extension
472 // NotEmpty or Acme.MyPck.Ext:MyValidator
473 list($extensionName, $extensionValidatorName) = explode(':', $validatorName);
474
475 if ($validatorName !== $extensionName && $extensionValidatorName !== '') {
476 // Shorthand custom
477 if (strpos($extensionName, '.') !== false) {
478 $extensionNameParts = explode('.', $extensionName);
479 $extensionName = array_pop($extensionNameParts);
480 $vendorName = implode('\\', $extensionNameParts);
481 $possibleClassName = $vendorName . '\\' . $extensionName . '\\Validation\\Validator\\' . $extensionValidatorName;
482 }
483 } else {
484 // Shorthand built in
485 $possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
486 }
487 } elseif (strpbrk($validatorName, '\\') === false) {
488 // Shorthand built in
489 $possibleClassName = 'TYPO3\\CMS\\Extbase\\Validation\\Validator\\' . $this->getValidatorType($validatorName);
490 } else {
491 // Full qualified
492 // Example: \Acme\Ext\Validation\Validator\FooValidator
493 $possibleClassName = $validatorName;
494 if (!empty($possibleClassName) && $possibleClassName[0] === '\\') {
495 $possibleClassName = substr($possibleClassName, 1);
496 }
497 }
498
499 if (substr($possibleClassName, - strlen('Validator')) !== 'Validator') {
500 $possibleClassName .= 'Validator';
501 }
502
503 if (class_exists($possibleClassName)) {
504 $possibleClassNameInterfaces = class_implements($possibleClassName);
505 if (!in_array(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface::class, $possibleClassNameInterfaces)) {
506 // The guessed validatorname is a valid class name, but does not implement the ValidatorInterface
507 throw new NoSuchValidatorException('Validator class ' . $validatorName . ' must implement \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface', 1365776838);
508 }
509 $resolvedValidatorName = $possibleClassName;
510 } else {
511 throw new NoSuchValidatorException('Validator class ' . $validatorName . ' does not exist', 1365799920);
512 }
513
514 return $resolvedValidatorName;
515 }
516
517 /**
518 * Used to map PHP types to validator types.
519 *
520 * @param string $type Data type to unify
521 * @return string unified data type
522 */
523 protected function getValidatorType($type)
524 {
525 switch ($type) {
526 case 'int':
527 $type = 'Integer';
528 break;
529 case 'bool':
530 $type = 'Boolean';
531 break;
532 case 'double':
533 $type = 'Float';
534 break;
535 case 'numeric':
536 $type = 'Number';
537 break;
538 case 'mixed':
539 $type = 'Raw';
540 break;
541 default:
542 $type = ucfirst($type);
543 }
544 return $type;
545 }
546
547 /**
548 * Temporary replacement for $this->reflectionService->getMethodAnnotations()
549 *
550 * @param string $className
551 * @param string $methodName
552 *
553 * @return array
554 * @deprecated Helper, remove in TYPO3 v10.0 together with buildMethodArgumentsValidatorConjunctions()
555 */
556 public function getMethodValidateAnnotations($className, $methodName)
557 {
558 $validateAnnotations = [];
559 $methodTagsValues = $this->reflectionService->getClassSchema($className)->getMethod($methodName)['tags'] ?? [];
560 if (isset($methodTagsValues['validate']) && is_array($methodTagsValues['validate'])) {
561 foreach ($methodTagsValues['validate'] as $validateValue) {
562 $parsedAnnotations = $this->parseValidatorAnnotation($validateValue);
563
564 foreach ($parsedAnnotations['validators'] as $validator) {
565 $validateAnnotations[] = [
566 'argumentName' => $parsedAnnotations['argumentName'],
567 'validatorName' => $validator['validatorName'],
568 'validatorOptions' => $validator['validatorOptions']
569 ];
570 }
571 }
572 }
573
574 return $validateAnnotations;
575 }
576 }