[+BUGFIX] Extbase (Validation): Temporarily fixed the ValidatorResolver so that it...
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Validation / ValidatorResolver.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
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 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * Validator resolver to automatically find a appropriate validator for a given subject
30 *
31 * @package Extbase
32 * @subpackage Validation
33 * @version $Id$
34 */
35 class Tx_Extbase_Validation_ValidatorResolver {
36
37 /**
38 * @var Tx_Extbase_Object_ManagerInterface
39 */
40 protected $objectManager;
41
42 /**
43 * @var Tx_Extbase_Reflection_Service
44 */
45 protected $reflectionService;
46
47 /**
48 * @var array
49 */
50 protected $baseValidatorConjunctions = array();
51
52 /**
53 * Injects the object manager
54 *
55 * @param Tx_Extbase_Object_ManagerInterface $objectManager A reference to the object manager
56 * @return void
57 */
58 public function injectObjectManager(Tx_Extbase_Object_ManagerInterface $objectManager) {
59 $this->objectManager = $objectManager;
60 }
61
62 /**
63 * Injects the reflection service
64 *
65 * @param Tx_Extbase_Reflection_Service $reflectionService
66 * @return void
67 */
68 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
69 $this->reflectionService = $reflectionService;
70 }
71
72 /**
73 * Get a validator for a given data type. Returns a validator implementing
74 * the Tx_Extbase_Validation_Validator_ValidatorInterface or NULL if no validator
75 * could be resolved.
76 *
77 * @param string $validatorName Either one of the built-in data types or fully qualified validator class name
78 * @param array $validatorOptions Options to be passed to the validator
79 * @return Tx_Extbase_Validation_Validator_ValidatorInterface Validator or NULL if none found.
80 */
81 public function createValidator($validatorName, array $validatorOptions = array()) {
82 $validatorClassName = $this->resolveValidatorObjectName($validatorName);
83 if ($validatorClassName === FALSE) return NULL;
84 $validator = $this->objectManager->getObject($validatorClassName);
85 $validator->setOptions($validatorOptions);
86 return ($validator instanceof Tx_Extbase_Validation_Validator_ValidatorInterface) ? $validator : NULL;
87 }
88
89 /**
90 * Resolves and returns the base validator conjunction for the given data type.
91 *
92 * If no validator could be resolved (which usually means that no validation is necessary),
93 * NULL is returned.
94 *
95 * @param string $dataType The data type to search a validator for. Usually the fully qualified object name
96 * @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL
97 */
98 public function getBaseValidatorConjunction($dataType) {
99 if (!isset($this->baseValidatorConjunctions[$dataType])) {
100 $this->baseValidatorConjunctions[$dataType] = $this->buildBaseValidatorConjunction($dataType);
101 }
102 return $this->baseValidatorConjunctions[$dataType];
103 }
104
105 /**
106 * Detects and registers any additional validators for arguments which were specified in the @validate
107 * annotations of a method.
108 *
109 * @return array Validator Conjunctions
110 */
111 public function buildMethodArgumentsValidatorConjunctions($className, $methodName) {
112 $validatorConjunctions = array();
113
114 $methodTagsValues = $this->reflectionService->getMethodTagsValues($className, $methodName);
115 if (isset($methodTagsValues['validate'])) {
116 foreach ($methodTagsValues['validate'] as $validateValue) {
117 $matches = array();
118 preg_match('/^\$(?P<argumentName>[a-zA-Z0-9]+)\s+(?P<validators>.*)$/', $validateValue, $matches);
119 $argumentName = $matches['argumentName'];
120
121 preg_match_all('/(?P<validatorName>[a-zA-Z0-9]+)(?:\((?P<validatorOptions>[^)]+)\))?/', $matches['validators'], $matches, PREG_SET_ORDER);
122 foreach ($matches as $match) {
123 $validatorName = $match['validatorName'];
124 $validatorOptions = array();
125 $rawValidatorOptions = isset($match['validatorOptions']) ? explode(',', $match['validatorOptions']) : array();
126 foreach ($rawValidatorOptions as $rawValidatorOption) {
127 if (strpos($rawValidatorOption, '=') !== FALSE) {
128 list($optionName, $optionValue) = explode('=', $rawValidatorOption);
129 $validatorOptions[trim($optionName)] = trim($optionValue);
130 }
131 }
132 $newValidator = $this->createValidator($validatorName, $validatorOptions);
133 if ($newValidator === NULL) throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $className . '->' . $methodName . '(): Could not resolve class name for validator "' . $validatorName . '".', 1239853109);
134
135 if (isset($validatorConjunctions[$argumentName])) {
136 $validatorConjunctions[$argumentName]->addValidator($newValidator);
137 } else {
138 $validatorConjunctions[$argumentName] = $this->createValidator('Conjunction');
139 $validatorConjunctions[$argumentName]->addValidator($newValidator);
140 }
141 }
142 }
143 }
144 return $validatorConjunctions;
145 }
146
147 /**
148 * Builds a base validator conjunction for the given data type.
149 *
150 * The base validation rules are those which were declared directly in a class (typically
151 * a model) through some @validate annotations.
152 *
153 * Additionally, if a custom validator was defined for the class in question, it will be added
154 * to the end of the conjunction. A custom validator is found if it follows the naming convention
155 * "[FullyqualifiedModelClassName]Validator".
156 *
157 * @param string $dataType The data type to build the validation conjunction for. Usually the fully qualified object name.
158 * @return Tx_Extbase_Validation_Validator_ConjunctionValidator The validator conjunction or NULL
159 */
160 protected function buildBaseValidatorConjunction($dataType) {
161 $validatorConjunction = $this->objectManager->getObject('Tx_Extbase_Validation_Validator_ConjunctionValidator');
162
163 if (class_exists($dataType)) {
164 $validatorCount = 0;
165 $objectValidator = $this->createValidator('GenericObject');
166
167 foreach ($this->reflectionService->getClassPropertyNames($dataType) as $classPropertyName) {
168 $classPropertyTagsValues = $this->reflectionService->getPropertyTagsValues($dataType, $classPropertyName);
169 if (!isset($classPropertyTagsValues['validate'])) continue;
170
171 foreach ($classPropertyTagsValues['validate'] as $validateValue) {
172 $matches = array();
173 preg_match_all('/(?P<validatorName>[a-zA-Z0-9]+)(?:\((?P<validatorOptions>[^)]+)\))?/', $validateValue, $matches, PREG_SET_ORDER);
174 foreach ($matches as $match) {
175 $validatorName = $match['validatorName'];
176 $validatorOptions = array();
177 $rawValidatorOptions = isset($match['validatorOptions']) ? explode(',', $match['validatorOptions']) : array();
178 foreach ($rawValidatorOptions as $rawValidatorOption) {
179 if (strpos($rawValidatorOption, '=') !== FALSE) {
180 list($optionName, $optionValue) = explode('=', $rawValidatorOption);
181 $validatorOptions[trim($optionName)] = trim($optionValue);
182 }
183 }
184 $newValidator = $this->createValidator($validatorName, $validatorOptions);
185 if ($newValidator === NULL) throw new Tx_Extbase_Validation_Exception_NoSuchValidator('Invalid validate annotation in ' . $dataType . '::' . $classPropertyName . ': Could not resolve class name for validator "' . $validatorName . '".', 1241098027);
186 $objectValidator->addPropertyValidator($classPropertyName, $newValidator);
187 $validatorCount ++;
188 }
189 }
190 }
191 if ($validatorCount > 0) $validatorConjunction->addValidator($objectValidator);
192 }
193
194 $possibleValidatorClassName = str_replace('_Model_', '_Validator_', $dataType) . 'Validator';
195 $customValidatorObjectName = $this->resolveValidatorObjectName($possibleValidatorClassName);
196 if ($customValidatorObjectName !== FALSE) {
197 $validatorConjunction->addValidator($this->objectManager->getObject($customValidatorObjectName));
198 }
199
200 return $validatorConjunction;
201 }
202
203 /**
204 * Returns an object of an appropriate validator for the given class. If no validator is available
205 * NULL is returned
206 *
207 * @param string $validatorName Either the fully qualified class name of the validator or the short name of a built-in validator
208 * @return string Name of the validator object or FALSE
209 */
210 protected function resolveValidatorObjectName($validatorName) {
211 // @TODO: Quick and dirty:
212 if (class_exists($validatorName) && substr($validatorName, -9) === 'Validator') return $validatorName;
213
214 $possibleClassName = 'Tx_Extbase_Validation_Validator_' . $this->unifyDataType($validatorName) . 'Validator';
215 if (class_exists($possibleClassName)) return $possibleClassName;
216
217 return FALSE;
218 }
219
220 /**
221 * Preprocess data types. Used to map primitive PHP types to DataTypes used in Extbase.
222 *
223 * @param string $type Data type to unify
224 * @return string unified data type
225 */
226 protected function unifyDataType($type) {
227 switch ($type) {
228 case 'int' :
229 $type = 'Integer';
230 break;
231 case 'string' :
232 $type = 'Text';
233 break;
234 case 'bool' :
235 $type = 'Boolean';
236 break;
237 case 'double' :
238 $type = 'Float';
239 break;
240 case 'numeric' :
241 $type = 'Number';
242 break;
243 case 'mixed' :
244 $type = 'Raw';
245 break;
246 }
247 return ucfirst($type);
248 }
249
250 }
251
252 ?>