Passed
Push — master ( 2628a9...59e798 )
by Jeroen De
02:20
created

ParamDefinition::getAllowedValues()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 0
cts 9
cp 0
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 9
nc 5
nop 0
crap 30
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 {
0 ignored issues
show
Deprecated Code introduced by jeroendedauw
The interface ParamProcessor\IParamDefinition has been deprecated with message: since 1.0, use ParamDefinition

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.

Loading history...
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 string[]
72
	 */
73
	protected $dependencies = [];
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 string[]
111
	 */
112
	protected $aliases = [];
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 = [];
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 36
	public function __construct( $type, $name, $default = null, $message = null, $isList = false ) {
173 36
		$this->type = $type;
174 36
		$this->name = $name;
175 36
		$this->default = $default;
176 36
		$this->message = $message;
177 36
		$this->isList = $isList;
178
179 36
		$this->postConstruct();
180 36
	}
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 36
	protected function postConstruct() {
189
190 36
	}
191
192
	/**
193
	 * @see IParamDefinition::trimDuringClean
194
	 *
195
	 * @since 1.0
196
	 *
197
	 * @return boolean|null
198
	 */
199 64
	public function trimDuringClean() {
200 64
		return $this->trimValue;
201
	}
202
203
	/**
204
	 * @see IParamDefinition::getAliases
205
	 *
206
	 * @since 1.0
207
	 *
208
	 * @return string[]
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 an empty array if there is no such restriction.
242
	 *
243
	 * @since 1.0
244
	 *
245
	 * @return array
246
	 */
247
	public function getAllowedValues() {
248
		$allowedValues = [];
249
250
		if ( $this->validator !== null && method_exists( $this->validator, 'getWhitelistedValues' ) ) {
251
			if ( method_exists( $this->validator, 'setOptions' ) ) {
252
				$this->validator->setOptions( $this->options );
253
			}
254
255
			$allowedValues = $this->validator->getWhitelistedValues();
0 ignored issues
show
Bug introduced by jeroendedauw
It seems like you code against a concrete implementation and not the interface ValueValidators\ValueValidator as the method getWhitelistedValues() does only exist in the following implementations of said interface: ValueValidators\DimensionValidator, ValueValidators\ListValidator, ValueValidators\RangeValidator, ValueValidators\StringValidator, ValueValidators\TitleValidator, ValueValidators\ValueValidatorObject.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
256
257
			if ( $allowedValues === false ) {
258
				$allowedValues = [];
259
			}
260
		}
261
262
		return $allowedValues;
263
	}
264
265
	/**
266
	 * @see IParamDefinition::setDefault
267
	 *
268
	 * @since 1.0
269
	 *
270
	 * @param mixed $default
271
	 * @param boolean $manipulate Should the default be manipulated or not? Since 0.4.6.
272
	 */
273
	public function setDefault( $default, $manipulate = true ) {
274
		$this->default = $default;
275
		$this->setDoManipulationOfDefault( $manipulate );
276
	}
277
278
	/**
279
	 * @see IParamDefinition::getDefault
280
	 *
281
	 * @since 1.0
282
	 *
283
	 * @return mixed
284
	 */
285 1
	public function getDefault() {
286 1
		return $this->default;
287
	}
288
289
	/**
290
	 * @see IParamDefinition::getMessage
291
	 *
292
	 * @since 1.0
293
	 *
294
	 * @return string
295
	 */
296
	public function getMessage() {
297
		return $this->message;
298
	}
299
300
	/**
301
	 * @see IParamDefinition::setMessage
302
	 *
303
	 * @since 1.0
304
	 *
305
	 * @param string $message
306
	 */
307
	public function setMessage( $message ) {
308
		$this->message = $message;
309
	}
310
311
	/**
312
	 * @see IParamDefinition::setDoManipulationOfDefault
313
	 *
314
	 * @since 1.0
315
	 *
316
	 * @param boolean $doOrDoNotThereIsNoTry
317
	 */
318
	public function setDoManipulationOfDefault( $doOrDoNotThereIsNoTry ) {
319
		$this->applyManipulationsToDefault = $doOrDoNotThereIsNoTry;
320
	}
321
322
	/**
323
	 * @see IParamDefinition::shouldManipulateDefault
324
	 *
325
	 * @since 1.0
326
	 *
327
	 * @return boolean
328
	 */
329 56
	public function shouldManipulateDefault() {
330 56
		return $this->applyManipulationsToDefault;
331
	}
332
333
	/**
334
	 * @see IParamDefinition::addAliases
335
	 *
336
	 * @since 1.0
337
	 *
338
	 * @param string|string[] $aliases
339
	 */
340
	public function addAliases( $aliases ) {
341
		$args = func_get_args();
342
		$this->aliases = array_merge( $this->aliases, is_array( $args[0] ) ? $args[0] : $args );
0 ignored issues
show
Documentation Bug introduced by Jeroen De Dauw
It seems like array_merge($this->alias...0]) ? $args[0] : $args) of type array is incompatible with the declared type array<integer,string> of property $aliases.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
343
	}
