1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace ParamProcessor; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use ValueParsers\NullParser; |
7
|
|
|
use ValueParsers\ParseException; |
8
|
|
|
use ValueParsers\ValueParser; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Parameter class, representing the "instance" of a parameter. |
12
|
|
|
* Holds a ParamDefinition, user provided input (name & value) and processing state. |
13
|
|
|
* |
14
|
|
|
* NOTE: as of version 1.0, this class is for internal use only! |
15
|
|
|
* |
16
|
|
|
* @since 1.0 |
17
|
|
|
* |
18
|
|
|
* @licence GNU GPL v2+ |
19
|
|
|
* @author Jeroen De Dauw < [email protected] > |
20
|
|
|
*/ |
21
|
|
|
final class Param implements IParam { |
|
|
|
|
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Indicates whether parameters not found in the criteria list |
25
|
|
|
* should be stored in case they are not accepted. The default is false. |
26
|
|
|
* |
27
|
|
|
* @since 1.0 |
28
|
|
|
* |
29
|
|
|
* @var boolean |
30
|
|
|
*/ |
31
|
|
|
public static $accumulateParameterErrors = false; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* The original parameter name as provided by the user. This can be the |
35
|
|
|
* main name or an alias. |
36
|
|
|
* |
37
|
|
|
* @since 1.0 |
38
|
|
|
* |
39
|
|
|
* @var string |
40
|
|
|
*/ |
41
|
|
|
protected $originalName; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* The original value as provided by the user. This is mainly retained for |
45
|
|
|
* usage in error messages when the parameter turns out to be invalid. |
46
|
|
|
* |
47
|
|
|
* @since 1.0 |
48
|
|
|
* |
49
|
|
|
* @var string |
50
|
|
|
*/ |
51
|
|
|
protected $originalValue; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* The value of the parameter. |
55
|
|
|
* |
56
|
|
|
* @since 1.0 |
57
|
|
|
* |
58
|
|
|
* @var mixed |
59
|
|
|
*/ |
60
|
|
|
protected $value; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Keeps track of how many times the parameter has been set by the user. |
64
|
|
|
* This is used to detect overrides and for figuring out a parameter is missing. |
65
|
|
|
* |
66
|
|
|
* @since 1.0 |
67
|
|
|
* |
68
|
|
|
* @var integer |
69
|
|
|
*/ |
70
|
|
|
protected $setCount = 0; |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* List of validation errors for this parameter. |
74
|
|
|
* |
75
|
|
|
* @since 1.0 |
76
|
|
|
* |
77
|
|
|
* @var array of ProcessingError |
78
|
|
|
*/ |
79
|
|
|
protected $errors = []; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Indicates if the parameter was set to it's default. |
83
|
|
|
* |
84
|
|
|
* @since 1.0 |
85
|
|
|
* |
86
|
|
|
* @var boolean |
87
|
|
|
*/ |
88
|
|
|
protected $defaulted = false; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @since 1.0 |
92
|
|
|
* |
93
|
|
|
* @var ParamDefinition |
94
|
|
|
*/ |
95
|
|
|
protected $definition; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* Constructor. |
99
|
|
|
* |
100
|
|
|
* @since 1.0 |
101
|
|
|
* |
102
|
|
|
* @param IParamDefinition $definition |
103
|
|
|
*/ |
104
|
65 |
|
public function __construct( IParamDefinition $definition ) { |
105
|
65 |
|
$this->definition = $definition; |
|
|
|
|
106
|
65 |
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* Sets and cleans the original value and name. |
110
|
|
|
* @see IParam::setUserValue |
111
|
|
|
* |
112
|
|
|
* @since 1.0 |
113
|
|
|
* |
114
|
|
|
* @param string $paramName |
115
|
|
|
* @param string $paramValue |
116
|
|
|
* @param Options $options |
117
|
|
|
* |
118
|
|
|
* @return boolean |
119
|
|
|
*/ |
120
|
64 |
|
public function setUserValue( $paramName, $paramValue, Options $options ) { |
121
|
64 |
|
if ( $this->setCount > 0 && !$options->acceptOverriding() ) { |
122
|
|
|
// TODO |
123
|
|
|
return false; |
124
|
|
|
} |
125
|
|
|
else { |
126
|
64 |
|
$this->originalName = $paramName; |
127
|
64 |
|
$this->originalValue = $paramValue; |
128
|
|
|
|
129
|
64 |
|
$this->cleanValue( $options ); |
130
|
|
|
|
131
|
64 |
|
$this->setCount++; |
132
|
|
|
|
133
|
64 |
|
return true; |
134
|
|
|
} |
135
|
|
|
} |
136
|
|
|
|
137
|
|
|
/** |
138
|
|
|
* Sets the value. |
139
|
|
|
* |
140
|
|
|
* @since 1.0 |
141
|
|
|
* |
142
|
|
|
* @param mixed $value |
143
|
|
|
*/ |
144
|
57 |
|
public function setValue( $value ) { |
145
|
57 |
|
$this->value = $value; |
146
|
57 |
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* Sets the $value to a cleaned value of $originalValue. |
150
|
|
|
* |
151
|
|
|
* TODO: the per-parameter lowercaseing and trimming here needs some thought |
152
|
|
|
* |
153
|
|
|
* @since 1.0 |
154
|
|
|
* |
155
|
|
|
* @param Options $options |
156
|
|
|
*/ |
157
|
64 |
|
protected function cleanValue( Options $options ) { |
158
|
64 |
|
$this->value = $this->originalValue; |
159
|
|
|
|
160
|
64 |
|
$trim = $this->getDefinition()->trimDuringClean(); |
161
|
|
|
|
162
|
64 |
|
if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) { |
163
|
64 |
|
if ( is_string( $this->value ) ) { |
164
|
64 |
|
$this->value = trim( $this->value ); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
|
169
|
64 |
|
if ( $this->definition->isList() ) { |
170
|
|
|
$this->value = explode( $this->definition->getDelimiter(), $this->value ); |
171
|
|
|
|
172
|
|
|
if ( $trim === true || ( is_null( $trim ) && $options->trimValues() ) ) { |
173
|
|
|
foreach ( $this->value as &$element ) { |
174
|
|
|
if ( is_string( $element ) ) { |
175
|
|
|
$element = trim( $element ); |
176
|
|
|
} |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
} |
180
|
|
|
|
181
|
64 |
|
$definitionOptions = $this->definition->getOptions(); |
182
|
|
|
|
183
|
64 |
|
if ( $options->lowercaseValues() || ( array_key_exists( 'tolower', $definitionOptions ) && $definitionOptions['tolower'] ) ) { |
184
|
|
|
if ( $this->definition->isList() ) { |
185
|
|
|
foreach ( $this->value as &$element ) { |
|
|
|
|
186
|
|
|
if ( is_string( $element ) ) { |
187
|
|
|
$element = strtolower( $element ); |
188
|
|
|
} |
189
|
|
|
} |
190
|
|
|
} |
191
|
|
|
elseif ( is_string( $this->value ) ) { |
192
|
|
|
$this->value = strtolower( $this->value ); |
193
|
|
|
} |
194
|
|
|
} |
195
|
64 |
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Parameter processing entry point. |
199
|
|
|
* Processes the parameter. This includes parsing, validation and additional formatting. |
200
|
|
|
* |
201
|
|
|
* @since 1.0 |
202
|
|
|
* |
203
|
|
|
* @param $definitions array of IParamDefinition |
204
|
|
|
* @param $params array of IParam |
205
|
|
|
* @param Options $options |
206
|
|
|
* |
207
|
|
|
* @throws Exception |
208
|
|
|
*/ |
209
|
65 |
|
public function process( array &$definitions, array $params, Options $options ) { |
210
|
65 |
|
if ( $this->setCount == 0 ) { |
211
|
1 |
|
if ( $this->definition->isRequired() ) { |
212
|
|
|
// This should not occur, so throw an exception. |
213
|
|
|
throw new Exception( 'Attempted to validate a required parameter without first setting a value.' ); |
214
|
|
|
} |
215
|
|
|
else { |
216
|
1 |
|
$this->setToDefault(); |
217
|
|
|
} |
218
|
|
|
} |
219
|
|
|
else { |
220
|
64 |
|
$this->parseAndValidate( $options ); |
221
|
|
|
} |
222
|
|
|
|
223
|
65 |
|
if ( !$this->hasFatalError() && ( $this->definition->shouldManipulateDefault() || !$this->wasSetToDefault() ) ) { |
224
|
57 |
|
$this->definition->format( $this, $definitions, $params ); |
|
|
|
|
225
|
|
|
} |
226
|
65 |
|
} |
227
|
|
|
|
228
|
|
|
/** |
229
|
|
|
* @since 1.0 |
230
|
|
|
* |
231
|
|
|
* @param Options $options |
232
|
|
|
* |
233
|
|
|
* @return ValueParser |
234
|
|
|
*/ |
235
|
64 |
|
public function getValueParser( Options $options ) { |
236
|
64 |
|
$parser = $this->definition->getValueParser(); |
237
|
|
|
|
238
|
64 |
|
if ( get_class( $parser ) === NullParser::class ) { |
239
|
64 |
|
$parserType = $options->isStringlyTyped() ? 'string-parser' : 'typed-parser'; |
240
|
|
|
|
241
|
|
|
// TODO: inject factory |
242
|
64 |
|
$parserClass = ParamDefinitionFactory::singleton()->getComponentForType( $this->definition->getType(), $parserType ); |
|
|
|
|
243
|
|
|
|
244
|
64 |
|
if ( $parserClass !== NullParser::class ) { |
245
|
50 |
|
$parser = new $parserClass( new \ValueParsers\ParserOptions() ); |
246
|
|
|
} |
247
|
|
|
} |
248
|
|
|
|
249
|
64 |
|
return $parser; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* @since 1.0 |
254
|
|
|
* |
255
|
|
|
* @param Options $options |
256
|
|
|
*/ |
257
|
64 |
|
protected function parseAndValidate( Options $options ) { |
258
|
64 |
|
$parser = $this->getValueParser( $options ); |
259
|
|
|
|
260
|
64 |
|
if ( $this->definition->isList() ) { |
261
|
|
|
$values = []; |
262
|
|
|
|
263
|
|
|
foreach ( $this->getValue() as $value ) { |
264
|
|
|
$parsedValue = $this->parseAndValidateValue( $parser, $value ); |
265
|
|
|
|
266
|
|
|
if ( is_array( $parsedValue ) ) { |
267
|
|
|
$values[] = $parsedValue[0]; |
268
|
|
|
} |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
$this->value = $values; |
272
|
|
|
} |
273
|
|
|
else { |
274
|
64 |
|
$parsedValue = $this->parseAndValidateValue( $parser, $this->getValue() ); |
275
|
|
|
|
276
|
64 |
|
if ( is_array( $parsedValue ) ) { |
277
|
64 |
|
$this->value = $parsedValue[0]; |
278
|
|
|
} |
279
|
|
|
} |
280
|
|
|
|
281
|
64 |
|
$this->setToDefaultIfNeeded(); |
282
|
64 |
|
} |
283
|
|
|
|
284
|
|
|
/** |
285
|
|
|
* Parses and validates the provided with with specified parser. |
286
|
|
|
* The result is returned in an array on success. On fail, false is returned. |
287
|
|
|
* The result is wrapped in an array since we need to be able to distinguish |
288
|
|
|
* between the method returning false and the value being false. |
289
|
|
|
* |
290
|
|
|
* Parsing and validation errors get added to $this->errors. |
291
|
|
|
* |
292
|
|
|
* @since 1.0 |
293
|
|
|
* |
294
|
|
|
* @param ValueParser $parser |
295
|
|
|
* @param mixed $value |
296
|
|
|
* |
297
|
|
|
* @return array|bool |
298
|
|
|
*/ |
299
|
64 |
|
protected function parseAndValidateValue( ValueParser $parser, $value ) { |
300
|
|
|
try { |
301
|
64 |
|
$value = $parser->parse( $value ); |
302
|
|
|
} |
303
|
7 |
|
catch ( ParseException $parseException ) { |
304
|
7 |
|
$this->registerProcessingError( $parseException->getMessage() ); |
305
|
7 |
|
return false; |
306
|
|
|
} |
307
|
|
|
|
308
|
64 |
|
if ( $value instanceof \DataValues\DataValue ) { |
309
|
64 |
|
$value = $value->getValue(); |
310
|
|
|
} |
311
|
|
|
|
312
|
64 |
|
$this->validateValue( $value ); |
313
|
|
|
|
314
|
64 |
|
return [ $value ]; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* @since 1.0 |
319
|
|
|
* |
320
|
|
|
* @param string $message |
321
|
|
|
*/ |
322
|
21 |
|
protected function registerProcessingError( $message ) { |
323
|
21 |
|
$this->errors[] = $this->newProcessingError( $message ); |
324
|
21 |
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* @since 1.0 |
328
|
|
|
* |
329
|
|
|
* @param string $message |
330
|
|
|
* |
331
|
|
|
* @return ProcessingError |
332
|
|
|
*/ |
333
|
21 |
|
protected function newProcessingError( $message ) { |
334
|
21 |
|
$severity = $this->isRequired() ? ProcessingError::SEVERITY_FATAL : ProcessingError::SEVERITY_NORMAL; |
335
|
21 |
|
return new ProcessingError( $message, $severity ); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
/** |
339
|
|
|
* @since 1.0 |
340
|
|
|
* |
341
|
|
|
* @param mixed $value |
342
|
|
|
*/ |
343
|
64 |
|
protected function validateValue( $value ) { |
344
|
64 |
|
$validationCallback = $this->definition->getValidationCallback(); |
345
|
|
|
|
346
|
64 |
|
if ( $validationCallback !== null && $validationCallback( $value ) !== true ) { |
347
|
7 |
|
$this->registerProcessingError( 'Validation callback failed' ); |
348
|
|
|
} |
349
|
|
|
else { |
350
|
64 |
|
$validator = $this->definition->getValueValidator(); |
351
|
64 |
|
if ( method_exists( $validator, 'setOptions' ) ) { |
352
|
64 |
|
$validator->setOptions( $this->definition->getOptions() ); |
353
|
|
|
} |
354
|
64 |
|
$validationResult = $validator->validate( $value ); |
355
|
|
|
|
356
|
64 |
|
if ( !$validationResult->isValid() ) { |
357
|
16 |
|
foreach ( $validationResult->getErrors() as $error ) { |
358
|
16 |
|
$this->registerProcessingError( $error->getText() ); |
359
|
|
|
} |
360
|
|
|
} |
361
|
|
|
} |
362
|
64 |
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Sets the parameter value to the default if needed. |
366
|
|
|
* |
367
|
|
|
* @since 1.0 |
368
|
|
|
*/ |
369
|
64 |
|
protected function setToDefaultIfNeeded() { |
370
|
64 |
|
if ( $this->shouldSetToDefault() ) { |
371
|
1 |
|
$this->setToDefault(); |
372
|
|
|
} |
373
|
64 |
|
} |
374
|
|
|
|
375
|
64 |
|
private function shouldSetToDefault(): bool { |
376
|
64 |
|
if ( $this->hasFatalError() ) { |
377
|
20 |
|
return false; |
378
|
|
|
} |
379
|
|
|
|
380
|
56 |
|
if ( $this->definition->isList() ) { |
381
|
|
|
return count( $this->errors ) >= count( $this->value ); |
382
|
|
|
} |
383
|
|
|
|
384
|
56 |
|
return $this->errors !== []; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* Returns the original use-provided name. |
389
|
|
|
* |
390
|
|
|
* @since 1.0 |
391
|
|
|
* |
392
|
|
|
* @throws Exception |
393
|
|
|
* @return string |
394
|
|
|
*/ |
395
|
|
|
public function getOriginalName() { |
396
|
|
|
if ( $this->setCount == 0 ) { |
397
|
|
|
throw new Exception( 'No user input set to the parameter yet, so the original name does not exist' ); |
398
|
|
|
} |
399
|
|
|
return $this->originalName; |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Returns the original use-provided value. |
404
|
|
|
* |
405
|
|
|
* @since 1.0 |
406
|
|
|
* |
407
|
|
|
* @throws Exception |
408
|
|
|
* @return string |
409
|
|
|
*/ |
410
|
|
|
public function getOriginalValue() { |
411
|
|
|
if ( $this->setCount == 0 ) { |
412
|
|
|
throw new Exception( 'No user input set to the parameter yet, so the original value does not exist' ); |
413
|
|
|
} |
414
|
|
|
return $this->originalValue; |
415
|
|
|
} |
416
|
|
|
|
417
|
|
|
/** |
418
|
|
|
* Returns all validation errors that occurred so far. |
419
|
|
|
* |
420
|
|
|
* @since 1.0 |
421
|
|
|
* |
422
|
|
|
* @return ProcessingError[] |
423
|
|
|
*/ |
424
|
50 |
|
public function getErrors() { |
425
|
50 |
|
return $this->errors; |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
/** |
429
|
|
|
* Sets the parameter value to the default. |
430
|
|
|
* |
431
|
|
|
* @since 1.0 |
432
|
|
|
*/ |
433
|
2 |
|
protected function setToDefault() { |
434
|
2 |
|
$this->defaulted = true; |
435
|
2 |
|
$this->value = $this->definition->getDefault(); |
436
|
2 |
|
} |
437
|
|
|
|
438
|
|
|
/** |
439
|
|
|
* Gets if the parameter was set to it's default. |
440
|
|
|
* |
441
|
|
|
* @since 1.0 |
442
|
|
|
* |
443
|
|
|
* @return boolean |
444
|
|
|
*/ |
445
|
|
|
public function wasSetToDefault() { |
446
|
|
|
return $this->defaulted; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* @return boolean |
451
|
|
|
*/ |
452
|
65 |
|
public function hasFatalError() { |
453
|
65 |
|
foreach ( $this->errors as $error ) { |
454
|
21 |
|
if ( $error->isFatal() ) { |
455
|
21 |
|
return true; |
456
|
|
|
} |
457
|
|
|
} |
458
|
|
|
|
459
|
57 |
|
return false; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Returns the IParamDefinition this IParam was constructed from. |
464
|
|
|
* |
465
|
|
|
* @since 1.0 |
466
|
|
|
* |
467
|
|
|
* @return IParamDefinition |
468
|
|
|
*/ |
469
|
64 |
|
public function getDefinition() { |
470
|
64 |
|
return $this->definition; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Returns the parameters value. |
475
|
|
|
* |
476
|
|
|
* @since 1.0 |
477
|
|
|
* |
478
|
|
|
* @return mixed |
479
|
|
|
*/ |
480
|
65 |
|
public function &getValue() { |
481
|
65 |
|
return $this->value; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Returns if the parameter is required or not. |
486
|
|
|
* |
487
|
|
|
* @since 1.0 |
488
|
|
|
* |
489
|
|
|
* @return boolean |
490
|
|
|
*/ |
491
|
21 |
|
public function isRequired() { |
492
|
21 |
|
return $this->definition->isRequired(); |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
/** |
496
|
|
|
* Returns if the name of the parameter. |
497
|
|
|
* |
498
|
|
|
* @since 1.0 |
499
|
|
|
* |
500
|
|
|
* @return boolean |
501
|
|
|
*/ |
502
|
|
|
public function getName() { |
503
|
|
|
return $this->definition->getName(); |
504
|
|
|
} |
505
|
|
|
|
506
|
|
|
/** |
507
|
|
|
* Returns the parameter name aliases. |
508
|
|
|
* |
509
|
|
|
* @since 1.0 |
510
|
|
|
* |
511
|
|
|
* @return string[] |
512
|
|
|
*/ |
513
|
|
|
public function getAliases() { |
514
|
|
|
return $this->definition->getAliases(); |
515
|
|
|
} |
516
|
|
|
|
517
|
|
|
} |
This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.