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