1
|
|
|
<?php |
2
|
|
|
namespace Agavi\Validator; |
3
|
|
|
|
4
|
|
|
// +---------------------------------------------------------------------------+ |
5
|
|
|
// | This file is part of the Agavi package. | |
6
|
|
|
// | Copyright (c) 2005-2011 the Agavi Project. | |
7
|
|
|
// | | |
8
|
|
|
// | For the full copyright and license information, please view the LICENSE | |
9
|
|
|
// | file that was distributed with this source code. You can also view the | |
10
|
|
|
// | LICENSE file online at http://www.agavi.org/LICENSE.txt | |
11
|
|
|
// | vi: set noexpandtab: | |
12
|
|
|
// | Local Variables: | |
13
|
|
|
// | indent-tabs-mode: t | |
14
|
|
|
// | End: | |
15
|
|
|
// +---------------------------------------------------------------------------+ |
16
|
|
|
use Agavi\Core\Context; |
17
|
|
|
use Agavi\Exception\ConfigurationException; |
18
|
|
|
use Agavi\Exception\ValidatorException; |
19
|
|
|
use Agavi\Request\RequestDataHolder; |
20
|
|
|
use Agavi\Util\ArrayPathDefinition; |
21
|
|
|
use Agavi\Util\ParameterHolder; |
22
|
|
|
use Agavi\Util\Toolkit; |
23
|
|
|
use Agavi\Util\VirtualArrayPath; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* Validator allows you to validate input |
27
|
|
|
* |
28
|
|
|
* Parameters for use in most validators: |
29
|
|
|
* 'name' name of validator |
30
|
|
|
* 'base' base path for validation of arrays |
31
|
|
|
* 'arguments' an array of input parameter keys to validate |
32
|
|
|
* 'export' destination for exported data |
33
|
|
|
* 'depends' list of dependencies needed by the validator |
34
|
|
|
* 'provides' list of dependencies the validator provides after success |
35
|
|
|
* 'severity' error severity in case of failure |
36
|
|
|
* 'error' error message when validation fails |
37
|
|
|
* 'errors' an array of errors with the reason as key |
38
|
|
|
* 'required' if true the validator will fail when the input parameter is |
39
|
|
|
* not set |
40
|
|
|
* |
41
|
|
|
* @package agavi |
42
|
|
|
* @subpackage validator |
43
|
|
|
* |
44
|
|
|
* @author Dominik del Bondio <[email protected]> |
45
|
|
|
* @author Uwe Mesecke <[email protected]> |
46
|
|
|
* @copyright Authors |
47
|
|
|
* @copyright The Agavi Project |
48
|
|
|
* |
49
|
|
|
* @since 0.11.0 |
50
|
|
|
* |
51
|
|
|
* @version $Id$ |
52
|
|
|
*/ |
53
|
|
|
abstract class Validator extends ParameterHolder |
54
|
|
|
{ |
55
|
|
|
/** |
56
|
|
|
* validator field success flag |
57
|
|
|
*/ |
58
|
|
|
const NOT_PROCESSED = -1; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* validator error severity (the validator succeeded) |
62
|
|
|
*/ |
63
|
|
|
const SUCCESS = 0; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* validator error severity (validator failed but without impact on result |
67
|
|
|
* of whole validation process, completely silent and does not remove the |
68
|
|
|
* "failed" parameters from the input parameters) |
69
|
|
|
*/ |
70
|
|
|
const INFO = 100; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* validator error severity (validator failed but without impact on result |
74
|
|
|
* of whole validation process and completely silent) |
75
|
|
|
*/ |
76
|
|
|
const SILENT = 200; |
77
|
|
|
const NONE = Validator::SILENT; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* validator error severity (validator failed but without impact on result |
81
|
|
|
* of whole validation process) |
82
|
|
|
*/ |
83
|
|
|
const NOTICE = 300; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* validation error severity (validator failed but validation process |
87
|
|
|
* continues) |
88
|
|
|
*/ |
89
|
|
|
const ERROR = 400; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* validation error severity (validator failed and validation process will |
93
|
|
|
* be aborted) |
94
|
|
|
*/ |
95
|
|
|
const CRITICAL = 500; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @var Context An Context instance. |
99
|
|
|
*/ |
100
|
|
|
protected $context = null; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @var ValidatorContainerInterface parent validator container (in |
104
|
|
|
* most cases the validator manager) |
105
|
|
|
*/ |
106
|
|
|
protected $parentContainer = null; |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @var VirtualArrayPath The current base for input names, |
110
|
|
|
* dependencies etc. |
111
|
|
|
*/ |
112
|
|
|
protected $curBase = null; |
113
|
|
|
|
114
|
|
|
/** |
115
|
|
|
* @var string The name of this validator instance. This will either |
116
|
|
|
* be the user supplied name (if any) or a random string |
117
|
|
|
*/ |
118
|
|
|
protected $name = null; |
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* @var RequestDataHolder The parameters which should be validated |
122
|
|
|
* in the current validation run. |
123
|
|
|
*/ |
124
|
|
|
protected $validationParameters = null; |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* @var array The name of the request parameters serving as argument to |
128
|
|
|
* this validator. |
129
|
|
|
*/ |
130
|
|
|
protected $arguments = array(); |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* @var array The error messages. |
134
|
|
|
*/ |
135
|
|
|
protected $errorMessages = array(); |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* @var ValidationIncident The current incident. |
139
|
|
|
*/ |
140
|
|
|
protected $incident = null; |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @var array The affected arguments of this validation run. |
144
|
|
|
*/ |
145
|
|
|
protected $affectedArguments = array(); |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Returns the base path of this validator. |
149
|
|
|
* |
150
|
|
|
* @return VirtualArrayPath The basepath of this validator |
151
|
|
|
* |
152
|
|
|
* @author Dominik del Bondio <[email protected]> |
153
|
|
|
* @since 0.11.0 |
154
|
|
|
*/ |
155
|
|
|
public function getBase() |
156
|
|
|
{ |
157
|
|
|
return $this->curBase; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
/** |
161
|
|
|
* Returns the "keys" in the path of the base |
162
|
|
|
* |
163
|
|
|
* @return array The keys from left to right |
164
|
|
|
* |
165
|
|
|
* @author Dominik del Bondio <[email protected]> |
166
|
|
|
* @since 0.11.0 |
167
|
|
|
*/ |
168
|
|
|
public function getBaseKeys() |
169
|
|
|
{ |
170
|
|
|
$keys = array(); |
171
|
|
|
$l = $this->curBase->length(); |
172
|
|
|
for ($i = 1; $i < $l; ++$i) { |
173
|
|
|
$keys[] = $this->curBase->get($i); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
return $keys; |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Returns the last "keys" in the path of the base |
181
|
|
|
* |
182
|
|
|
* @return mixed The key |
183
|
|
|
* |
184
|
|
|
* @author Dominik del Bondio <[email protected]> |
185
|
|
|
* @since 0.11.0 |
186
|
|
|
*/ |
187
|
|
|
public function getLastKey() |
188
|
|
|
{ |
189
|
|
|
$base = $this->curBase; |
190
|
|
|
if ($base->length() == 0 || ($base->length() == 1 && $base->isAbsolute())) { |
191
|
|
|
return null; |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
return $base->get($base->length() - 1); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Returns the name of this validator. |
199
|
|
|
* |
200
|
|
|
* @return string The name |
201
|
|
|
* |
202
|
|
|
* @author Dominik del Bondio <[email protected]> |
203
|
|
|
* @since 0.11.0 |
204
|
|
|
*/ |
205
|
|
|
public function getName() |
206
|
|
|
{ |
207
|
|
|
return $this->name; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* Initialize this validator. |
212
|
|
|
* |
213
|
|
|
* @param Context $context The Context. |
214
|
|
|
* @param array $parameters An array of validator parameters. |
215
|
|
|
* @param array $arguments An array of argument names which should be validated. |
216
|
|
|
* @param array $errors An array of error messages. |
217
|
|
|
* |
218
|
|
|
* @author Dominik del Bondio <[email protected]> |
219
|
|
|
* @since 0.11.0 |
220
|
|
|
*/ |
221
|
|
|
public function initialize(Context $context, array $parameters = array(), array $arguments = array(), array $errors = array()) |
222
|
|
|
{ |
223
|
|
|
$this->context = $context; |
224
|
|
|
|
225
|
|
|
$this->arguments = $arguments; |
226
|
|
|
$this->errorMessages = $errors; |
227
|
|
|
|
228
|
|
View Code Duplication |
if (!isset($parameters['depends']) || !is_array($parameters['depends'])) { |
|
|
|
|
229
|
|
|
$parameters['depends'] = (!empty($parameters['depends'])) ? explode(' ', $parameters['depends']) : array(); |
230
|
|
|
} |
231
|
|
View Code Duplication |
if (!isset($parameters['provides']) || !is_array($parameters['provides'])) { |
|
|
|
|
232
|
|
|
$parameters['provides'] = (!empty($parameters['provides'])) ? explode(' ', $parameters['provides']) : array(); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
if (!isset($parameters['source'])) { |
236
|
|
|
$parameters['source'] = RequestDataHolder::SOURCE_PARAMETERS; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
$this->setParameters($parameters); |
240
|
|
|
|
241
|
|
|
$this->name = $this->getParameter('name', Toolkit::uniqid()); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
/** |
245
|
|
|
* Retrieve the current application context. |
246
|
|
|
* |
247
|
|
|
* @return Context The current Context instance. |
248
|
|
|
* |
249
|
|
|
* @author David Zülke <[email protected]> |
250
|
|
|
* @since 0.11.0 |
251
|
|
|
*/ |
252
|
|
|
final public function getContext() |
253
|
|
|
{ |
254
|
|
|
return $this->context; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Retrieve the parent container. |
259
|
|
|
* |
260
|
|
|
* @return ValidatorContainerInterface The parent container. |
261
|
|
|
* |
262
|
|
|
* @author Dominik del Bondio <[email protected]> |
263
|
|
|
* @since 0.11.0 |
264
|
|
|
*/ |
265
|
|
|
final public function getParentContainer() |
266
|
|
|
{ |
267
|
|
|
return $this->parentContainer; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Sets the parent container. |
272
|
|
|
* |
273
|
|
|
* @param ValidatorContainerInterface $parent The parent container. |
274
|
|
|
* |
275
|
|
|
* @author Dominik del Bondio <[email protected]> |
276
|
|
|
* @since 0.11.0 |
277
|
|
|
*/ |
278
|
|
|
public function setParentContainer(ValidatorContainerInterface $parent) |
279
|
|
|
{ |
280
|
|
|
// we need a reference here, so when looping happens in a parent |
281
|
|
|
// we always have the right base |
282
|
|
|
$this->curBase = $parent->getBase(); |
283
|
|
|
$this->parentContainer = $parent; |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Validates the input. |
288
|
|
|
* |
289
|
|
|
* This is the method where all the validation stuff is going to happen. |
290
|
|
|
* Inherited classes have to implement their validation logic here. It |
291
|
|
|
* returns only true or false as validation results. The handling of |
292
|
|
|
* error severities is done by the validator itself and should not concern |
293
|
|
|
* the writer of a new validator. |
294
|
|
|
* |
295
|
|
|
* @return bool The result of the validation. |
296
|
|
|
* |
297
|
|
|
* @author Uwe Mesecke <[email protected]> |
298
|
|
|
* @since 0.11.0 |
299
|
|
|
*/ |
300
|
|
|
abstract protected function validate(); |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Shuts the validator down. |
304
|
|
|
* |
305
|
|
|
* This method can be used in validators to shut down used models or |
306
|
|
|
* other activities before the validator is killed. |
307
|
|
|
* |
308
|
|
|
* @see AgaviValidationManager::shutdown() |
309
|
|
|
* |
310
|
|
|
* @author Uwe Mesecke <[email protected]> |
311
|
|
|
* @since 0.11.0 |
312
|
|
|
*/ |
313
|
|
|
public function shutdown() |
314
|
|
|
{ |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Returns the specified input value. |
319
|
|
|
* |
320
|
|
|
* The given parameter is fetched from the request. You should _always_ |
321
|
|
|
* use this method to fetch data from the request because it pays attention |
322
|
|
|
* to specified paths. |
323
|
|
|
* |
324
|
|
|
* @param string $paramName The name of the parameter to fetch from request. |
325
|
|
|
* |
326
|
|
|
* @return mixed The input value from the validation input. |
327
|
|
|
* |
328
|
|
|
* @author Dominik del Bondio <[email protected]> |
329
|
|
|
* @since 0.11.0 |
330
|
|
|
*/ |
331
|
|
|
protected function &getData($paramName) |
332
|
|
|
{ |
333
|
|
|
$paramType = $this->getParameter('source'); |
334
|
|
|
$array =& $this->validationParameters->getAll($paramType); |
335
|
|
|
return $this->curBase->getValueByChildPath($paramName, $array); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* Returns true if this validator has multiple arguments which need to be |
340
|
|
|
* validated. |
341
|
|
|
* |
342
|
|
|
* @return bool Whether this validator has multiple arguments or not. |
343
|
|
|
* |
344
|
|
|
* @author Dominik del Bondio <[email protected]> |
345
|
|
|
* @since 0.11.0 |
346
|
|
|
*/ |
347
|
|
|
protected function hasMultipleArguments() |
348
|
|
|
{ |
349
|
|
|
return count($this->arguments) > 1; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Returns the name of the argument which should be validated. |
354
|
|
|
* Returns the name of the first (and typically only) argument by default, or, |
355
|
|
|
* if a string is provided to the method, returns the name of the argument |
356
|
|
|
* as configured for that identifier. |
357
|
|
|
* |
358
|
|
|
* @param string $name The optional argument identifier, as configured. |
359
|
|
|
* |
360
|
|
|
* @return string The resulting name of the argument in the request data. |
361
|
|
|
* |
362
|
|
|
* @author Dominik del Bondio <[email protected]> |
363
|
|
|
* @author David Zülke <[email protected]> |
364
|
|
|
* |
365
|
|
|
* @since 0.11.0 |
366
|
|
|
*/ |
367
|
|
|
protected function getArgument($name = null) |
368
|
|
|
{ |
369
|
|
|
if ($name === null) { |
370
|
|
|
$argNames = $this->arguments; |
371
|
|
|
reset($argNames); |
372
|
|
|
return current($argNames); |
373
|
|
|
} else { |
374
|
|
|
if (isset($this->arguments[$name])) { |
375
|
|
|
return $this->arguments[$name]; |
376
|
|
|
} |
377
|
|
|
} |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Returns all arguments which should be validated. |
382
|
|
|
* |
383
|
|
|
* @return array A list of input arguments names. |
384
|
|
|
* |
385
|
|
|
* @author Dominik del Bondio <[email protected]> |
386
|
|
|
* @since 0.11.0 |
387
|
|
|
*/ |
388
|
|
|
protected function getArguments() |
389
|
|
|
{ |
390
|
|
|
return $this->arguments; |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
/** |
394
|
|
|
* Sets the arguments which should be flagged with the result of the |
395
|
|
|
* validator |
396
|
|
|
* |
397
|
|
|
* @param array $arguments A list of (absolute) argument names |
398
|
|
|
* |
399
|
|
|
* @author Dominik del Bondio <[email protected]> |
400
|
|
|
* @since 0.11.0 |
401
|
|
|
*/ |
402
|
|
|
protected function setAffectedArguments($arguments) |
403
|
|
|
{ |
404
|
|
|
$this->affectedArguments = $arguments; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Returns whether all arguments are set in the validation input parameters. |
409
|
|
|
* Set means anything but empty string. |
410
|
|
|
* |
411
|
|
|
* @param bool $throwError Whether an error should be thrown for each missing |
412
|
|
|
* argument if this validator is required. |
413
|
|
|
* |
414
|
|
|
* @return bool Whether the arguments are set. |
415
|
|
|
* |
416
|
|
|
* @author Dominik del Bondio <[email protected]> |
417
|
|
|
* @since 0.11.0 |
418
|
|
|
*/ |
419
|
|
|
protected function checkAllArgumentsSet($throwError = true) |
420
|
|
|
{ |
421
|
|
|
$isRequired = $this->getParameter('required', true); |
422
|
|
|
$paramType = $this->getParameter('source'); |
423
|
|
|
$result = true; |
424
|
|
|
|
425
|
|
|
foreach ($this->getArguments() as $argument) { |
426
|
|
|
$pName = $this->curBase->pushRetNew($argument)->__toString(); |
427
|
|
|
if ($this->validationParameters->isValueEmpty($paramType, $pName)) { |
428
|
|
|
if ($throwError && $isRequired) { |
429
|
|
|
$this->throwError('required', $pName); |
430
|
|
|
} |
431
|
|
|
$result = false; |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
return $result; |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* Retrieves the error message for the given index with fallback. |
439
|
|
|
* |
440
|
|
|
* If the given index does not exist in the error messages array, it first |
441
|
|
|
* checks if an unnamed error message exists and returns it or falls back the |
442
|
|
|
* the backup message. |
443
|
|
|
* |
444
|
|
|
* @param string $index The name of the error. |
445
|
|
|
* @param string $backupMessage The backup error message. |
446
|
|
|
* |
447
|
|
|
* @author Dominik del Bondio <[email protected]> |
448
|
|
|
* @since 0.11.0 |
449
|
|
|
*/ |
450
|
|
|
protected function getErrorMessage($index = null, $backupMessage = null) |
451
|
|
|
{ |
452
|
|
|
if ($index !== null && isset($this->errorMessages[$index])) { |
453
|
|
|
$error = $this->errorMessages[$index]; |
454
|
|
|
} elseif (isset($this->errorMessages[''])) { |
455
|
|
|
// check if a default error exists. |
456
|
|
|
$error = $this->errorMessages['']; |
457
|
|
|
} else { |
458
|
|
|
$error = $backupMessage; |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
return $error; |
462
|
|
|
} |
463
|
|
|
|
464
|
|
|
/** |
465
|
|
|
* Submits an error to the error manager. |
466
|
|
|
* |
467
|
|
|
* Will look up the index in the errors array with automatic fallback to the |
468
|
|
|
* default error. You can optionally specify the fields affected by this |
469
|
|
|
* error. The error will be appended to the current incident. |
470
|
|
|
* |
471
|
|
|
* @param string $index The name of the error parameter to fetch the message |
472
|
|
|
* from. |
473
|
|
|
* @param string|array $affectedArgument The arguments which are affected by this error. |
474
|
|
|
* If null is given it will affect all fields. |
475
|
|
|
* @param boolean $argumentsRelative Whether the argument names in $affectedArgument are |
476
|
|
|
* relative or absolute. |
477
|
|
|
* @param boolean $setAffected Whether to set the affected fields of the validator |
478
|
|
|
* to the $affectedArguments |
479
|
|
|
* |
480
|
|
|
* @author Dominik del Bondio <[email protected]> |
481
|
|
|
* @since 0.11.0 |
482
|
|
|
*/ |
483
|
|
|
protected function throwError($index = null, $affectedArgument = null, $argumentsRelative = false, $setAffected = false) |
484
|
|
|
{ |
485
|
|
|
if ($affectedArgument === null) { |
486
|
|
|
$affectedArguments = $this->getFullArgumentNames(); |
487
|
|
|
} else { |
488
|
|
|
$affectedArguments = (array) $affectedArgument; |
489
|
|
|
if ($argumentsRelative) { |
490
|
|
|
foreach ($affectedArguments as &$arg) { |
491
|
|
|
$arg = $this->curBase->pushRetNew($arg)->__toString(); |
492
|
|
|
} |
493
|
|
|
} |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
if ($setAffected) { |
497
|
|
|
$this->affectedArguments = $affectedArguments; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
$error = $this->getErrorMessage($index); |
501
|
|
|
|
502
|
|
|
if ($this->hasParameter('translation_domain')) { |
503
|
|
|
$error = $this->getContext()->getTranslationManager()->_($error, $this->getParameter('translation_domain')); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
if (!$this->incident) { |
507
|
|
|
$this->incident = new ValidationIncident($this, self::mapErrorCode($this->getParameter('severity', 'error'))); |
508
|
|
|
} |
509
|
|
|
|
510
|
|
|
foreach ($affectedArguments as &$argument) { |
511
|
|
|
$argument = new ValidationArgument($argument, $this->getParameter('source')); |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
if ($error !== null || count($affectedArguments) != 0) { |
515
|
|
|
// don't throw empty error messages without affected fields |
516
|
|
|
$this->incident->addError(new ValidationError($error, $index, $affectedArguments)); |
517
|
|
|
} |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
/** |
521
|
|
|
* Exports a value back into the request. |
522
|
|
|
* |
523
|
|
|
* Exports data into the request at the index given in the parameter |
524
|
|
|
* 'export'. If there is no such parameter, then the method returns |
525
|
|
|
* without exporting. |
526
|
|
|
* |
527
|
|
|
* Similar to getData() you should always use export() to submit data to |
528
|
|
|
* the request because it pays attention to paths and otherwise you could |
529
|
|
|
* overwrite stuff you don't want to. |
530
|
|
|
* |
531
|
|
|
* @param mixed $value The value to be exported. |
532
|
|
|
* @param mixed $argument An optional parameter name which should be used for |
533
|
|
|
* exporting instead of the "export" attribute value, or an |
534
|
|
|
* AgaviValidationArgument object if the value should be |
535
|
|
|
* exported to a different source. |
536
|
|
|
* @param int $result The result status code to use for the exported value. |
537
|
|
|
* Defaults to AgaviValidator::SUCCESS. |
538
|
|
|
* |
539
|
|
|
* @author Dominik del Bondio <[email protected]> |
540
|
|
|
* @author David Zülke <[email protected]> |
541
|
|
|
* @since 0.11.0 |
542
|
|
|
*/ |
543
|
|
|
protected function export($value, $argument = null, $result = null) |
544
|
|
|
{ |
545
|
|
|
if ($argument === null) { |
546
|
|
|
$argument = $this->getParameter('export'); |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
if ($result === null) { |
550
|
|
|
$result = $this->getParameter('export_severity', Validator::SUCCESS); |
551
|
|
|
if (!is_numeric($result) && defined($result)) { |
552
|
|
|
$result = constant($result); |
553
|
|
|
} |
554
|
|
|
} |
555
|
|
|
|
556
|
|
|
if (!($argument instanceof ValidationArgument) && (!is_string($argument) || $argument === '')) { |
557
|
|
|
return; |
558
|
|
|
} |
559
|
|
|
|
560
|
|
|
if ($argument instanceof ValidationArgument) { |
561
|
|
|
$source = $argument->getSource(); |
562
|
|
|
$name = $argument->getName(); |
563
|
|
|
} else { |
564
|
|
|
$source = $this->getParameter('export_to_source', $this->getParameter('source')); |
565
|
|
|
$name = $argument; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
$array =& $this->validationParameters->getAll($source); |
569
|
|
|
$currentParts = $this->curBase->getParts(); |
570
|
|
|
|
571
|
|
|
if (count($currentParts) > 0 && strpos($name, '%') !== false) { |
572
|
|
|
// this is a validator which actually has a base (<arguments base="xx">) set |
573
|
|
|
// and the export name contains sprintf syntax |
574
|
|
|
$name = vsprintf($name, $currentParts); |
575
|
|
|
} |
576
|
|
|
// CAUTION |
577
|
|
|
// we had a feature here during development that would allow [] at the end to append values to an array |
578
|
|
|
// that would, however, mean that we have to cast the value to an array, and, either way, a user would be able to manipulate the keys |
579
|
|
|
// example: we export to foo[], and the user supplies ?foo[28] in the URL. that means our export will be in foo[29]. foo[28] will be removed by the validation, but the keys are still potentially harmful |
580
|
|
|
// that's why we decided to remove this again |
581
|
|
|
$cp = new VirtualArrayPath($name); |
582
|
|
|
$cp->setValue($array, $value); |
583
|
|
|
if ($this->parentContainer !== null) { |
584
|
|
|
// make sure the parameter doesn't get removed by the validation manager |
585
|
|
|
if (is_array($value)) { |
586
|
|
|
// for arrays all child elements need to be marked as not processed |
587
|
|
|
foreach (ArrayPathDefinition::getFlatKeyNames($value) as $keyName) { |
588
|
|
|
$this->parentContainer->addArgumentResult(new ValidationArgument($cp->pushRetNew($keyName)->__toString(), $source), $result, $this); |
589
|
|
|
} |
590
|
|
|
} |
591
|
|
|
$this->parentContainer->addArgumentResult(new ValidationArgument($cp->__toString(), $source), $result, $this); |
592
|
|
|
} |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
/** |
596
|
|
|
* Validates this validator in the given base. |
597
|
|
|
* |
598
|
|
|
* @param VirtualArrayPath $base The base in which the input should be |
599
|
|
|
* validated. |
600
|
|
|
* |
601
|
|
|
* @return int AgaviValidator::SUCCESS if validation succeeded or given |
602
|
|
|
* error severity. |
603
|
|
|
* |
604
|
|
|
* @author Dominik del Bondio <[email protected]> |
605
|
|
|
* @author Uwe Mesecke <[email protected]> |
606
|
|
|
* @since 0.11.0 |
607
|
|
|
*/ |
608
|
|
|
protected function validateInBase(VirtualArrayPath $base) |
609
|
|
|
{ |
610
|
|
|
$base = clone $base; |
611
|
|
|
if ($base->length() == 0) { |
612
|
|
|
// we have an empty base so we do the actual validation |
613
|
|
View Code Duplication |
if ($this->getDependencyManager() && (count($this->getParameter('depends')) > 0 && !$this->getDependencyManager()->checkDependencies($this->getParameter('depends'), $this->curBase))) { |
|
|
|
|
614
|
|
|
// dependencies not met, exit with success |
615
|
|
|
return self::NOT_PROCESSED; |
616
|
|
|
} |
617
|
|
|
|
618
|
|
|
$this->affectedArguments = $this->getFullArgumentNames(); |
619
|
|
|
|
620
|
|
|
$result = self::SUCCESS; |
621
|
|
|
$errorCode = self::mapErrorCode($this->getParameter('severity', 'error')); |
622
|
|
|
|
623
|
|
|
if ($this->checkAllArgumentsSet(false)) { |
624
|
|
|
if (!$this->validate()) { |
625
|
|
|
// validation failed, exit with configured error code |
626
|
|
|
$result = $errorCode; |
627
|
|
|
} |
628
|
|
|
} else { |
629
|
|
|
if ($this->getParameter('required', true)) { |
630
|
|
|
$this->throwError('required'); |
631
|
|
|
$result = $errorCode; |
632
|
|
|
} else { |
633
|
|
|
// we don't throw an error here because this is not an incident per se |
634
|
|
|
// but rather a non validated field |
635
|
|
|
$result = self::NOT_PROCESSED; |
636
|
|
|
} |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
if ($this->parentContainer !== null) { |
640
|
|
|
foreach ($this->affectedArguments as $fieldname) { |
641
|
|
|
$this->parentContainer->addArgumentResult(new ValidationArgument($fieldname, $this->getParameter('source')), $result, $this); |
642
|
|
|
} |
643
|
|
|
|
644
|
|
|
if ($this->incident) { |
645
|
|
|
$this->parentContainer->addIncident($this->incident); |
646
|
|
|
} |
647
|
|
|
} |
648
|
|
|
|
649
|
|
|
$this->incident = null; |
650
|
|
|
// put dependencies provided by this validator into manager |
651
|
|
View Code Duplication |
if ($this->getDependencyManager() && ($result == self::SUCCESS && count($this->getParameter('provides')) > 0)) { |
|
|
|
|
652
|
|
|
$this->getDependencyManager()->addDependTokens($this->getParameter('provides'), $this->curBase); |
653
|
|
|
} |
654
|
|
|
return $result; |
655
|
|
|
} elseif ($base->left() !== '') { |
656
|
|
|
/* |
657
|
|
|
* the next component in the base is no wildcard so we |
658
|
|
|
* just put it into our own base and validate further |
659
|
|
|
* into the base. |
660
|
|
|
*/ |
661
|
|
|
|
662
|
|
|
$this->curBase->push($base->shift()); |
663
|
|
|
$ret = $this->validateInBase($base); |
664
|
|
|
$this->curBase->pop(); |
665
|
|
|
|
666
|
|
|
return $ret; |
667
|
|
|
} else { |
668
|
|
|
/* |
669
|
|
|
* now we have a wildcard as next component so we collect |
670
|
|
|
* all defined value names in the request at the path |
671
|
|
|
* specified by our own base and validate in each of that |
672
|
|
|
* names |
673
|
|
|
*/ |
674
|
|
|
$names = $this->getKeysInCurrentBase(); |
675
|
|
|
|
676
|
|
|
// if the names array is empty this means we need to throw an error since |
677
|
|
|
// this means the input doesn't exist |
678
|
|
|
if (count($names) == 0) { |
679
|
|
|
if ($this->getDependencyManager() && (count($this->getParameter('depends')) > 0 && !$this->getDependencyManager()->checkDependencies($this->getParameter('depends'), $this->curBase))) { |
680
|
|
|
// since the dependencies are only ever checked if the base gets empty (which happens when |
681
|
|
|
// the validation is about to validate an argument), but we are already bailing out in an earlier |
682
|
|
|
// stage, lets do the dependency check so the validator doesn't accidently return an error even |
683
|
|
|
// if it's dependencies aren't met |
684
|
|
|
return self::NOT_PROCESSED; |
685
|
|
|
} else { |
686
|
|
|
if ($this->getParameter('required', true)) { |
687
|
|
|
$this->throwError('required'); |
688
|
|
|
return self::mapErrorCode($this->getParameter('severity', 'error')); |
689
|
|
|
} else { |
690
|
|
|
return self::NOT_PROCESSED; |
691
|
|
|
} |
692
|
|
|
} |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
// throw the wildcard away |
696
|
|
|
$base->shift(); |
697
|
|
|
|
698
|
|
|
$ret = self::NOT_PROCESSED; |
699
|
|
|
|
700
|
|
|
// validate in every name defined in the request |
701
|
|
|
foreach ($names as $name) { |
702
|
|
|
$newBase = clone $base; |
703
|
|
|
$newBase->unshift($name); |
704
|
|
|
$t = $this->validateInBase($newBase); |
705
|
|
|
|
706
|
|
|
if ($t == self::CRITICAL) { |
707
|
|
|
return $t; |
708
|
|
|
} |
709
|
|
|
|
710
|
|
|
// remember the highest error severity |
711
|
|
|
$ret = max($ret, $t); |
712
|
|
|
} |
713
|
|
|
|
714
|
|
|
return $ret; |
715
|
|
|
} |
716
|
|
|
} |
717
|
|
|
|
718
|
|
|
/** |
719
|
|
|
* Executes the validator. |
720
|
|
|
* |
721
|
|
|
* @param RequestDataHolder $parameters The data which should be validated. |
722
|
|
|
* |
723
|
|
|
* @return int The validation result (see severity constants). |
724
|
|
|
* |
725
|
|
|
* @author Uwe Mesecke <[email protected]> |
726
|
|
|
* @since 0.11.0 |
727
|
|
|
*/ |
728
|
|
|
public function execute(RequestDataHolder $parameters) |
729
|
|
|
{ |
730
|
|
|
if ($this->getParameter('source') != RequestDataHolder::SOURCE_PARAMETERS && !in_array($this->getParameter('source'), $parameters->getSourceNames())) { |
731
|
|
|
throw new ConfigurationException('Unknown source "' . $this->getParameter('source') . '" specified in validator ' . $this->getName()); |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
$this->validationParameters = $parameters; |
735
|
|
|
$base = new VirtualArrayPath($this->getParameter('base')); |
736
|
|
|
|
737
|
|
|
$res = $this->validateInBase($base); |
738
|
|
|
if ($this->incident && $this->parentContainer) { |
739
|
|
|
$this->parentContainer->addIncident($this->incident); |
740
|
|
|
$this->incident = null; |
741
|
|
|
} |
742
|
|
|
return $res; |
743
|
|
|
} |
744
|
|
|
|
745
|
|
|
/** |
746
|
|
|
* Converts string severity codes into integer values |
747
|
|
|
* (see severity constants) |
748
|
|
|
* |
749
|
|
|
* critical -> Validator::CRITICAL |
750
|
|
|
* error -> Validator::ERROR |
751
|
|
|
* notice -> Validator::NOTICE |
752
|
|
|
* none -> Validator::NONE |
753
|
|
|
* success -> not allowed to be specified by the user. |
754
|
|
|
* |
755
|
|
|
* @param string $code The error severity as string. |
756
|
|
|
* |
757
|
|
|
* @return int The error severity as in (see severity constants). |
758
|
|
|
* |
759
|
|
|
* @throws <b>AgaviValidatorException</b> if the input was no known |
760
|
|
|
* severity |
761
|
|
|
* |
762
|
|
|
* @author Uwe Mesecke <[email protected]> |
763
|
|
|
* @since 0.11.0 |
764
|
|
|
*/ |
765
|
|
|
public static function mapErrorCode($code) |
766
|
|
|
{ |
767
|
|
|
switch (strtolower($code)) { |
768
|
|
|
case 'critical': |
769
|
|
|
return self::CRITICAL; |
770
|
|
|
case 'error': |
771
|
|
|
return self::ERROR; |
772
|
|
|
case 'notice': |
773
|
|
|
return self::NOTICE; |
774
|
|
|
case 'none': |
775
|
|
|
case 'silent': |
776
|
|
|
return self::SILENT; |
777
|
|
|
case 'info': |
778
|
|
|
return self::INFO; |
779
|
|
|
default: |
780
|
|
|
throw new ValidatorException('unknown error code: '.$code); |
781
|
|
|
} |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
/** |
785
|
|
|
* Returns all available keys in the currently set base. |
786
|
|
|
* |
787
|
|
|
* @return array The available keys. |
788
|
|
|
* |
789
|
|
|
* @author Dominik del Bondio <[email protected]> |
790
|
|
|
* @since 0.11.0 |
791
|
|
|
*/ |
792
|
|
|
protected function getKeysInCurrentBase() |
793
|
|
|
{ |
794
|
|
|
$paramType = $this->getParameter('source'); |
795
|
|
|
|
796
|
|
|
$array = $this->validationParameters->getAll($paramType); |
797
|
|
|
$names = $this->curBase->getValue($array, array()); |
798
|
|
|
|
799
|
|
|
return is_array($names) ? array_keys($names) : array(); |
800
|
|
|
} |
801
|
|
|
|
802
|
|
|
/** |
803
|
|
|
* Returns all arguments with their full path. |
804
|
|
|
* |
805
|
|
|
* @return array The arguments. |
806
|
|
|
* |
807
|
|
|
* @author Dominik del Bondio <[email protected]> |
808
|
|
|
* @since 0.11.0 |
809
|
|
|
*/ |
810
|
|
|
protected function getFullArgumentNames() |
811
|
|
|
{ |
812
|
|
|
$arguments = array(); |
813
|
|
|
foreach ($this->getArguments() as $argument) { |
814
|
|
|
if ($argument) { |
815
|
|
|
$arguments[] = $this->curBase->pushRetNew($argument)->__toString(); |
816
|
|
|
} else { |
817
|
|
|
$arguments[] = $this->curBase->__toString(); |
818
|
|
|
} |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
return $arguments; |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
/** |
825
|
|
|
* Returns the depency manager of the parent container if any. |
826
|
|
|
* |
827
|
|
|
* @return DependencyManager The parent's dependency manager. |
828
|
|
|
* |
829
|
|
|
* @author Dominik del Bondio <[email protected]> |
830
|
|
|
* @since 0.11.0 |
831
|
|
|
*/ |
832
|
|
|
public function getDependencyManager() |
833
|
|
|
{ |
834
|
|
|
if ($this->parentContainer instanceof ValidatorContainerInterface) { |
835
|
|
|
return $this->parentContainer->getDependencyManager(); |
836
|
|
|
} |
837
|
|
|
return null; |
838
|
|
|
} |
839
|
|
|
} |
840
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.