Completed
Push — master ( b5c2b5...842562 )
by Jeroen De
02:32
created

src/ParamDefinition.php (5 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace ParamProcessor;
4
5
use Exception;
6
7
use ValueParsers\ValueParser;
8
use ValueParsers\NullParser;
9
10
use ValueValidators\ValueValidator;
11
use ValueValidators\NullValidator;
12
13
/**
14
 * Parameter definition.
15
 * Specifies what kind of values are accepted, how they should be validated,
16
 * how they should be formatted, what their dependencies are and how they should be described.
17
 *
18
 * Try to avoid using this interface outside of ParamProcessor for anything else then defining parameters.
19
 * In particular, do not derive from this class to implement methods such as formatValue.
20
 *
21
 * @since 1.0
22
 *
23
 * @licence GNU GPL v2+
24
 * @author Jeroen De Dauw < [email protected] >
25
 */
26
class ParamDefinition implements IParamDefinition {
27
28
	/**
29
	 * Indicates whether parameters that are provided more then once  should be accepted,
30
	 * and use the first provided value, or not, and generate an error.
31
	 *
32
	 * @since 1.0
33
	 *
34
	 * @var boolean
35
	 */
36
	public static $acceptOverriding = false;
37
38
	/**
39
	 * Indicates whether parameters not found in the criteria list
40
	 * should be stored in case they are not accepted. The default is false.
41
	 *
42
	 * @since 1.0
43
	 *
44
	 * @var boolean
45
	 */
46
	public static $accumulateParameterErrors = false;
47
48
	/**
49
	 * Indicates if the parameter value should trimmed during the clean process.
50
	 *
51
	 * @since 1.0
52
	 *
53
	 * @var boolean|null
54
	 */
55
	protected $trimValue = null;
56
57
	/**
58
	 * Indicates if the parameter manipulations should be applied to the default value.
59
	 *
60
	 * @since 1.0
61
	 *
62
	 * @var boolean
63
	 */
64
	protected $applyManipulationsToDefault = true;
65
66
	/**
67
	 * Dependency list containing parameters that need to be handled before this one.
68
	 *
69
	 * @since 1.0
70
	 *
71
	 * @var array
72
	 */
73
	protected $dependencies = array();
74
75
	/**
76
	 * The default value for the parameter, or null when the parameter is required.
77
	 *
78
	 * @since 1.0
79
	 *
80
	 * @var mixed
81
	 */
82
	protected $default;
83
84
	/**
85
	 * The main name of the parameter.
86
	 *
87
	 * @since 1.0
88
	 *
89
	 * @var string
90
	 */
91
	protected $name;
92
93
	/**
94
	 * @since 1.0
95
	 * @var boolean
96
	 */
97
	protected $isList;
98
99
	/**
100
	 * @since 1.0
101
	 * @var string
102
	 */
103
	protected $delimiter = ',';
104
105
	/**
106
	 * List of aliases for the parameter name.
107
	 *
108
	 * @since 1.0
109
	 *
110
	 * @var array
111
	 */
112
	protected $aliases = array();
113
114
	/**
115
	 * A message that acts as description for the parameter or false when there is none.
116
	 * Can be obtained via getMessage and set via setMessage.
117
	 *
118
	 * @since 1.0
119
	 *
120
	 * @var string
121
	 */
122
	protected $message = 'validator-message-nodesc';
123
124
	/**
125
	 * Original array definition of the parameter
126
	 *
127
	 * @since 1.0
128
	 *
129
	 * @var array
130
	 */
131
	protected $options = array();
132
133
	/**
134
	 * @since 1.0
135
	 *
136
	 * @var ValueParser|null
137
	 */
138
	protected $parser = null;
139
140
	/**
141
	 * @since 1.0
142
	 *
143
	 * @var ValueValidator|null
144
	 */
145
	protected $validator = null;
146
147
	/**
148
	 * @since 0.1
149
	 *
150
	 * @var callable|null
151
	 */
152
	protected $validationFunction = null;
153
154
	/**
155
	 * @since 0.1
156
	 *
157
	 * @var string
158
	 */
159
	protected $type;
160
161
	/**
162
	 * Constructor.
163
	 *
164
	 * @since 1.0
165
	 *
166
	 * @param string $type
167
	 * @param string $name
168
	 * @param mixed $default Use null for no default (which makes the parameter required)
169
	 * @param string $message
170
	 * @param boolean $isList
171
	 */
172
	public function __construct( $type, $name, $default = null, $message = null, $isList = false ) {
173
		$this->type = $type;
174
		$this->name = $name;
175
		$this->default = $default;
176
		$this->message = $message;
177
		$this->isList = $isList;
178
179
		$this->postConstruct();
180
	}
181
182
	/**
183
	 * Allows deriving classed to do additional stuff on instance construction
184
	 * without having to get and pass all the constructor arguments.
185
	 *
186
	 * @since 1.0
187
	 */
188
	protected function postConstruct() {
189
190
	}
191
192
	/**
193
	 * @see IParamDefinition::trimDuringClean
194
	 *
195
	 * @since 1.0
196
	 *
197
	 * @return boolean|null
198
	 */
199
	public function trimDuringClean() {
200
		return $this->trimValue;
201
	}
202
203
	/**
204
	 * @see IParamDefinition::getAliases
205
	 *
206
	 * @since 1.0
207
	 *
208
	 * @return array
209
	 */
210
	public function getAliases() {
211
		return $this->aliases;
212
	}
213
214
	/**
215
	 * @see IParamDefinition::hasAlias
216
	 *
217
	 * @since 1.0
218
	 *
219
	 * @param string $alias
220
	 *
221
	 * @return boolean
222
	 */
223
	public function hasAlias( $alias ) {
224
		return in_array( $alias, $this->getAliases() );
225
	}
226
227
	/**
228
	 * @see IParamDefinition::hasDependency
229
	 *
230
	 * @since 1.0
231
	 *
232
	 * @param string $dependency
233
	 *
234
	 * @return boolean
235
	 */
236
	public function hasDependency( $dependency ) {
237
		return in_array( $dependency, $this->getDependencies() );
238
	}
239
240
	/**
241
	 * Returns the list of allowed values, or false if there is no such restriction.
242
	 *
243
	 * @since 1.0
244
	 *
245
	 * @return array|boolean false
246
	 */
247
	public function getAllowedValues() {
248
		$allowedValues = array();
249
250
		if ( $this->validator !== null && method_exists( $this->validator, 'getWhitelistedValues' ) ) {
251
			// TODO: properly implement this
252
			$this->validator->setOptions( $this->options );
253
254
			$allowedValues = $this->validator->getWhitelistedValues();
255
256
			if ( $allowedValues === false ) {
257
				$allowedValues = array();
258
			}
259
		}
260
261
		return $allowedValues;
262
	}
263
264
	/**
265
	 * @see IParamDefinition::setDefault
266
	 *
267
	 * @since 1.0
268
	 *
269
	 * @param mixed $default
270
	 * @param boolean $manipulate Should the default be manipulated or not? Since 0.4.6.
271
	 */
272
	public function setDefault( $default, $manipulate = true ) {
273
		$this->default = $default;
274
		$this->setDoManipulationOfDefault( $manipulate );
275
	}
276
277
	/**
278
	 * @see IParamDefinition::getDefault
279
	 *
280
	 * @since 1.0
281
	 *
282
	 * @return mixed
283
	 */
284
	public function getDefault() {
285
		return $this->default;
286
	}
287
288
	/**
289
	 * @see IParamDefinition::getMessage
290
	 *
291
	 * @since 1.0
292
	 *
293
	 * @return string
294
	 */
295
	public function getMessage() {
296
		return $this->message;
297
	}
298
299
	/**
300
	 * @see IParamDefinition::setMessage
301
	 *
302
	 * @since 1.0
303
	 *
304
	 * @param string $message
305
	 */
306
	public function setMessage( $message ) {
307
		$this->message = $message;
308
	}
309
310
	/**
311
	 * @see IParamDefinition::setDoManipulationOfDefault
312
	 *
313
	 * @since 1.0
314
	 *
315
	 * @param boolean $doOrDoNotThereIsNoTry
316
	 */
317
	public function setDoManipulationOfDefault( $doOrDoNotThereIsNoTry ) {
318
		$this->applyManipulationsToDefault = $doOrDoNotThereIsNoTry;
319
	}
320
321
	/**
322
	 * @see IParamDefinition::shouldManipulateDefault
323
	 *
324
	 * @since 1.0
325
	 *
326
	 * @return boolean
327
	 */
328
	public function shouldManipulateDefault() {
329
		return $this->applyManipulationsToDefault;
330
	}
331
332
	/**
333
	 * @see IParamDefinition::addAliases
334
	 *
335
	 * @since 1.0
336
	 *
337
	 * @param mixed $aliases string or array of string
338
	 */
339
	public function addAliases( $aliases ) {
340
		$args = func_get_args();
341
		$this->aliases = array_merge( $this->aliases, is_array( $args[0] ) ? $args[0] : $args );
342
	}
343
344
	/**
345
	 * @see IParamDefinition::addDependencies
346
	 *
347
	 * @since 1.0
348
	 *
349
	 * @param mixed $dependencies string or array of string
350
	 */
351
	public function addDependencies( $dependencies ) {
352
		$args = func_get_args();
353
		$this->dependencies = array_merge( $this->dependencies, is_array( $args[0] ) ? $args[0] : $args );
354
	}
355
356
	/**
357
	 * @see IParamDefinition::getName
358
	 *
359
	 * @since 1.0
360
	 *
361
	 * @return string
362
	 */
363
	public function getName() {
364
		return $this->name;
365
	}
366
367
	/**
368
	 * Returns a message key for a message describing the parameter type.
369
	 *
370
	 * @since 1.0
371
	 *
372
	 * @return string
373
	 */
374
	public function getTypeMessage() {
375
		$message = 'validator-type-' . $this->getType();
376
377
		if ( $this->isList() ) {
378
			$message .= '-list';
379
		}
380
381
		return $message;
382
	}
383
384
	/**
385
	 * @see IParamDefinition::getDependencies
386
	 *
387
	 * @since 1.0
388
	 *
389
	 * @return array
390
	 */
391
	public function getDependencies() {
392
		return $this->dependencies;
393
	}
394
395
	/**
396
	 * @see IParamDefinition::isRequired
397
	 *
398
	 * @since 1.0
399
	 *
400
	 * @return boolean
401
	 */
402
	public function isRequired() {
403
		return is_null( $this->default );
404
	}
405
406
	/**
407
	 * @see IParamDefinition::isList
408
	 *
409
	 * @since 1.0
410
	 *
411
	 * @return boolean
412
	 */
413
	public function isList() {
414
		return $this->isList;
415
	}
416
417
	/**
418
	 * @see IParamDefinition::getDelimiter
419
	 *
420
	 * @since 1.0
421
	 *
422
	 * @return string
423
	 */
424
	public function getDelimiter() {
425
		return $this->delimiter;
426
	}
427
428
	/**
429
	 * @see IParamDefinition::setDelimiter
430
	 *
431
	 * @since 1.0
432
	 *
433
	 * @param $delimiter string
434
	 */
435
	public function setDelimiter( $delimiter ) {
436
		$this->delimiter = $delimiter;
437
	}
438
439
	/**
440
	 * @see IParamDefinition::setArrayValues
441
	 *
442
	 * @since 1.0
443
	 *
444
	 * @param array $param
445
	 */
446
	public function setArrayValues( array $param ) {
447
		if ( array_key_exists( 'aliases', $param ) ) {
448
			$this->addAliases( $param['aliases'] );
449
		}
450
451
		if ( array_key_exists( 'dependencies', $param ) ) {
452
			$this->addDependencies( $param['dependencies'] );
453
		}
454
455
		if ( array_key_exists( 'trim', $param ) ) {
456
			$this->trimValue = $param['trim'];
457
		}
458
459
		if ( array_key_exists( 'delimiter', $param ) ) {
460
			$this->delimiter = $param['delimiter'];
461
		}
462
463
		if ( array_key_exists( 'manipulatedefault', $param ) ) {
464
			$this->setDoManipulationOfDefault( $param['manipulatedefault'] );
465
		}
466
467
		$this->options = $param;
468
	}
469
470
	/**
471
	 * @see IParamDefinition::validate
472
	 *
473
	 * @since 1.0
474
	 * @deprecated
475
	 *
476
	 * @param $param IParam
477
	 * @param $definitions array of IParamDefinition
478
	 * @param $params array of IParam
479
	 * @param Options $options
480
	 *
481
	 * @return array|true
482
	 *
483
	 * TODO: return error list (ie Status object)
484
	 */
485
	public function validate( IParam $param, array $definitions, array $params, Options $options ) {
486
		if ( $this->isList() ) {
487
			$valid = true;
488
			$values = $param->getValue();
489
490
			foreach ( $values as $value ) {
491
				// TODO: restore not bailing out at one error in list but filtering on valid
492
				$valid = $this->validateValue( $value, $param, $definitions, $params, $options );
0 ignored issues
show
The method validateValue() does not exist on ParamProcessor\ParamDefinition. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
493
494
				if ( !$valid ) {
495
					break;
496
				}
497
			}
498
499
			return $valid && $this->validateList( $param, $definitions, $params, $options );
0 ignored issues
show
The method validateList() does not exist on ParamProcessor\ParamDefinition. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
Bug Best Practice introduced by
The return type of return $valid && $this->...ns, $params, $options); (boolean) is incompatible with the return type declared by the interface ParamProcessor\IParamDefinition::validate of type array|ParamProcessor\true.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
500
		}
501
		else {
502
			$valid = $this->validateValue( $param->getValue(), $param, $definitions, $params, $options );
0 ignored issues
show
The method validateValue() does not exist on ParamProcessor\ParamDefinition. Did you maybe mean validate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
503
504
			return $valid ? true : array( new ProcessingError( 'Error' ) ); // TODO
0 ignored issues
show
Bug Compatibility introduced by
The expression $valid ? true : array(ne...cessingError('Error')); of type boolean|ParamProcessor\ProcessingError[] adds the type boolean to the return on line 504 which is incompatible with the return type declared by the interface ParamProcessor\IParamDefinition::validate of type array|ParamProcessor\true.
Loading history...
505
		}
506
	}
507
508
	/**
509
	 * @see IParamDefinition::format
510
	 *
511
	 * @since 1.0
512
	 * @deprecated
513
	 *
514
	 * @param IParam $param
515
	 * @param IParamDefinition[] $definitions
516
	 * @param IParam[] $params
517
	 */
518
	public function format( IParam $param, array &$definitions, array $params ) {
519
		if ( $this->isList() ) {
520
			$values = $param->getValue();
521
522
			foreach ( $values as &$value ) {
523
				$value = $this->formatValue( $value, $param, $definitions, $params );
524
			}
525
526
			$param->setValue( $values );
527
			$this->formatList( $param, $definitions, $params );
528
		}
529
		else {
530
			$param->setValue( $this->formatValue( $param->getValue(), $param, $definitions, $params ) );
531
		}
532
533
		// deprecated, deriving classes should not add array-definitions to the list
534
		$definitions = self::getCleanDefinitions( $definitions );
535
536
		if ( array_key_exists( 'post-format', $this->options ) ) {
537
			$param->setValue( call_user_func( $this->options['post-format'], $param->getValue() ) );
538
		}
539
	}
540
541
	/**
542
	 * Formats the parameters values to their final result.
543
	 *
544
	 * @since 1.0
545
	 * @deprecated
546
	 *
547
	 * @param $param IParam
548
	 * @param $definitions array of IParamDefinition
549
	 * @param $params array of IParam
550
	 */
551
	protected function formatList( IParam $param, array &$definitions, array $params ) {
552
		// TODO
553
	}
554
555
	/**
556
	 * Formats the parameter value to it's final result.
557
	 *
558
	 * @since 1.0
559
	 * @deprecated
560
	 *
561
	 * @param mixed $value
562
	 * @param IParam $param
563
	 * @param IParamDefinition[] $definitions
564
	 * @param IParam[] $params
565
	 *
566
	 * @return mixed
567
	 */
568
	protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
569
		return $value;
570
		// No-op
571
	}