344
345
	/**
346
	 * @see IParamDefinition::addDependencies
347
	 *
348
	 * @since 1.0
349
	 *
350
	 * @param string|string[] $dependencies
351
	 */
352
	public function addDependencies( $dependencies ) {
353
		$args = func_get_args();
354
		$this->dependencies = array_merge( $this->dependencies, is_array( $args[0] ) ? $args[0] : $args );
0 ignored issues
show
Documentation Bug introduced by Jeroen De Dauw
It seems like array_merge($this->depen...0]) ? $args[0] : $args) of type array is incompatible with the declared type array<integer,string> of property $dependencies.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
355
	}
356
357
	/**
358
	 * @see IParamDefinition::getName
359
	 *
360
	 * @since 1.0
361
	 *
362
	 * @return string
363
	 */
364 68
	public function getName() {
365 68
		return $this->name;
366
	}
367
368
	/**
369
	 * Returns a message key for a message describing the parameter type.
370
	 *
371
	 * @since 1.0
372
	 *
373
	 * @return string
374
	 */
375
	public function getTypeMessage() {
376
		$message = 'validator-type-' . $this->getType();
377
378
		if ( $this->isList() ) {
379
			$message .= '-list';
380
		}
381
382
		return $message;
383
	}
384
385
	/**
386
	 * @see IParamDefinition::getDependencies
387
	 *
388
	 * @since 1.0
389
	 *
390
	 * @return string[]
391
	 */
392
	public function getDependencies() {
393
		return $this->dependencies;
394
	}
395
396
	/**
397
	 * @see IParamDefinition::isRequired
398
	 *
399
	 * @since 1.0
400
	 *
401
	 * @return boolean
402
	 */
403 21
	public function isRequired() {
404 21
		return is_null( $this->default );
405
	}
406
407
	/**
408
	 * @see IParamDefinition::isList
409
	 *
410
	 * @since 1.0
411
	 *
412
	 * @return boolean
413
	 */
414 64
	public function isList() {
415 64
		return $this->isList;
416
	}
417
418
	/**
419
	 * @see IParamDefinition::getDelimiter
420
	 *
421
	 * @since 1.0
422
	 *
423
	 * @return string
424
	 */
425
	public function getDelimiter() {
426
		return $this->delimiter;
427
	}
428
429
	/**
430
	 * @see IParamDefinition::setDelimiter
431
	 *
432
	 * @since 1.0
433
	 *
434
	 * @param $delimiter string
435
	 */
436
	public function setDelimiter( $delimiter ) {
437
		$this->delimiter = $delimiter;
438
	}
439
440
	/**
441
	 * @see IParamDefinition::setArrayValues
442
	 *
443
	 * @since 1.0
444
	 *
445
	 * @param array $param
446
	 */
