Completed
Push — master ( cb0578...28965e )
by Jeroen De
07:11 queued 04:27
created

src/ParamDefinition.php (1 issue)

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 = [];
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 = [];
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 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 = [];
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();
0 ignored issues
show
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...
255
256
			if ( $allowedValues === false ) {
257
				$allowedValues = [];
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 1
	public function getDefault() {
285 1
		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 56
	public function shouldManipulateDefault() {
329 56
		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 68
	public function getName() {
364 68
		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 21
	public function isRequired() {
403 21
		return is_null( $this->default );
404
	}
405
406
	/**
407
	 * @see IParamDefinition::isList
408
	 *
409
	 * @since 1.0
410
	 *
411
	 * @return boolean
412
	 */
413 64
	public function isList() {
414 64
		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 36
	public function setArrayValues( array $param ) {
447 36
		if ( array_key_exists( 'aliases', $param ) ) {
448
			$this->addAliases( $param['aliases'] );
449
		}
450
451 36
		if ( array_key_exists( 'dependencies', $param ) ) {
452
			$this->addDependencies( $param['dependencies'] );
453
		}
454
455 36
		if ( array_key_exists( 'trim', $param ) ) {
456
			$this->trimValue = $param['trim'];
457
		}
458
459 36
		if ( array_key_exists( 'delimiter', $param ) ) {
460
			$this->delimiter = $param['delimiter'];
461
		}
462
463 36
		if ( array_key_exists( 'manipulatedefault', $param ) ) {
464
			$this->setDoManipulationOfDefault( $param['manipulatedefault'] );
465
		}
466
467 36
		$this->options = $param;
468 36
	}
469
470
	/**
471
	 * @see IParamDefinition::format
472
	 *
473
	 * @since 1.0
474
	 * @deprecated
475
	 *
476
	 * @param IParam $param
477
	 * @param IParamDefinition[] $definitions
478
	 * @param IParam[] $params
479
	 */
480 56
	public function format( IParam $param, array &$definitions, array $params ) {
481 56
		if ( $this->isList() && is_array( $param->getValue() ) ) {
482
			// TODO: if isList returns true, the value should be an array.
483
			// The second check here is to avoid a mysterious error.
484
			// Should have logging that writes down the value whenever this occurs.
485
486
			$values = $param->getValue();
487
488
			foreach ( $values as &$value ) {
489
				$value = $this->formatValue( $value, $param, $definitions, $params );
490
			}
491
492
			$param->setValue( $values );
493
			$this->formatList( $param, $definitions, $params );
494
		}
495
		else {
496 56
			$param->setValue( $this->formatValue( $param->getValue(), $param, $definitions, $params ) );
497
		}
498
499
		// deprecated, deriving classes should not add array-definitions to the list
500 56
		$definitions = self::getCleanDefinitions( $definitions );
501
502 56
		if ( array_key_exists( 'post-format', $this->options ) ) {
503
			$param->setValue( call_user_func( $this->options['post-format'], $param->getValue() ) );
504
		}
505 56
	}
506
507
	/**
508
	 * Formats the parameters values to their final result.
509
	 *
510
	 * @since 1.0
511
	 * @deprecated
512
	 *
513
	 * @param $param IParam
514
	 * @param $definitions array of IParamDefinition
515
	 * @param $params array of IParam
516
	 */
517
	protected function formatList( IParam $param, array &$definitions, array $params ) {
518
		// TODO
519
	}
520
521
	/**
522
	 * Formats the parameter value to it's final result.
523
	 *
524
	 * @since 1.0
525
	 * @deprecated
526
	 *
527
	 * @param mixed $value
528
	 * @param IParam $param
529
	 * @param IParamDefinition[] $definitions
530
	 * @param IParam[] $params
531
	 *
532
	 * @return mixed
533
	 */
534 42
	protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
535 42
		return $value;
536
		// No-op
537
	}
538
539
	/**
540
	 * Returns a cleaned version of the list of parameter definitions.
541
	 * This includes having converted all supported definition types to
542
	 * ParamDefinition classes and having all keys set to the names of the
543
	 * corresponding parameters.
544
	 *
545
	 * @since 1.0
546
	 *
547
	 * @param IParamDefinition[] $definitions
548
	 *
549
	 * @return IParamDefinition[]
550
	 * @throws Exception
551
	 */
552 56
	public static function getCleanDefinitions( array $definitions ) {
553 56
		$cleanList = [];
554
555 56
		foreach ( $definitions as $key => $definition ) {
556
			if ( is_array( $definition ) ) {
557
				if ( !array_key_exists( 'name', $definition ) && is_string( $key ) ) {
558
					$definition['name'] = $key;
559
				}
560
561
				$definition = ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition );
562
			}
563
564
			if ( !( $definition instanceof IParamDefinition ) ) {
565
				throw new Exception( '$definition not an instance of IParamDefinition' );
566
			}
567
568
			$cleanList[$definition->getName()] = $definition;
569
		}
570
571 56
		return $cleanList;
572
	}
573
574
	/**
575
	 * @see IParamDefinition::getType
576
	 *
577
	 * @since 1.0
578
	 *
579
	 * @return string
580
	 */
581 80
	public function getType() {
582 80
		return $this->type;
583
	}
584
585
	/**
586
	 * @see IParamDefinition::getValueParser
587
	 *
588
	 * @since 1.0
589
	 *
590
	 * @return ValueParser
591
	 */
592 64
	public function getValueParser() {
593 64
		if ( $this->parser === null ) {
594 64
			$this->parser = new NullParser();
595
		}
596
597 64
		return $this->parser;
598
	}
599
600
	/**
601
	 * @see IParamDefinition::getValueValidator
602
	 *
603
	 * @since 1.0
604
	 *
605
	 * @return ValueValidator
606
	 */
607 64
	public function getValueValidator() {
608 64
		if ( $this->validator === null ) {
609
			$this->validator = new NullValidator();
610
		}
611
612 64
		return $this->validator;
613
	}
614
615
	/**
616
	 * @see IParamDefinition::setValueParser
617
	 *
618
	 * @since 1.0
619
	 *
620
	 * @param ValueParser $parser
621
	 */
622
	public function setValueParser( ValueParser $parser ) {
623
		$this->parser = $parser;
624
	}
625
626
	/**
627
	 * @see IParamDefinition::setValueValidator
628
	 *
629
	 * @since 1.0
630
	 *
631
	 * @param ValueValidator $validator
632
	 */
633 36
	public function setValueValidator( ValueValidator $validator ) {
634 36
		$this->validator = $validator;
635 36
	}
636
637
	/**
638
	 * @see IParamDefinition::setValidationCallback
639
	 *
640
	 * @since 1.0
641
	 *
642
	 * @param callable $validationFunction
643
	 */
644 36
	public function setValidationCallback( /* callable */ $validationFunction ) {
645 36
		$this->validationFunction = $validationFunction;
646 36
	}
647
648
	/**
649
	 * @see IParamDefinition::getValidationCallback
650
	 *
651
	 * @since 1.0
652
	 *
653
	 * @return callable|null
654
	 */
655 64
	public function getValidationCallback() {
656 64
		return $this->validationFunction;
657
	}
658
659
	/**
660
	 * @since 0.1
661
	 *
662
	 * @return array
663
	 */
664 64
	public function getOptions() {
665 64
		return $this->options;
666
	}
667
668
}
669