572
573
	/**
574
	 * Returns a cleaned version of the list of parameter definitions.
575
	 * This includes having converted all supported definition types to
576
	 * ParamDefinition classes and having all keys set to the names of the
577
	 * corresponding parameters.
578
	 *
579
	 * @since 1.0
580
	 *
581
	 * @param IParamDefinition[] $definitions
582
	 *
583
	 * @return IParamDefinition[]
584
	 * @throws Exception
585
	 */
586
	public static function getCleanDefinitions( array $definitions ) {
587
		$cleanList = array();
588
589
		foreach ( $definitions as $key => $definition ) {
590
			if ( is_array( $definition ) ) {
591
				if ( !array_key_exists( 'name', $definition ) && is_string( $key ) ) {
592
					$definition['name'] = $key;
593
				}
594
595
				$definition = ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition );
596
			}
597
598
			if ( !( $definition instanceof IParamDefinition ) ) {
599
				throw new Exception( '$definition not an instance of IParamDefinition' );
600
			}
601
602
			$cleanList[$definition->getName()] = $definition;
603
		}
604
605
		return $cleanList;
606
	}
607
608
	/**
609
	 * @see IParamDefinition::getType
610
	 *
611
	 * @since 1.0
612
	 *
613
	 * @return string
614
	 */