447 36
	public function setArrayValues( array $param ) {
448 36
		if ( array_key_exists( 'aliases', $param ) ) {
449
			$this->addAliases( $param['aliases'] );
450
		}
451
452 36
		if ( array_key_exists( 'dependencies', $param ) ) {
453
			$this->addDependencies( $param['dependencies'] );
454
		}
455
456 36
		if ( array_key_exists( 'trim', $param ) ) {
457
			$this->trimValue = $param['trim'];
458
		}
459
460 36
		if ( array_key_exists( 'delimiter', $param ) ) {
461
			$this->delimiter = $param['delimiter'];
462
		}
463
464 36
		if ( array_key_exists( 'manipulatedefault', $param ) ) {
465
			$this->setDoManipulationOfDefault( $param['manipulatedefault'] );
466
		}
467
468 36
		$this->options = $param;
469 36
	}
470
471
	/**
472
	 * @see IParamDefinition::format
473
	 *
474
	 * @since 1.0
475
	 * @deprecated
476
	 *
477
	 * @param IParam $param
478
	 * @param IParamDefinition[] $definitions
479
	 * @param IParam[] $params
480
	 */
481 56
	public function format( IParam $param, array &$definitions, array $params ) {
482 56
		if ( $this->isList() && is_array( $param->getValue() ) ) {
483
			// TODO: if isList returns true, the value should be an array.
484
			// The second check here is to avoid a mysterious error.
485
			// Should have logging that writes down the value whenever this occurs.
486
487
			$values = $param->getValue();
488
489
			foreach ( $values as &$value ) {
490
				$value = $this->formatValue( $value, $param, $definitions, $params );
0 ignored issues
show
Deprecated Code introduced by jeroendedauw
The method ParamProcessor\ParamDefinition::formatValue() has been deprecated.

This method has been deprecated.

Loading history...
491
			}
492
493
			$param->setValue( $values );
494
			$this->formatList( $param, $definitions, $params );
0 ignored issues
show
Deprecated Code introduced by jeroendedauw
The method ParamProcessor\ParamDefinition::formatList() has been deprecated.

This method has been deprecated.

Loading history...
495
		}
496
		else {
497 56
			$param->setValue( $this->formatValue( $param->getValue(), $param, $definitions, $params ) );
0 ignored issues
show
Deprecated Code introduced by jeroendedauw
The method ParamProcessor\ParamDefinition::formatValue() has been deprecated.

This method has been deprecated.

Loading history...
498
		}
499
500
		// deprecated, deriving classes should not add array-definitions to the list
501 56
		$definitions = self::getCleanDefinitions( $definitions );
502
503 56
		if ( array_key_exists( 'post-format', $this->options ) ) {
504
			$param->setValue( call_user_func( $this->options['post-format'], $param->getValue() ) );
505
		}
506 56
	}
507
508
	/**
509
	 * Formats the parameters values to their final result.
510
	 *
511
	 * @since 1.0
512
	 * @deprecated
513
	 *
514
	 * @param $param IParam
515
	 * @param $definitions array of IParamDefinition
516
	 * @param $params array of IParam
517
	 */
518
	protected function formatList( IParam $param, array &$definitions, array $params ) {
0 ignored issues
show
Unused Code introduced by jeroendedauw
The parameter $param is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by jeroendedauw
The parameter $definitions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by jeroendedauw
The parameter $params is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
519
		// TODO
520
	}
521
522
	/**
523
	 * Formats the parameter value to it's final result.
524
	 *
525
	 * @since 1.0
526
	 * @deprecated
527
	 *
528
	 * @param mixed $value
529
	 * @param IParam $param
530
	 * @param IParamDefinition[] $definitions
531
	 * @param IParam[] $params
532
	 *
533
	 * @return mixed
534
	 */
535 42
	protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
