Completed
Push — master ( ebda25...4c5acd )
by Jeroen De
01:42
created

ParamDefinition::getAliases()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace ParamProcessor;
4
5
use Exception;
6
7
use ValueParsers\ValueParser;
8
use ValueParsers\NullParser;
9
use ValueValidators\ValueValidator;
10
use ValueValidators\NullValidator;
11
12
/**
13
 * Parameter definition.
14
 * Specifies what kind of values are accepted, how they should be validated,
15
 * how they should be formatted, what their dependencies are and how they should be described.
16
 *
17
 * Try to avoid using this interface outside of ParamProcessor for anything else then defining parameters.
18
 * In particular, do not derive from this class to implement methods such as formatValue.
19
 *
20
 * @since 1.0
21
 *
22
 * @licence GNU GPL v2+
23
 * @author Jeroen De Dauw < [email protected] >
24
 */
25
class ParamDefinition implements IParamDefinition {
0 ignored issues
show
Deprecated Code introduced by
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...
26
27
	/**
28
	 * Indicates whether parameters that are provided more then once  should be accepted,
29
	 * and use the first provided value, or not, and generate an error.
30
	 *
31
	 * @since 1.0
32
	 *
33
	 * @var boolean
34
	 */
35
	public static $acceptOverriding = false;
36
37
	/**
38
	 * Indicates whether parameters not found in the criteria list
39
	 * should be stored in case they are not accepted. The default is false.
40
	 *
41
	 * @since 1.0
42
	 *
43
	 * @var boolean
44
	 */
45
	public static $accumulateParameterErrors = false;
46
47
	protected $type;
48
	protected $name;
49
	protected $default;
50
	protected $isList;
51
52
	/**
53
	 * A message that acts as description for the parameter or false when there is none.
54
	 * Can be obtained via getMessage and set via setMessage.
55
	 *
56
	 * @var string
57
	 */
58
	protected $message = 'validator-message-nodesc';
59
60
	/**
61
	 * Indicates if the parameter value should trimmed during the clean process.
62
	 *
63
	 * @since 1.0
64
	 *
65
	 * @var boolean|null
66
	 */
67
	protected $trimValue = null;
68
69
	/**
70
	 * Indicates if the parameter manipulations should be applied to the default value.
71
	 *
72
	 * @since 1.0
73
	 *
74
	 * @var boolean
75
	 */
76
	protected $applyManipulationsToDefault = true;
77
78
	/**
79
	 * Dependency list containing parameters that need to be handled before this one.
80
	 *
81
	 * @since 1.0
82
	 *
83
	 * @var string[]
84
	 */
85
	protected $dependencies = [];
86
87
	/**
88
	 * @var string
89
	 */
90
	protected $delimiter = ',';
91
92
	/**
93
	 * List of aliases for the parameter name.
94
	 *
95
	 * @var string[]
96
	 */
97
	protected $aliases = [];
98
99
	/**
100
	 * Original array definition of the parameter
101
	 * @var array
102
	 */
103
	protected $options = [];
104
105
	/**
106
	 * @var ValueParser|null
107
	 */
108
	protected $parser = null;
109
110
	/**
111
	 * @var ValueValidator|null
112
	 */
113
	protected $validator = null;
114
115
	/**
116
	 * @var callable|null
117
	 */
118
	protected $validationFunction = null;
119
120
	/**
121
	 * @param string $type
122
	 * @param string $name
123
	 * @param mixed $default Use null for no default (which makes the parameter required)
124
	 * @param string $message
125
	 * @param boolean $isList
126
	 */
127 36
	public function __construct( string $type, string $name, $default = null, string $message = null, bool $isList = false ) {
128 36
		$this->type = $type;
129 36
		$this->name = $name;
130 36
		$this->default = $default;
131 36
		$this->message = $message;
132 36
		$this->isList = $isList;
133
134 36
		$this->postConstruct();
135 36
	}
136
137
	/**
138
	 * Allows deriving classed to do additional stuff on instance construction
139
	 * without having to get and pass all the constructor arguments.
140
	 *
141
	 * @since 1.0
142
	 */
143 36
	protected function postConstruct() {
144
145 36
	}
146
147
	/**
148
	 * Returns if the value should be trimmed before validation and any further processing.
149
	 * @return boolean|null
150
	 */
151 64
	public function trimDuringClean() {
152 64
		return $this->trimValue;
153
	}
154
155
	/**
156
	 * Returns the parameter name aliases.
157
	 * @return string[]
158
	 */
159
	public function getAliases(): array {
160
		return $this->aliases;
161
	}
162
163
	public function hasAlias( string $alias ): bool {
164
		return in_array( $alias, $this->getAliases() );
165
	}
166
167
	/**
168
	 * @see IParamDefinition::hasDependency
169
	 *
170
	 * @since 1.0
171
	 *
172
	 * @param string $dependency
173
	 *
174
	 * @return boolean
175
	 */
176
	public function hasDependency( string $dependency ): bool {
177
		return in_array( $dependency, $this->getDependencies() );
178
	}
179
180
	/**
181
	 * Returns the list of allowed values, or an empty array if there is no such restriction.
182
	 *
183
	 * @since 1.0
184
	 *
185
	 * @return array
186
	 */
187
	public function getAllowedValues() {
188
		$allowedValues = [];
189
190
		if ( $this->validator !== null && method_exists( $this->validator, 'getWhitelistedValues' ) ) {
191
			if ( method_exists( $this->validator, 'setOptions' ) ) {
192
				$this->validator->setOptions( $this->options );
193
			}
194
195
			$allowedValues = $this->validator->getWhitelistedValues();
0 ignored issues
show
Bug introduced by
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...
196
197
			if ( $allowedValues === false ) {
198
				$allowedValues = [];
199
			}
200
		}
201
202
		return $allowedValues;
203
	}
204
205
	/**
206
	 * @see IParamDefinition::setDefault
207
	 *
208
	 * @since 1.0
209
	 *
210
	 * @param mixed $default
211
	 * @param boolean $manipulate Should the default be manipulated or not? Since 0.4.6.
212
	 */
213
	public function setDefault( $default, $manipulate = true ) {
214
		$this->default = $default;
215
		$this->setDoManipulationOfDefault( $manipulate );
216
	}
217
218
	/**
219
	 * @see IParamDefinition::getDefault
220
	 *
221
	 * @since 1.0
222
	 *
223
	 * @return mixed
224
	 */
225 1
	public function getDefault() {
226 1
		return $this->default;
227
	}
228
229
	/**
230
	 * Returns a message describing the parameter.
231
	 */
232
	public function getMessage(): string {
233
		return $this->message;
234
	}
235
236
	/**
237
	 * This should be a message key, ie something that can be passed
238
	 * to wfMsg. Not an actual text.
239
	 */
240
	public function setMessage( string $message ) {
241
		$this->message = $message;
242
	}
243
244
	/**
245
	 * Set if the parameter manipulations should be applied to the default value.
246
	 */
247
	public function setDoManipulationOfDefault( bool $doOrDoNotThereIsNoTry ) {
248
		$this->applyManipulationsToDefault = $doOrDoNotThereIsNoTry;
249
	}
250
251 56
	public function shouldManipulateDefault(): bool {
252 56
		return $this->applyManipulationsToDefault;
253
	}
254
255
	/**
256
	 * Adds one or more aliases for the parameter name.
257
	 *
258
	 * @param string|string[] $aliases
259
	 */
260
	public function addAliases( $aliases ) {
261
		$args = func_get_args();
262
		$this->aliases = array_merge( $this->aliases, is_array( $args[0] ) ? $args[0] : $args );
0 ignored issues
show
Documentation Bug introduced by
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...
263
	}
264
265
	/**
266
	 * Adds one or more dependencies. There are the names of parameters
267
	 * that need to be validated and formatted before this one.
268
	 *
269
	 * @param string|string[] $dependencies
270
	 */
271
	public function addDependencies( $dependencies ) {
272
		$args = func_get_args();
273
		$this->dependencies = array_merge( $this->dependencies, is_array( $args[0] ) ? $args[0] : $args );
0 ignored issues
show
Documentation Bug introduced by
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...
274
	}
275
276 68
	public function getName(): string {
277 68
		return $this->name;
278
	}
279
280
	/**
281
	 * Returns a message key for a message describing the parameter type.
282
	 */
283
	public function getTypeMessage(): string {
284
		$message = 'validator-type-' . $this->getType();
285
286
		if ( $this->isList() ) {
287
			$message .= '-list';
288
		}
289
290
		return $message;
291
	}
292
293
	/**
294
	 * Returns a list of dependencies the parameter has, in the form of
295
	 * other parameter names.
296
	 *
297
	 * @return string[]
298
	 */
299
	public function getDependencies(): array {
300
		return $this->dependencies;
301
	}
302
303 21
	public function isRequired(): bool {
304 21
		return is_null( $this->default );
305
	}
306
307 64
	public function isList(): bool {
308 64
		return $this->isList;
309
	}
310
311
	/**
312
	 * Returns the delimiter to use to split the raw value in case the
313
	 * parameter is a list.
314
	 */
315
	public function getDelimiter(): string {
316
		return $this->delimiter;
317
	}
318
319
	/**
320
	 * Sets the delimiter to use to split the raw value in case the
321
	 * parameter is a list.
322
	 *
323
	 * @param $delimiter string
324
	 */
325
	public function setDelimiter( $delimiter ) {
326
		$this->delimiter = $delimiter;
327
	}
328
329
	/**
330
	 * Sets the parameter definition values contained in the provided array.
331
	 *
332
	 * @param array $param
333
	 */
334 36
	public function setArrayValues( array $param ) {
335 36
		if ( array_key_exists( 'aliases', $param ) ) {
336
			$this->addAliases( $param['aliases'] );
337
		}
338
339 36
		if ( array_key_exists( 'dependencies', $param ) ) {
340
			$this->addDependencies( $param['dependencies'] );
341
		}
342
343 36
		if ( array_key_exists( 'trim', $param ) ) {
344
			$this->trimValue = $param['trim'];
345
		}
346
347 36
		if ( array_key_exists( 'delimiter', $param ) ) {
348
			$this->delimiter = $param['delimiter'];
349
		}
350
351 36
		if ( array_key_exists( 'manipulatedefault', $param ) ) {
352
			$this->setDoManipulationOfDefault( $param['manipulatedefault'] );
353
		}
354
355 36
		$this->options = $param;
356 36
	}
357
358
	/**
359
	 * @see IParamDefinition::format
360
	 *
361
	 * @since 1.0
362
	 * @deprecated
363
	 *
364
	 * @param IParam $param
365
	 * @param IParamDefinition[] $definitions
366
	 * @param IParam[] $params
367
	 */
368 56
	public function format( IParam $param, array &$definitions, array $params ) {
369
		/**
370
		 * @var Param $param
371
		 */
372
373 56
		if ( $this->isList() && is_array( $param->getValue() ) ) {
374
			// TODO: if isList returns true, the value should be an array.
375
			// The second check here is to avoid a mysterious error.
376
			// Should have logging that writes down the value whenever this occurs.
377
378
			$values = $param->getValue();
379
380
			foreach ( $values as &$value ) {
381
				$value = $this->formatValue( $value, $param, $definitions, $params );
0 ignored issues
show
Deprecated Code introduced by
The method ParamProcessor\ParamDefinition::formatValue() has been deprecated.

This method has been deprecated.

Loading history...
382
			}
383
384
			$param->setValue( $values );
385
			$this->formatList( $param, $definitions, $params );
0 ignored issues
show
Deprecated Code introduced by
The method ParamProcessor\ParamDefinition::formatList() has been deprecated.

This method has been deprecated.

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

This method has been deprecated.

Loading history...
389
		}
390
391
		// deprecated, deriving classes should not add array-definitions to the list
392 56
		$definitions = self::getCleanDefinitions( $definitions );
393
394 56
		if ( array_key_exists( 'post-format', $this->options ) ) {
395
			$param->setValue( call_user_func( $this->options['post-format'], $param->getValue() ) );
396
		}
397 56
	}
398
399
	/**
400
	 * Formats the parameters values to their final result.
401
	 *
402
	 * @since 1.0
403
	 * @deprecated
404
	 *
405
	 * @param $param IParam
406
	 * @param $definitions array of IParamDefinition
407
	 * @param $params array of IParam
408
	 */
409
	protected function formatList( IParam $param, array &$definitions, array $params ) {
0 ignored issues
show
Unused Code introduced by
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
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
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...
410
	}
411
412
	/**
413
	 * Formats the parameter value to it's final result.
414
	 *
415
	 * @since 1.0
416
	 * @deprecated
417
	 *
418
	 * @param mixed $value
419
	 * @param IParam $param
420
	 * @param IParamDefinition[] $definitions
421
	 * @param IParam[] $params
422
	 *
423
	 * @return mixed
424
	 */
425 42
	protected function formatValue( $value, IParam $param, array &$definitions, array $params ) {
0 ignored issues
show
Unused Code introduced by
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
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
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...
426 42
		return $value;
427
	}
428
429
	/**
430
	 * Returns a cleaned version of the list of parameter definitions.
431
	 * This includes having converted all supported definition types to
432
	 * ParamDefinition classes and having all keys set to the names of the
433
	 * corresponding parameters.
434
	 *
435
	 * @since 1.0
436
	 *
437
	 * @param ParamDefinition[] $definitions
438
	 *
439
	 * @return ParamDefinition[]
440
	 * @throws Exception
441
	 */
442 56
	public static function getCleanDefinitions( array $definitions ): array {
443 56
		$cleanList = [];
444
445 56
		foreach ( $definitions as $key => $definition ) {
446
			if ( is_array( $definition ) ) {
447
				if ( !array_key_exists( 'name', $definition ) && is_string( $key ) ) {
448
					$definition['name'] = $key;
449
				}
450
451
				$definition = ParamDefinitionFactory::singleton()->newDefinitionFromArray( $definition );
0 ignored issues
show
Deprecated Code introduced by
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...
452
			}
453
454
			if ( !( $definition instanceof IParamDefinition ) ) {
455
				throw new Exception( '$definition not an instance of IParamDefinition' );
456
			}
457
458
			$cleanList[$definition->getName()] = $definition;
459
		}
460
461 56
		return $cleanList;
462
	}
463
464
	/**
465
	 * Returns an identifier for the type of the parameter.
466
	 */
467 80
	public function getType(): string {
468 80
		return $this->type;
469
	}
470
471
	/**
472
	 * Returns a ValueParser object to parse the parameters value.
473
	 */
474 64
	public function getValueParser(): ValueParser {
475 64
		if ( $this->parser === null ) {
476 64
			$this->parser = new NullParser();
477
		}
478
479 64
		return $this->parser;
480
	}
481
482
	/**
483
	 * Returns a ValueValidator that can be used to validate the parameters value.
484
	 */
485 64
	public function getValueValidator(): ValueValidator {
486 64
		if ( $this->validator === null ) {
487 2
			$this->validator = new NullValidator();
488
		}
489
490 64
		return $this->validator;
491
	}
492
493
	public function setValueParser( ValueParser $parser ) {
494
		$this->parser = $parser;
495
	}
496
497 36
	public function setValueValidator( ValueValidator $validator ) {
498 36
		$this->validator = $validator;
499 36
	}
500
501
	/**
502
	 * Sets a validation function that will be run before the ValueValidator.
503
	 *
504
	 * This can be used instead of a ValueValidator where validation is very
505
	 * trivial, ie checking if something is a boolean can be done with is_bool.
506
	 */
507 36
	public function setValidationCallback( ?callable $validationFunction ) {
508 36
		$this->validationFunction = $validationFunction;
509 36
	}
510
511
	/**
512
	 * @see setValidationCallback
513
	 */
514 64
	public function getValidationCallback(): ?callable {
515 64
		return $this->validationFunction;
516
	}
517
518 64
	public function getOptions(): array {
519 64
		return $this->options;
520
	}
521
522
}
523