615
	public function getType() {
616
		return $this->type;
617
	}
618
619
	/**
620
	 * @see IParamDefinition::getValueParser
621
	 *
622
	 * @since 1.0
623
	 *
624
	 * @return ValueParser
625
	 */
626
	public function getValueParser() {
627
		if ( $this->parser === null ) {
628
			$this->parser = new NullParser();
629
		}
630
631
		return $this->parser;
632
	}
633
634
	/**
635
	 * @see IParamDefinition::getValueValidator
636
	 *
637
	 * @since 1.0
638
	 *
639
	 * @return ValueValidator
640
	 */
641
	public function getValueValidator() {
642
		if ( $this->validator === null ) {
643
			$this->validator = new NullValidator();
644
		}
645
646
		return $this->validator;
647
	}
648
649
	/**
650
	 * @see IParamDefinition::setValueParser
651
	 *
652
	 * @since 1.0
653
	 *
654
	 * @param ValueParser $parser
655
	 */
656
	public function setValueParser( ValueParser $parser ) {
657
		$this->parser = $parser;
658
	}
659
660
	/**
661
	 * @see IParamDefinition::setValueValidator
662
	 *
663
	 * @since 1.0
664
	 *
665
	 * @param ValueValidator $validator
666
	 */
667
	public function setValueValidator( ValueValidator $validator ) {
668
		$this->validator = $validator;
669
	}
670
671
	/**
672
	 * @see IParamDefinition::setValidationCallback
673
	 *
674
	 * @since 1.0
675
	 *
676
	 * @param callable $validationFunction
677
	 */
678
	public function setValidationCallback( /* callable */ $validationFunction ) {
679
		$this->validationFunction = $validationFunction;
680
	}
681
682
	/**
683
	 * @see IParamDefinition::getValidationCallback
684
	 *
685
	 * @since 1.0
686
	 *
687
	 * @return callable|null
688
	 */
689
	public function getValidationCallback() {
690
		return $this->validationFunction;
691
	}
692
693
	/**
694
	 * @since 0.1
695
	 *
696
	 * @return array
697
	 */
698
	public function getOptions() {
699
		return $this->options;
700
	}
701
702
}
703