0 ignored issues
show
Unused Code introduced by jeroendedauw
The parameter $param is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by jeroendedauw
The parameter $definitions is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by jeroendedauw
The parameter $params is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
536 42
		return $value;
537
		// No-op
538
	}
539
540
	/**
541
	 * Returns a cleaned version of the list of parameter definitions.
542
	 * This includes having converted all supported definition types to
543
	 * ParamDefinition classes and having all keys set to the names of the
544
	 * corresponding parameters.
545
	 *
546
	 * @since 1.0
547
	 *
548
	 * @param IParamDefinition[] $definitions
549
	 *
550
	 * @return IParamDefinition[]
551
	 * @throws Exception
552
	 */
553 56
	public static function getCleanDefinitions( array $definitions ) {
554 56
		$cleanList = [];
555
556 56
		foreach ( $definitions as $key => $definition ) {
557
			if ( is_array( $definition ) ) {
558
				if ( !array_key_exists( 'name', $definition ) && is_string( $key ) ) {
559
					$definition['name'] = $key;
560
				}
561
562
				$definition = ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition );
0 ignored issues
show
Deprecated Code introduced by jeroendedauw
The method ParamProcessor\ParamDefinitionFactory::singleton() has been deprecated with message: since 1.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
563
			}
564
565
			if ( !( $definition instanceof IParamDefinition ) ) {
566
				throw new Exception( '$definition not an instance of IParamDefinition' );
567
			}
568
569
			$cleanList[$definition->getName()] = $definition;
570
		}
571
572 56
		return $cleanList;
573
	}
574
575
	/**
576
	 * @see IParamDefinition::getType
577
	 *
578
	 * @since 1.0
579
	 *
580
	 * @return string
581
	 */
582 80
	public function getType() {
583 80
		return $this->type;
584
	}
585
586
	/**
587
	 * @see IParamDefinition::getValueParser
588
	 *
589
	 * @since 1.0
590
	 *
591
	 * @return ValueParser
592
	 */
593 64
	public function getValueParser() {
594 64
		if ( $this->parser === null ) {
595 64
			$this->parser = new NullParser();
596
		}
597
598 64
		return $this->parser;
599
	}
600
601
	/**
602
	 * @see IParamDefinition::getValueValidator
603
	 *
604
	 * @since 1.0
605
	 *
606
	 * @return ValueValidator
607
	 */
608 64
	public function getValueValidator() {
609 64
		if ( $this->validator === null ) {
610
			$this->validator = new NullValidator();
611
		}
612
613 64
		return $this->validator;
614
	}
615
616
	/**
617
	 * @see IParamDefinition::setValueParser
618
	 *
619
	 * @since 1.0
620
	 *
621
	 * @param ValueParser $parser
622
	 */
623
	public function setValueParser( ValueParser $parser ) {
624
		$this->parser = $parser;
625
	}
626
627
	/**
628
	 * @see IParamDefinition::setValueValidator
629
	 *
630
	 * @since 1.0
631
	 *
632
	 * @param ValueValidator $validator
633
	 */
634 36
	public function setValueValidator( ValueValidator $validator ) {
635 36
		$this->validator = $validator;
636 36
	}
637
638
	/**
639
	 * @see IParamDefinition::setValidationCallback
640
	 *
641
	 * @since 1.0
642
	 *
643
	 * @param callable $validationFunction
644
	 */
645 36
	public function setValidationCallback( /* callable */ $validationFunction ) {
646 36
		$this->validationFunction = $validationFunction;
647 36
	}
648
649
	/**
650
	 * @see IParamDefinition::getValidationCallback
651
	 *
652
	 * @since 1.0
653
	 *
654
	 * @return callable|null
655
	 */
656 64
	public function getValidationCallback() {
657 64
		return $this->validationFunction;
658
	}
659
660
	/**
661
	 * @since 0.1
662
	 *
663
	 * @return array
664
	 */
665 64
	public function getOptions() {
666 64
		return $this->options;
667
	}
668
669
}
670