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