[!!!][+FEATURE] Extbase (Security): Added a HMAC generator and checker to prevent...
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / MVC / Controller / Argument.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 * A controller argument
30 *
31 * @package Extbase
32 * @subpackage MVC\Controller
33 * @version $ID:$
34 * @scope prototype
35 */
36 class Tx_Extbase_MVC_Controller_Argument {
37
38 /**
39 * @var Tx_Extbase_Persistence_QueryFactory
40 */
41 protected $queryFactory;
42
43 /**
44 * @var Tx_Extbase_Property_Mapper
45 */
46 protected $propertyMapper;
47
48 /**
49 * @var Tx_Extbase_Reflection_Service
50 */
51 protected $reflectionService;
52
53 /**
54 * Name of this argument
55 * @var string
56 */
57 protected $name = '';
58
59 /**
60 * Short name of this argument
61 * @var string
62 */
63 protected $shortName = NULL;
64
65 /**
66 * Data type of this argument's value
67 * @var string
68 */
69 protected $dataType = 'Text';
70
71 /**
72 * If the data type is an object, the class schema of the data type class is resolved
73 * @var Tx_Extbase_Reflection_ClassSchema
74 */
75 protected $dataTypeClassSchema;
76
77 /**
78 * TRUE if this argument is required
79 * @var boolean
80 */
81 protected $isRequired = FALSE;
82
83 /**
84 * Actual value of this argument
85 * @var object
86 */
87 protected $value = NULL;
88
89 /**
90 * Default value. Used if argument is optional.
91 * @var mixed
92 */
93 protected $defaultValue = NULL;
94
95 /**
96 * A custom validator, used supplementary to the base validation
97 * @var Tx_Extbase_Validation_Validator_ValidatorInterface
98 */
99 protected $validator = NULL;
100
101 /**
102 * Uid for the argument, if it has one
103 * @var string
104 */
105 protected $uid = NULL;
106
107 const ORIGIN_CLIENT = 0;
108 const ORIGIN_PERSISTENCE = 1;
109 const ORIGIN_PERSISTENCE_AND_MODIFIED = 2;
110 const ORIGIN_NEWLY_CREATED = 3;
111
112 /**
113 * The origin of the argument value. This is only meaningful after argument mapping.
114 *
115 * One of the ORIGIN_* constants above
116 * @var integer
117 */
118 protected $origin = 0;
119
120 /**
121 * Constructs this controller argument
122 *
123 * @param string $name Name of this argument
124 * @param string $dataType The data type of this argument
125 * @throws InvalidArgumentException if $name is not a string or empty
126 * @api
127 */
128 public function __construct($name, $dataType = 'Text') {
129 $this->reflectionService = t3lib_div::makeInstance('Tx_Extbase_Reflection_Service');
130 $this->propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper');
131 $this->propertyMapper->injectReflectionService($this->reflectionService);
132 if (!is_string($name) || strlen($name) < 1) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
133 $this->name = $name;
134 $this->setDataType($dataType);
135 }
136
137 /**
138 * Injects the Persistence Manager
139 *
140 * @param Tx_Extbase_Persistence_ManagerInterface
141 * @return void
142 */
143 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
144 $this->persistenceManager = $persistenceManager;
145 }
146
147 /**
148 * Injects a QueryFactory instance
149 *
150 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
151 * @return void
152 */
153 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
154 $this->queryFactory = $queryFactory;
155 }
156
157 /**
158 * Returns the name of this argument
159 *
160 * @return string This argument's name
161 * @api
162 */
163 public function getName() {
164 return $this->name;
165 }
166
167 /**
168 * Sets the short name of this argument.
169 *
170 * @param string $shortName A "short name" - a single character
171 * @return Tx_Extbase_MVC_Controller_Argument $this
172 * @throws InvalidArgumentException if $shortName is not a character
173 * @api
174 */
175 public function setShortName($shortName) {
176 if ($shortName !== NULL && (!is_string($shortName) || strlen($shortName) !== 1)) throw new InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
177 $this->shortName = $shortName;
178 return $this;
179 }
180
181 /**
182 * Returns the short name of this argument
183 *
184 * @return string This argument's short name
185 * @api
186 */
187 public function getShortName() {
188 return $this->shortName;
189 }
190
191 /**
192 * Sets the data type of this argument's value
193 *
194 * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name
195 * @return Tx_Extbase_MVC_Controller_Argument $this
196 * @api
197 */
198 public function setDataType($dataType) {
199 $this->dataType = $dataType;
200 $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($this->dataType);
201 return $this;
202 }
203
204 /**
205 * Returns the data type of this argument's value
206 *
207 * @return string The data type
208 * @api
209 */
210 public function getDataType() {
211 return $this->dataType;
212 }
213
214 /**
215 * Marks this argument to be required
216 *
217 * @param boolean $required TRUE if this argument should be required
218 * @return Tx_Extbase_MVC_Controller_Argument $this
219 * @api
220 */
221 public function setRequired($required) {
222 $this->isRequired = (boolean)$required;
223 return $this;
224 }
225
226 /**
227 * Returns TRUE if this argument is required
228 *
229 * @return boolean TRUE if this argument is required
230 * @api
231 */
232 public function isRequired() {
233 return $this->isRequired;
234 }
235
236 /**
237 * Sets the default value of the argument
238 *
239 * @param mixed $defaultValue Default value
240 * @return void
241 * @api
242 */
243 public function setDefaultValue($defaultValue) {
244 $this->defaultValue = $defaultValue;
245 }
246
247 /**
248 * Returns the default value of this argument
249 *
250 * @return mixed The default value
251 * @api
252 */
253 public function getDefaultValue() {
254 return $this->defaultValue;
255 }
256
257 /**
258 * Sets a custom validator which is used supplementary to the base validation
259 *
260 * @param Tx_Extbase_Validation_Validator_ValidatorInterface $validator The actual validator object
261 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
262 * @api
263 */
264 public function setValidator(Tx_Extbase_Validation_Validator_ValidatorInterface $validator) {
265 $this->validator = $validator;
266 return $this;
267 }
268
269 /**
270 * Create and set a validator chain
271 *
272 * @param array Object names of the validators
273 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
274 * @api
275 */
276 public function setNewValidatorConjunction(array $objectNames) {
277 if ($this->validator === NULL) {
278 $this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ConjunctionValidator');
279 }
280 foreach ($objectNames as $objectName) {
281 if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
282 $this->validator->addValidator(t3lib_div::makeInstance($objectName));
283 }
284 return $this;
285 }
286
287 /**
288 * Returns the set validator
289 *
290 * @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
291 * @api
292 */
293 public function getValidator() {
294 return $this->validator;
295 }
296
297 /**
298 * Get the origin of the argument value. This is only meaningful after argument mapping.
299 *
300 * @return integer one of the ORIGIN_* constants
301 * @author Sebastian Kurf├╝rst <sebastian@typo3.org>
302 */
303 public function getOrigin() {
304 return $this->origin;
305 }
306
307 /**
308 * Sets the value of this argument.
309 *
310 * @param mixed $value: The value of this argument
311 * @return Tx_Extbase_MVC_Controller_Argument $this
312 * @throws Tx_Extbase_MVC_Exception_InvalidArgumentValue if the argument is not a valid object of type $dataType
313 */
314 public function setValue($value) {
315 $this->value = $this->transformValue($value);
316
317 return $this;
318 }
319
320 /**
321 * Checks if the value is a UUID or an array but should be an object, i.e.
322 * the argument's data type class schema is set. If that is the case, this
323 * method tries to look up the corresponding object instead.
324 *
325 * Additionally, it maps arrays to objects in case it is a normal object.
326 *
327 * @param mixed $value The value of an argument
328 * @return mixed
329 */
330 protected function transformValue($value) {
331 if ($value === NULL) {
332 return NULL;
333 }
334 if (!class_exists($this->dataType)) {
335 return $value;
336 }
337 $transformedValue = NULL;
338 if ($this->dataTypeClassSchema !== NULL) {
339 // The target object is an Entity or ValueObject.
340 if (is_numeric($value)) {
341 $this->origin = self::ORIGIN_PERSISTENCE;
342 $transformedValue = $this->findObjectByUid($value);
343 } elseif (is_array($value)) {
344 $this->origin = self::ORIGIN_PERSISTENCE_AND_MODIFIED;
345 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
346 }
347 } else {
348 if (!is_array($value)) {
349 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value was a simple type, so we could not map it to an object. Maybe the @entity or @valueobject annotations are missing?', 1251730701);
350 }
351 $this->origin = self::ORIGIN_NEWLY_CREATED;
352 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
353 }
354
355 if (!($transformedValue instanceof $this->dataType)) {
356 if (is_object($transformedValue)) {
357 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value must be of type "' . $this->dataType . '", but was of type "' . get_class($transformedValue) . '".', 1251730701);
358 } else {
359 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value must be of type "' . $this->dataType . '", but was of type "' . gettype($transformedValue) . '".', 1251730702);
360 }
361 }
362 return $transformedValue;
363 }
364
365 /**
366 * Finds an object from the repository by searching for its technical UID.
367 *
368 * @param int $uid The object's uid
369 * @return mixed Either the object matching the uid or, if none or more than one object was found, FALSE
370 */
371 protected function findObjectByUid($uid) {
372 $query = $this->queryFactory->create($this->dataType);
373 $result = $query->matching($query->withUid($uid))->execute();
374 $object = NULL;
375 if (count($result) > 0) {
376 $object = current($result);
377 }
378 return $object;
379 }
380
381 /**
382 * Returns the value of this argument
383 *
384 * @return object The value of this argument - if none was set, NULL is returned
385 * @api
386 */
387 public function getValue() {
388 if ($this->value === NULL) {
389 return $this->defaultValue;
390 } else {
391 return $this->value;
392 }
393 }
394
395 /**
396 * Checks if this argument has a value set.
397 *
398 * @return boolean TRUE if a value was set, otherwise FALSE
399 */
400 public function isValue() {
401 return $this->value !== NULL;
402 }
403
404 /**
405 * Returns a string representation of this argument's value
406 *
407 * @return string
408 * @api
409 */
410 public function __toString() {
411 return (string)$this->value;
412 }
413 }
414 ?>