|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de> 00006 * All rights reserved 00007 * 00008 * This class is a backport of the corresponding class of FLOW3. 00009 * All credits go to the v5 team. 00010 * 00011 * This script is part of the TYPO3 project. The TYPO3 project is 00012 * free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. 00016 * 00017 * The GNU General Public License can be found at 00018 * http://www.gnu.org/copyleft/gpl.html. 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 /** 00029 * Validator resolver to automatically find a appropriate validator for a given subject 00030 * 00031 * @package Extbase 00032 * @subpackage Validation 00033 * @version $Id: ValidatorResolver.php 1790 2010-01-18 22:27:37Z jocrau $ 00034 */ 00035 class Tx_Extbase_Validation_ValidatorResolver implements t3lib_Singleton { 00036 00037 /** 00038 * Match validator names and options 00039 * @var string 00040 */ 00041 const PATTERN_MATCH_VALIDATORS = '/ 00042 (?:^|,\s*) 00043 (?P<validatorName>[a-z0-9_]+) 00044 \s* 00045 (?:\( 00046 (?P<validatorOptions>(?:\s*[a-z0-9]+\s*=\s*(?: 00047 "(?:\\\\"|[^"])*" 00048 |\'(?:\\\\\'|[^\'])*\' 00049 |(?:\s|[^,"\']*) 00050 )(?:\s|,)*)*) 00051 \))? 00052 /ixS'; 00053 00054 /** 00055 * Match validator options (to parse actual options) 00056 * @var string 00057 */ 00058 const PATTERN_MATCH_VALIDATOROPTIONS = '/ 00059 \s* 00060 (?P<optionName>[a-z0-9]+) 00061 \s*=\s* 00062 (?P<optionValue> 00063 "(?:\\\\"|[^"])*" 00064 |\'(?:\\\\\'|[^\'])*\' 00065 |(?:\s|[^,"\']*) 00066 ) 00067 /ixS'; 00068 00069 /** 00070 * @var Tx_Extbase_Object_ObjectManagerInterface 00071 */ 00072 protected $objectManager; 00073 00074 /** 00075 * @var Tx_Extbase_Reflection_Service 00076 */ 00077 protected $reflectionService; 00078 00079 /** 00080 * @var array 00081 */ 00082 protected $baseValidatorConjunctions = array(); 00083 00084 /** 00085 * Injects the object manager 00086 * 00087 * @param Tx_Extbase_Object_ObjectManagerInterface $objectManager A reference to the object manager 00088 * @return void 00089 */ 00090 public function injectObjectManager(Tx_Extbase_Object_ObjectManagerInterface $objectManager) { 00091 $this->objectManager = $objectManager; 00092 } 00093 00094 /** 00095 * Injects the reflection service 00096 * 00097 * @param Tx_Extbase_Reflection_Service $reflectionService 00098 * @return void 00099 */ 00100 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) { 00101 $this->reflectionService = $reflectionService; 00102 } 00103 00104 /** 00105 * Get a validator for a given data type. Returns a validator implementing 00106 * the Tx_Extbase_Validation_Validator_ValidatorInterface or NULL if no validator 00107 * could be resolved. 00108 * 00109 * @param string $validatorName Either one of the built-in data types or fully qualified validator class name 00110 * @param array $validatorOptions Options to be passed to the validator 00111 * @return Tx_Extbase_Validation_Validator_ValidatorInterface Validator or NULL if none found. 00112 */ 00113 public function createValidator($validatorName, array $validatorOptions = array()) { 00114 $validatorClassName = $this->resolveValidatorObjectName($validatorName); 00115 if ($validatorClassName === FALSE) return NULL; 00116 $validator = $this->objectManager->get($validatorClassName); 00117 if (!($validator instanceof Tx_Extbase_Validation_Validator_ValidatorInterface)) { 00118 return NULL; 00119 } 00120 00121 $validator->setOptions($validatorOptions); 00122 return $validator; 00123 } 00124 00125 /** 00126 * Resolves and returns the base validator conjunction for the given data type. 00127 * 00128 * If no validator could be resolved (which usually means that no validation is necessary), 00129 * NULL is returned. 00130 * 00131 * @param string $dataType The data type to search a validator for. Usually the fully qualified object name 00132 * @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL 00133 */ 00134 public function getBaseValidatorConjunction($dataType) { 00135 if (!isset($this->baseValidatorConjunctions[$dataType])) { 00136 $this->baseValidatorConjunctions[$dataType] = $this->buildBaseValidatorConjunction($dataType); 00137 } 00138 return $this->baseValidatorConjunctions[$dataType]; 00139 } 00140 00141 /** 00142 * Detects and registers any validators for arguments: 00143 * - by the data type specified in the @param annotations 00144 * - additional validators specified in the @validate annotations of a method 00145 * 00146 * @return array An Array of ValidatorConjunctions for each method parameters. 00147 */ 00148 public function buildMethodArgumentsValidatorConjunctions($className, $methodName) { 00149 $validatorConjunctions = array(); 00150 00151 $methodParameters = $this->reflectionService->getMethodParameters($className, $methodName); 00152 $methodTagsValues = $this->reflectionService->getMethodTagsValues($className, $methodName); 00153 if (!count($methodParameters)) { 00154 // early return in case no parameters were found. 00155 return $validatorConjunctions; 00156 } 00157 foreach ($methodParameters as $parameterName => $methodParameter) { 00158 $validatorConjunction = $this->createValidator('Conjunction'); 00159 $typeValidator = $this->createValidator($methodParameter['type']); 00160 if ($typeValidator !== NULL) $validatorConjunction->addValidator($typeValidator); 00161 $validatorConjunctions[$parameterName] = $validatorConjunction; 00162 } 00163 00164 if (isset($methodTagsValues['validate'])) { 00165 foreach ($methodTagsValues['validate'] as $validateValue) { 00166 $parsedAnnotation = $this->parseValidatorAnnotation($validateValue); 00167 foreach ($parsedAnnotation['validators'] as $validatorConfiguration) { 00168 $newValidator = $this->createValidator($validatorConfiguration['validatorName'], $validatorConfiguration['validatorOptions']); 00169 if ($newValidator === NULL) throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $validatorConfiguration['validatorName'] . '".', 1239853109); 00170 00171 if (isset($validatorConjunctions[$parsedAnnotation['argumentName']])) { 00172 $validatorConjunctions[$parsedAnnotation['argumentName']]->addValidator($newValidator); 00173 } else { 00174 throw new Tx_Extbase_Validation_Exception_InvalidValidationConfiguration('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Validator specified for argument name "' . $parsedAnnotation['argumentName'] . '", but this argument does not exist.', 1253172726); 00175 } 00176 } 00177 } 00178 } 00179 return $validatorConjunctions; 00180 } 00181 00182 /** 00183 * Builds a base validator conjunction for the given data type. 00184 * 00185 * The base validation rules are those which were declared directly in a class (typically 00186 * a model) through some @validate annotations on properties. 00187 * 00188 * Additionally, if a custom validator was defined for the class in question, it will be added 00189 * to the end of the conjunction. A custom validator is found if it follows the naming convention 00190 * "Replace '\Model\' by '\Validator\' and append "Validator". 00191 * 00192 * Example: $dataType is F3\Foo\Domain\Model\Quux, then the Validator will be found if it has the 00193 * name F3\Foo\Domain\Validator\QuuxValidator 00194 * 00195 * @param string $dataType The data type to build the validation conjunction for. Needs to be the fully qualified object name. 00196 * @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL 00197 */ 00198 protected function buildBaseValidatorConjunction($dataType) { 00199 $validatorConjunction = $this->objectManager->get('Tx_Extbase_Validation_Validator_ConjunctionValidator'); 00200 00201 // Model based validator 00202 if (strstr($dataType, '_') !== FALSE && class_exists($dataType)) { 00203 $validatorCount = 0; 00204 $objectValidator = $this->createValidator('GenericObject'); 00205 00206 foreach ($this->reflectionService->getClassPropertyNames($dataType) as $classPropertyName) { 00207 $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($dataType, $classPropertyName); 00208 if (!isset($classPropertyTagsValues['validate'])) continue; 00209 00210 foreach ($classPropertyTagsValues['validate'] as $validateValue) { 00211 $parsedAnnotation = $this->parseValidatorAnnotation($validateValue); 00212 foreach ($parsedAnnotation['validators'] as $validatorConfiguration) { 00213 $newValidator = $this->createValidator($validatorConfiguration['validatorName'], $validatorConfiguration['validatorOptions']); 00214 if ($newValidator === NULL) { 00215 throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $dataType . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorConfiguration['validatorName'] . '".', 1241098027); 00216 } 00217 $objectValidator->addPropertyValidator($classPropertyName, $newValidator); 00218 $validatorCount ++; 00219 } 00220 } 00221 } 00222 if ($validatorCount > 0) $validatorConjunction->addValidator($objectValidator); 00223 } 00224 00225 // Custom validator for the class 00226 $possibleValidatorClassName = str_replace('_Model_', '_Validator_', $dataType) . 'Validator'; 00227 $customValidator = $this->createValidator($possibleValidatorClassName); 00228 if ($customValidator !== NULL) { 00229 $validatorConjunction->addValidator($customValidator); 00230 } 00231 00232 return $validatorConjunction; 00233 } 00234 00235 /** 00236 * Parses the validator options given in @validate annotations. 00237 * 00238 * @return array 00239 */ 00240 protected function parseValidatorAnnotation($validateValue) { 00241 $matches = array(); 00242 if ($validateValue[0] === '$') { 00243 $parts = explode(' ', $validateValue, 2); 00244 $validatorConfiguration = array('argumentName' => ltrim($parts[0], '$'), 'validators' => array()); 00245 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $parts[1], $matches, PREG_SET_ORDER); 00246 } else { 00247 $validatorConfiguration = array('validators' => array()); 00248 preg_match_all(self::PATTERN_MATCH_VALIDATORS, $validateValue, $matches, PREG_SET_ORDER); 00249 } 00250 00251 foreach ($matches as $match) { 00252 $validatorOptions = array(); 00253 if (isset($match['validatorOptions'])) { 00254 $validatorOptions = $this->parseValidatorOptions($match['validatorOptions']); 00255 } 00256 $validatorConfiguration['validators'][] = array('validatorName' => $match['validatorName'], 'validatorOptions' => $validatorOptions); 00257 } 00258 00259 return $validatorConfiguration; 00260 } 00261 00262 /** 00263 * Parses $rawValidatorOptions not containing quoted option values. 00264 * $rawValidatorOptions will be an empty string afterwards (pass by ref!). 00265 * 00266 * @param string &$rawValidatorOptions 00267 * @return array An array of optionName/optionValue pairs 00268 */ 00269 protected function parseValidatorOptions($rawValidatorOptions) { 00270 $validatorOptions = array(); 00271 $parsedValidatorOptions = array(); 00272 preg_match_all(self::PATTERN_MATCH_VALIDATOROPTIONS, $rawValidatorOptions, $validatorOptions, PREG_SET_ORDER); 00273 foreach ($validatorOptions as $validatorOption) { 00274 $parsedValidatorOptions[trim($validatorOption['optionName'])] = trim($validatorOption['optionValue']); 00275 } 00276 array_walk($parsedValidatorOptions, array($this, 'unquoteString')); 00277 return $parsedValidatorOptions; 00278 } 00279 00280 /** 00281 * Removes escapings from a given argument string and trims the outermost 00282 * quotes. 00283 * 00284 * This method is meant as a helper for regular expression results. 00285 * 00286 * @param string &$quotedValue Value to unquote 00287 */ 00288 protected function unquoteString(&$quotedValue) { 00289 switch ($quotedValue[0]) { 00290 case '"': 00291 $quotedValue = str_replace('\"', '"', trim($quotedValue, '"')); 00292 break; 00293 case '\'': 00294 $quotedValue = str_replace('\\\'', '\'', trim($quotedValue, '\'')); 00295 break; 00296 } 00297 $quotedValue = str_replace('\\\\', '\\', $quotedValue); 00298 } 00299 00300 /** 00301 * 00302 * 00303 * Returns an object of an appropriate validator for the given class. If no validator is available 00304 * FALSE is returned 00305 * 00306 * @param string $validatorName Either the fully qualified class name of the validator or the short name of a built-in validator 00307 * @return string Name of the validator object or FALSE 00308 */ 00309 protected function resolveValidatorObjectName($validatorName) { 00310 if (strstr($validatorName, '_') !== FALSE && class_exists($validatorName)) return $validatorName; 00311 00312 $possibleClassName = 'Tx_Extbase_Validation_Validator_' . $this->unifyDataType($validatorName) . 'Validator'; 00313 if (class_exists($possibleClassName)) return $possibleClassName; 00314 00315 return FALSE; 00316 } 00317 00318 /** 00319 * Preprocess data types. Used to map primitive PHP types to DataTypes used in Extbase. 00320 * 00321 * @param string $type Data type to unify 00322 * @return string unified data type 00323 */ 00324 protected function unifyDataType($type) { 00325 switch ($type) { 00326 case 'int' : 00327 $type = 'Integer'; 00328 break; 00329 case 'bool' : 00330 $type = 'Boolean'; 00331 break; 00332 case 'double' : 00333 $type = 'Float'; 00334 break; 00335 case 'numeric' : 00336 $type = 'Number'; 00337 break; 00338 case 'mixed' : 00339 $type = 'Raw'; 00340 break; 00341 } 00342 return ucfirst($type); 00343 } 00344 00345 } 00346 00347 ?>
1.8.0