Completed
Push — master ( a0c2f2...3a847e )
by
unknown
02:48 queued 10s
created

parseSeparatorsParameter()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 3
nop 1
1
<?php
2
3
namespace WikibaseQuality\ConstraintReport\ConstraintCheck\Helper;
4
5
use Config;
6
use DataValues\DataValue;
7
use DataValues\MonolingualTextValue;
8
use DataValues\MultilingualTextValue;
9
use DataValues\StringValue;
10
use DataValues\UnboundedQuantityValue;
11
use LogicException;
12
use Wikibase\DataModel\DeserializerFactory;
13
use Wikibase\DataModel\Deserializers\SnakDeserializer;
14
use Wikibase\DataModel\Entity\EntityId;
15
use Wikibase\DataModel\Entity\EntityIdValue;
16
use Wikibase\DataModel\Entity\ItemId;
17
use Wikibase\DataModel\Entity\PropertyId;
18
use Wikibase\DataModel\Snak\PropertyNoValueSnak;
19
use Wikibase\DataModel\Snak\PropertySomeValueSnak;
20
use Wikibase\DataModel\Snak\PropertyValueSnak;
21
use Wikibase\DataModel\Snak\Snak;
22
use WikibaseQuality\ConstraintReport\ConstraintCheck\Context\Context;
23
use WikibaseQuality\ConstraintReport\ConstraintCheck\ItemIdSnakValue;
24
use WikibaseQuality\ConstraintReport\ConstraintCheck\Message\ViolationMessage;
25
use WikibaseQuality\ConstraintReport\ConstraintParameterRenderer;
26
use WikibaseQuality\ConstraintReport\Role;
27
28
/**
29
 * Helper for parsing constraint parameters
30
 * that were imported from constraint statements.
31
 *
32
 * All public methods of this class expect constraint parameters
33
 * (see {@link \WikibaseQuality\Constraint::getConstraintParameters()})
34
 * and return parameter objects or throw {@link ConstraintParameterException}s.
35
 * The results are used by the checkers,
36
 * which may include rendering them into violation messages.
37
 *
38
 * @author Lucas Werkmeister
39
 * @license GPL-2.0-or-later
40
 */
41
class ConstraintParameterParser {
42
43
	/**
44
	 * @var Config
45
	 */
46
	private $config;
47
48
	/**
49
	 * @var SnakDeserializer
50
	 */
51
	private $snakDeserializer;
52
53
	/**
54
	 * @var ConstraintParameterRenderer
55
	 */
56
	private $constraintParameterRenderer;
57
58
	/**
59
	 * @var string[]
60
	 */
61
	private $conceptBaseUris;
62
63
	/**
64
	 * @param Config $config
65
	 *   contains entity IDs used in constraint parameters (constraint statement qualifiers)
66
	 * @param DeserializerFactory $factory
67
	 *   used to parse constraint statement qualifiers into constraint parameters
68
	 * @param ConstraintParameterRenderer $constraintParameterRenderer
69
	 *   used to render incorrect parameters for error messages
70
	 * @param string[] $conceptBaseUris
71
	 *   mapping from repository names to base URIs of concept URIs,
72
	 *   used to obtain the full unit string from an entity ID given in constraint parameters
73
	 */
74
	public function __construct(
75
		Config $config,
76
		DeserializerFactory $factory,
77
		ConstraintParameterRenderer $constraintParameterRenderer,
78
		array $conceptBaseUris
79
	) {
80
		$this->config = $config;
81
		$this->snakDeserializer = $factory->newSnakDeserializer();
82
		$this->constraintParameterRenderer = $constraintParameterRenderer;
83
		$this->conceptBaseUris = $conceptBaseUris;
84
	}
85
86
	/**
87
	 * Check if any errors are recorded in the constraint parameters.
88
	 * @param array $parameters
89
	 * @throws ConstraintParameterException
90
	 */
91
	public function checkError( array $parameters ) {
92
		if ( array_key_exists( '@error', $parameters ) ) {
93
			$error = $parameters['@error'];
94
			if ( array_key_exists( 'toolong', $error ) && $error['toolong'] ) {
95
				$msg = 'wbqc-violation-message-parameters-error-toolong';
96
			} else {
97
				$msg = 'wbqc-violation-message-parameters-error-unknown';
98
			}
99
			throw new ConstraintParameterException( new ViolationMessage( $msg ) );
100
		}
101
	}
102
103
	/**
104
	 * Require that $parameters contains exactly one $parameterId parameter.
105
	 * @param array $parameters
106
	 * @param string $parameterId
107
	 * @throws ConstraintParameterException
108
	 */
109 View Code Duplication
	private function requireSingleParameter( array $parameters, $parameterId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
110
		if ( count( $parameters[$parameterId] ) !== 1 ) {
111
			throw new ConstraintParameterException(
112
				( new ViolationMessage( 'wbqc-violation-message-parameter-single' ) )
113
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
114
			);
115
		}
116
	}
117
118
	/**
119
	 * Require that $snak is a {@link PropertyValueSnak}.
120
	 * @param Snak $snak
121
	 * @param string $parameterId
122
	 * @return void
123
	 * @throws ConstraintParameterException
124
	 */
125 View Code Duplication
	private function requireValueParameter( Snak $snak, $parameterId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
		if ( !( $snak instanceof PropertyValueSnak ) ) {
127
			throw new ConstraintParameterException(
128
				( new ViolationMessage( 'wbqc-violation-message-parameter-value' ) )
129
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
130
			);
131
		}
132
	}
133
134
	/**
135
	 * Parse a single entity ID parameter.
136
	 * @param array $snakSerialization
137
	 * @param string $parameterId
138
	 * @throws ConstraintParameterException
139
	 * @return EntityId
140
	 */
141 View Code Duplication
	private function parseEntityIdParameter( array $snakSerialization, $parameterId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
143
		$this->requireValueParameter( $snak, $parameterId );
144
		$value = $snak->getDataValue();
0 ignored issues
show
Bug introduced by
The method getDataValue does only exist in Wikibase\DataModel\Snak\PropertyValueSnak, but not in Wikibase\DataModel\Snak\...k\PropertySomeValueSnak.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
145
		if ( $value instanceof EntityIdValue ) {
146
			return $value->getEntityId();
147
		} else {
148
			throw new ConstraintParameterException(
149
				( new ViolationMessage( 'wbqc-violation-message-parameter-entity' ) )
150
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
151
					->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
152
			);
153
		}
154
	}
155
156
	/**
157
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
158
	 * @param string $constraintTypeItemId used in error messages
159
	 * @throws ConstraintParameterException if the parameter is invalid or missing
160
	 * @return string[] class entity ID serializations
161
	 */
162
	public function parseClassParameter( array $constraintParameters, $constraintTypeItemId ) {
163
		$this->checkError( $constraintParameters );
164
		$classId = $this->config->get( 'WBQualityConstraintsClassId' );
165
		if ( !array_key_exists( $classId, $constraintParameters ) ) {
166
			throw new ConstraintParameterException(
167
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
168
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
169
					->withEntityId( new PropertyId( $classId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
170
			);
171
		}
172
173
		$classes = [];
174
		foreach ( $constraintParameters[$classId] as $class ) {
175
			$classes[] = $this->parseEntityIdParameter( $class, $classId )->getSerialization();
176
		}
177
		return $classes;
178
	}
179
180
	/**
181
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
182
	 * @param string $constraintTypeItemId used in error messages
183
	 * @throws ConstraintParameterException if the parameter is invalid or missing
184
	 * @return string 'instance', 'subclass', or 'instanceOrSubclass'
185
	 */
186
	public function parseRelationParameter( array $constraintParameters, $constraintTypeItemId ) {
187
		$this->checkError( $constraintParameters );
188
		$relationId = $this->config->get( 'WBQualityConstraintsRelationId' );
189
		if ( !array_key_exists( $relationId, $constraintParameters ) ) {
190
			throw new ConstraintParameterException(
191
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
192
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
193
					->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
194
			);
195
		}
196
197
		$this->requireSingleParameter( $constraintParameters, $relationId );
198
		$relationEntityId = $this->parseEntityIdParameter( $constraintParameters[$relationId][0], $relationId );
199
		$instanceId = $this->config->get( 'WBQualityConstraintsInstanceOfRelationId' );
200
		$subclassId = $this->config->get( 'WBQualityConstraintsSubclassOfRelationId' );
201
		$instanceOrSubclassId = $this->config->get( 'WBQualityConstraintsInstanceOrSubclassOfRelationId' );
202
		switch ( $relationEntityId ) {
203
			case $instanceId:
204
				return 'instance';
205
			case $subclassId:
206
				return 'subclass';
207
			case $instanceOrSubclassId:
208
				return 'instanceOrSubclass';
209 View Code Duplication
			default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
				throw new ConstraintParameterException(
211
					( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
212
						->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
213
						->withEntityIdList(
214
							[
215
								new ItemId( $instanceId ),
216
								new ItemId( $subclassId ),
217
								new ItemId( $instanceOrSubclassId ),
218
							],
219
							Role::CONSTRAINT_PARAMETER_VALUE
220
						)
221
				);
222
		}
223
	}
224
225
	/**
226
	 * Parse a single property ID parameter.
227
	 * @param array $snakSerialization
228
	 * @param string $parameterId used in error messages
229
	 * @throws ConstraintParameterException
230
	 * @return PropertyId
231
	 */
232
	private function parsePropertyIdParameter( array $snakSerialization, $parameterId ) {
233
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
234
		$this->requireValueParameter( $snak, $parameterId );
235
		$value = $snak->getDataValue();
0 ignored issues
show
Bug introduced by
The method getDataValue does only exist in Wikibase\DataModel\Snak\PropertyValueSnak, but not in Wikibase\DataModel\Snak\...k\PropertySomeValueSnak.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
236
		if ( $value instanceof EntityIdValue ) {
237
			$id = $value->getEntityId();
238
			if ( $id instanceof PropertyId ) {
239
				return $id;
240
			}
241
		}
242
		throw new ConstraintParameterException(
243
			( new ViolationMessage( 'wbqc-violation-message-parameter-property' ) )
244
				->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
245
				->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
246
		);
247
	}
248
249
	/**
250
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
251
	 * @param string $constraintTypeItemId used in error messages
252
	 *
253
	 * @throws ConstraintParameterException if the parameter is invalid or missing
254
	 * @return PropertyId
255
	 */
256 View Code Duplication
	public function parsePropertyParameter( array $constraintParameters, $constraintTypeItemId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
		$this->checkError( $constraintParameters );
258
		$propertyId = $this->config->get( 'WBQualityConstraintsPropertyId' );
259
		if ( !array_key_exists( $propertyId, $constraintParameters ) ) {
260
			throw new ConstraintParameterException(
261
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
262
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
263
					->withEntityId( new PropertyId( $propertyId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
264
			);
265
		}
266
267
		$this->requireSingleParameter( $constraintParameters, $propertyId );
268
		return $this->parsePropertyIdParameter( $constraintParameters[$propertyId][0], $propertyId );
269
	}
270
271
	private function parseItemIdParameter( PropertyValueSnak $snak, $parameterId ) {
272
		$dataValue = $snak->getDataValue();
273
		if ( $dataValue instanceof EntityIdValue &&
274
			$dataValue->getEntityId() instanceof ItemId
275
		) {
276
			return ItemIdSnakValue::fromItemId( $dataValue->getEntityId() );
277
		} else {
278
			throw new ConstraintParameterException(
279
				( new ViolationMessage( 'wbqc-violation-message-parameter-item' ) )
280
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
281
					->withDataValue( $dataValue, Role::CONSTRAINT_PARAMETER_VALUE )
282
			);
283
		}
284
	}
285
286
	/**
287
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
288
	 * @param string $constraintTypeItemId used in error messages
289
	 * @param bool $required whether the parameter is required (error if absent) or not ([] if absent)
290
	 * @throws ConstraintParameterException if the parameter is invalid or missing
291
	 * @return ItemIdSnakValue[] array of values
292
	 */
293
	public function parseItemsParameter( array $constraintParameters, $constraintTypeItemId, $required ) {
294
		$this->checkError( $constraintParameters );
295
		$qualifierId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
296
		if ( !array_key_exists( $qualifierId, $constraintParameters ) ) {
297
			if ( $required ) {
298
				throw new ConstraintParameterException(
299
					( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
300
						->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
301
						->withEntityId( new PropertyId( $qualifierId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
302
				);
303
			} else {
304
				return [];
305
			}
306
		}
307
308
		$values = [];
309
		foreach ( $constraintParameters[$qualifierId] as $parameter ) {
310
			$snak = $this->snakDeserializer->deserialize( $parameter );
311
			switch ( true ) {
312
				case $snak instanceof PropertyValueSnak:
313
					$values[] = $this->parseItemIdParameter( $snak, $qualifierId );
314
					break;
315
				case $snak instanceof PropertySomeValueSnak:
316
					$values[] = ItemIdSnakValue::someValue();
317
					break;
318
				case $snak instanceof PropertyNoValueSnak:
319
					$values[] = ItemIdSnakValue::noValue();
320
					break;
321
			}
322
		}
323
		return $values;
324
	}
325
326
	/**
327
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
328
	 * @param string $constraintTypeItemId used in error messages
329
	 * @throws ConstraintParameterException if the parameter is invalid or missing
330
	 * @return PropertyId[]
331
	 */
332
	public function parsePropertiesParameter( array $constraintParameters, $constraintTypeItemId ) {
333
		$this->checkError( $constraintParameters );
334
		$propertyId = $this->config->get( 'WBQualityConstraintsPropertyId' );
335
		if ( !array_key_exists( $propertyId, $constraintParameters ) ) {
336
			throw new ConstraintParameterException(
337
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
338
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
339
					->withEntityId( new PropertyId( $propertyId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
340
			);
341
		}
342
343
		$parameters = $constraintParameters[$propertyId];
344
		if ( count( $parameters ) === 1 &&
345
			$this->snakDeserializer->deserialize( $parameters[0] ) instanceof PropertyNoValueSnak
346
		) {
347
			return [];
348
		}
349
350
		$properties = [];
351
		foreach ( $parameters as $parameter ) {
352
			$properties[] = $this->parsePropertyIdParameter( $parameter, $propertyId );
353
		}
354
		return $properties;
355
	}
356
357
	/**
358
	 * @param array $snakSerialization
359
	 * @param string $parameterId
360
	 * @throws ConstraintParameterException
361
	 * @return DataValue|null
362
	 */
363
	private function parseValueOrNoValueParameter( array $snakSerialization, $parameterId ) {
364
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
365
		if ( $snak instanceof PropertyValueSnak ) {
366
			return $snak->getDataValue();
367
		} elseif ( $snak instanceof PropertyNoValueSnak ) {
368
			return null;
369
		} else {
370
			throw new ConstraintParameterException(
371
				( new ViolationMessage( 'wbqc-violation-message-parameter-value-or-novalue' ) )
372
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
373
			);
374
		}
375
	}
376
377
	/**
378
	 * @param array $snakSerialization
379
	 * @param string $parameterId
380
	 * @return DataValue|null
381
	 */
382
	private function parseValueOrNoValueOrNowParameter( array $snakSerialization, $parameterId ) {
383
		try {
384
			return $this->parseValueOrNoValueParameter( $snakSerialization, $parameterId );
385
		} catch ( ConstraintParameterException $e ) {
386
			// unknown value means “now”
387
			return new NowValue();
388
		}
389
	}
390
391
	/**
392
	 * Checks whether there is exactly one non-null quantity with the given unit.
393
	 * @param DataValue|null $min
394
	 * @param DataValue|null $max
395
	 * @param string $unit
396
	 * @return bool
397
	 */
398
	private function exactlyOneQuantityWithUnit( DataValue $min = null, DataValue $max = null, $unit ) {
399
		if ( !( $min instanceof UnboundedQuantityValue ) ||
0 ignored issues
show
Bug introduced by
The class DataValues\UnboundedQuantityValue does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
400
			!( $max instanceof UnboundedQuantityValue )
0 ignored issues
show
Bug introduced by
The class DataValues\UnboundedQuantityValue does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
401
		) {
402
			return false;
403
		}
404
405
		return ( $min->getUnit() === $unit ) !== ( $max->getUnit() === $unit );
406
	}
407
408
	/**
409
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
410
	 * @param string $minimumId
411
	 * @param string $maximumId
412
	 * @param string $constraintTypeItemId used in error messages
413
	 * @param string $type 'quantity' or 'time' (can be data type or data value type)
414
	 *
415
	 * @throws ConstraintParameterException if the parameter is invalid or missing
416
	 * @return DataValue[] if the parameter is invalid or missing
417
	 */
418
	private function parseRangeParameter( array $constraintParameters, $minimumId, $maximumId, $constraintTypeItemId, $type ) {
419
		$this->checkError( $constraintParameters );
420
		if ( !array_key_exists( $minimumId, $constraintParameters ) ||
421
			!array_key_exists( $maximumId, $constraintParameters )
422
		) {
423
			throw new ConstraintParameterException(
424
				( new ViolationMessage( 'wbqc-violation-message-range-parameters-needed' ) )
425
					->withDataValueType( $type )
426
					->withEntityId( new PropertyId( $minimumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
427
					->withEntityId( new PropertyId( $maximumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
428
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
429
			);
430
		}
431
432
		$this->requireSingleParameter( $constraintParameters, $minimumId );
433
		$this->requireSingleParameter( $constraintParameters, $maximumId );
434
		$parseFunction = $type === 'time' ? 'parseValueOrNoValueOrNowParameter' : 'parseValueOrNoValueParameter';
435
		$min = $this->$parseFunction( $constraintParameters[$minimumId][0], $minimumId );
436
		$max = $this->$parseFunction( $constraintParameters[$maximumId][0], $maximumId );
437
438
		$yearUnit = $this->config->get( 'WBQualityConstraintsYearUnit' );
439
		if ( $this->exactlyOneQuantityWithUnit( $min, $max, $yearUnit ) ) {
440
			throw new ConstraintParameterException(
441
				new ViolationMessage( 'wbqc-violation-message-range-parameters-one-year' )
442
			);
443
		}
444
		if ( $min === null && $max === null ||
445
			$min !== null && $max !== null && $min->equals( $max ) ) {
446
			throw new ConstraintParameterException(
447
				( new ViolationMessage( 'wbqc-violation-message-range-parameters-same' ) )
448
					->withEntityId( new PropertyId( $minimumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
449
					->withEntityId( new PropertyId( $maximumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
450
			);
451
		}
452
453
		return [ $min, $max ];
454
	}
455
456
	/**
457
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
458
	 * @param string $constraintTypeItemId used in error messages
459
	 *
460
	 * @throws ConstraintParameterException if the parameter is invalid or missing
461
	 * @return DataValue[] a pair of two data values, either of which may be null to signify an open range
462
	 */
463
	public function parseQuantityRangeParameter( array $constraintParameters, $constraintTypeItemId ) {
464
		return $this->parseRangeParameter(
465
			$constraintParameters,
466
			$this->config->get( 'WBQualityConstraintsMinimumQuantityId' ),
467
			$this->config->get( 'WBQualityConstraintsMaximumQuantityId' ),
468
			$constraintTypeItemId,
469
			'quantity'
470
		);
471
	}
472
473
	/**
474
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
475
	 * @param string $constraintTypeItemId used in error messages
476
	 *
477
	 * @throws ConstraintParameterException if the parameter is invalid or missing
478
	 * @return DataValue[] a pair of two data values, either of which may be null to signify an open range
479
	 */
480
	public function parseTimeRangeParameter( array $constraintParameters, $constraintTypeItemId ) {
481
		return $this->parseRangeParameter(
482
			$constraintParameters,
483
			$this->config->get( 'WBQualityConstraintsMinimumDateId' ),
484
			$this->config->get( 'WBQualityConstraintsMaximumDateId' ),
485
			$constraintTypeItemId,
486
			'time'
487
		);
488
	}
489
490
	/**
491
	 * Parse a single string parameter.
492
	 * @param array $snakSerialization
493
	 * @param string $parameterId
494
	 * @throws ConstraintParameterException
495
	 * @return string
496
	 */
497 View Code Duplication
	private function parseStringParameter( array $snakSerialization, $parameterId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
498
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
499
		$this->requireValueParameter( $snak, $parameterId );
500
		$value = $snak->getDataValue();
0 ignored issues
show
Bug introduced by
The method getDataValue does only exist in Wikibase\DataModel\Snak\PropertyValueSnak, but not in Wikibase\DataModel\Snak\...k\PropertySomeValueSnak.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
501
		if ( $value instanceof StringValue ) {
502
			return $value->getValue();
503
		} else {
504
			throw new ConstraintParameterException(
505
				( new ViolationMessage( 'wbqc-violation-message-parameter-string' ) )
506
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
507
					->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
508
			);
509
		}
510
	}
511
512
	/**
513
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
514
	 * @param string $constraintTypeItemId used in error messages
515
	 * @throws ConstraintParameterException if the parameter is invalid or missing
516
	 * @return string
517
	 */
518
	public function parseNamespaceParameter( array $constraintParameters, $constraintTypeItemId ) {
0 ignored issues
show
Unused Code introduced by
The parameter $constraintTypeItemId 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
		$this->checkError( $constraintParameters );
520
		$namespaceId = $this->config->get( 'WBQualityConstraintsNamespaceId' );
521
		if ( !array_key_exists( $namespaceId, $constraintParameters ) ) {
522
			return '';
523
		}
524
525
		$this->requireSingleParameter( $constraintParameters, $namespaceId );
526
		return $this->parseStringParameter( $constraintParameters[$namespaceId][0], $namespaceId );
527
	}
528
529
	/**
530
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
531
	 * @param string $constraintTypeItemId used in error messages
532
	 * @throws ConstraintParameterException if the parameter is invalid or missing
533
	 * @return string
534
	 */
535 View Code Duplication
	public function parseFormatParameter( array $constraintParameters, $constraintTypeItemId ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
536
		$this->checkError( $constraintParameters );
537
		$formatId = $this->config->get( 'WBQualityConstraintsFormatAsARegularExpressionId' );
538
		if ( !array_key_exists( $formatId, $constraintParameters ) ) {
539
			throw new ConstraintParameterException(
540
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
541
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
542
					->withEntityId( new PropertyId( $formatId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
543
			);
544
		}
545
546
		$this->requireSingleParameter( $constraintParameters, $formatId );
547
		return $this->parseStringParameter( $constraintParameters[$formatId][0], $formatId );
548
	}
549
550
	/**
551
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
552
	 * @throws ConstraintParameterException if the parameter is invalid
553
	 * @return EntityId[]
554
	 */
555
	public function parseExceptionParameter( array $constraintParameters ) {
556
		$this->checkError( $constraintParameters );
557
		$exceptionId = $this->config->get( 'WBQualityConstraintsExceptionToConstraintId' );
558
		if ( !array_key_exists( $exceptionId, $constraintParameters ) ) {
559
			return [];
560
		}
561
562
		return array_map(
563
			function( $snakSerialization ) use ( $exceptionId ) {
564
				return $this->parseEntityIdParameter( $snakSerialization, $exceptionId );
565
			},
566
			$constraintParameters[$exceptionId]
567
		);
568
	}
569
570
	/**
571
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
572
	 * @throws ConstraintParameterException if the parameter is invalid
573
	 * @return string|null 'mandatory' or null
574
	 */
575
	public function parseConstraintStatusParameter( array $constraintParameters ) {
576
		$this->checkError( $constraintParameters );
577
		$constraintStatusId = $this->config->get( 'WBQualityConstraintsConstraintStatusId' );
578
		if ( !array_key_exists( $constraintStatusId, $constraintParameters ) ) {
579
			return null;
580
		}
581
582
		$mandatoryId = $this->config->get( 'WBQualityConstraintsMandatoryConstraintId' );
583
		$this->requireSingleParameter( $constraintParameters, $constraintStatusId );
584
		$snak = $this->snakDeserializer->deserialize( $constraintParameters[$constraintStatusId][0] );
585
		$this->requireValueParameter( $snak, $constraintStatusId );
586
		$statusId = $snak->getDataValue()->getEntityId()->getSerialization();
0 ignored issues
show
Bug introduced by
The method getDataValue does only exist in Wikibase\DataModel\Snak\PropertyValueSnak, but not in Wikibase\DataModel\Snak\...k\PropertySomeValueSnak.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
587
588
		if ( $statusId === $mandatoryId ) {
589
			return 'mandatory';
590
		} else {
591
			throw new ConstraintParameterException(
592
				( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
593
					->withEntityId( new PropertyId( $constraintStatusId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
594
					->withEntityIdList( [ new ItemId( $mandatoryId ) ], Role::CONSTRAINT_PARAMETER_VALUE )
595
			);
596
		}
597
	}
598
599
	/**
600
	 * Require that $dataValue is a {@link MonolingualTextValue}.
601
	 * @param DataValue $dataValue
602
	 * @param string $parameterId
603
	 * @return void
604
	 * @throws ConstraintParameterException
605
	 */
606
	private function requireMonolingualTextParameter( DataValue $dataValue, $parameterId ) {
607
		if ( !( $dataValue instanceof MonolingualTextValue ) ) {
0 ignored issues
show
Bug introduced by
The class DataValues\MonolingualTextValue does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
608
			throw new ConstraintParameterException(
609
				( new ViolationMessage( 'wbqc-violation-message-parameter-monolingualtext' ) )
610
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
611
					->withDataValue( $dataValue, Role::CONSTRAINT_PARAMETER_VALUE )
612
			);
613
		}
614
	}
615
616
	/**
617
	 * Parse a series of monolingual text snaks (serialized) into a map from language code to string.
618
	 *
619
	 * @param array $snakSerializations
620
	 * @param string $parameterId
621
	 * @throws ConstraintParameterException if invalid snaks are found or a language has multiple texts
622
	 * @return MultilingualTextValue
623
	 */
624
	private function parseMultilingualTextParameter( array $snakSerializations, $parameterId ) {
625
		$result = [];
626
627
		foreach ( $snakSerializations as $snakSerialization ) {
628
			$snak = $this->snakDeserializer->deserialize( $snakSerialization );
629
			$this->requireValueParameter( $snak, $parameterId );
630
631
			$value = $snak->getDataValue();
0 ignored issues
show
Bug introduced by
The method getDataValue does only exist in Wikibase\DataModel\Snak\PropertyValueSnak, but not in Wikibase\DataModel\Snak\...k\PropertySomeValueSnak.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
632
			$this->requireMonolingualTextParameter( $value, $parameterId );
633
			/** @var MonolingualTextValue $value */
634
635
			$code = $value->getLanguageCode();
636
			if ( array_key_exists( $code, $result ) ) {
637
				throw new ConstraintParameterException(
638
					( new ViolationMessage( 'wbqc-violation-message-parameter-single-per-language' ) )
639
						->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
640
						->withLanguage( $code )
641
				);
642
			}
643
644
			$result[$code] = $value;
645
		}
646
647
		return new MultilingualTextValue( $result );
648
	}
649
650
	/**
651
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
652
	 * @throws ConstraintParameterException if the parameter is invalid
653
	 * @return MultilingualTextValue
654
	 */
655
	public function parseSyntaxClarificationParameter( array $constraintParameters ) {
656
		$syntaxClarificationId = $this->config->get( 'WBQualityConstraintsSyntaxClarificationId' );
657
658
		if ( !array_key_exists( $syntaxClarificationId, $constraintParameters ) ) {
659
			return new MultilingualTextValue( [] );
660
		}
661
662
		$syntaxClarifications = $this->parseMultilingualTextParameter(
663
			$constraintParameters[$syntaxClarificationId],
664
			$syntaxClarificationId
665
		);
666
667
		return $syntaxClarifications;
668
	}
669
670
	/**
671
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
672
	 * @param string $constraintTypeItemId used in error messages
673
	 * @param string[]|null $validScopes a list of Context::TYPE_* constants which are valid where this parameter appears.
674
	 * If this is not null and one of the specified scopes is not in this list, a ConstraintParameterException is thrown.
675
	 * @throws ConstraintParameterException if the parameter is invalid
676
	 * @return string[]|null Context::TYPE_* constants
677
	 */
678
	public function parseConstraintScopeParameter( array $constraintParameters, $constraintTypeItemId, array $validScopes = null ) {
679
		$constraintScopeId = $this->config->get( 'WBQualityConstraintsConstraintScopeId' );
680
		$mainSnakId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnMainValueId' );
681
		$qualifiersId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnQualifiersId' );
682
		$referencesId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnReferencesId' );
683
684
		if ( !array_key_exists( $constraintScopeId, $constraintParameters ) ) {
685
			return null;
686
		}
687
688
		$contextTypes = [];
689
		foreach ( $constraintParameters[$constraintScopeId] as $snakSerialization ) {
690
			$scopeEntityId = $this->parseEntityIdParameter( $snakSerialization, $constraintScopeId );
691
			switch ( $scopeEntityId->getSerialization() ) {
692
				case $mainSnakId:
693
					$contextTypes[] = Context::TYPE_STATEMENT;
694
					break;
695
				case $qualifiersId:
696
					$contextTypes[] = Context::TYPE_QUALIFIER;
697
					break;
698
				case $referencesId:
699
					$contextTypes[] = Context::TYPE_REFERENCE;
700
					break;
701 View Code Duplication
				default:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
702
					throw new ConstraintParameterException(
703
						( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
704
							->withEntityId( new PropertyId( $constraintScopeId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
705
							->withEntityIdList(
706
								[
707
									new ItemId( $mainSnakId ),
708
									new ItemId( $qualifiersId ),
709
									new ItemId( $referencesId ),
710
								],
711
								Role::CONSTRAINT_PARAMETER_VALUE
712
							)
713
					);
714
			}
715
		}
716
717
		if ( $validScopes !== null ) {
718
			$invalidScopes = array_diff( $contextTypes, $validScopes );
719
			if ( $invalidScopes !== [] ) {
720
				$invalidScope = array_pop( $invalidScopes );
721
				throw new ConstraintParameterException(
722
					( new ViolationMessage( 'wbqc-violation-message-invalid-scope' ) )
723
						->withConstraintScope( $invalidScope, Role::CONSTRAINT_PARAMETER_VALUE )
724
						->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
725
						->withConstraintScopeList( $validScopes, Role::CONSTRAINT_PARAMETER_VALUE )
726
				);
727
			}
728
		}
729
730
		return $contextTypes;
731
	}
732
733
	/**
734
	 * Turn an item ID into a full unit string (using the concept URI).
735
	 *
736
	 * @param ItemId $unitId
737
	 * @return string unit
738
	 */
739
	private function parseUnitParameter( ItemId $unitId ) {
740
		$unitRepositoryName = $unitId->getRepositoryName();
741
		if ( !array_key_exists( $unitRepositoryName, $this->conceptBaseUris ) ) {
742
			throw new LogicException(
743
				'No base URI for concept URI for repository: ' . $unitRepositoryName
744
			);
745
		}
746
		$baseUri = $this->conceptBaseUris[$unitRepositoryName];
747
		return $baseUri . $unitId->getSerialization();
748
	}
749
750
	/**
751
	 * Turn an ItemIdSnakValue into a single unit parameter.
752
	 *
753
	 * @param ItemIdSnakValue $item
754
	 * @return UnitsParameter
755
	 * @throws ConstraintParameterException
756
	 */
757
	private function parseUnitItem( ItemIdSnakValue $item ) {
758
		switch ( true ) {
759
			case $item->isValue():
760
				$unit = $this->parseUnitParameter( $item->getItemId() );
0 ignored issues
show
Bug introduced by
It seems like $item->getItemId() can be null; however, parseUnitParameter() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
761
				return new UnitsParameter(
762
					[ $item->getItemId() ],
0 ignored issues
show
Documentation introduced by
array($item->getItemId()) is of type array<integer,object<Wik...Entity\\ItemId>|null"}>, but the function expects a array<integer,object<Wik...taModel\Entity\ItemId>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
763
					[ UnboundedQuantityValue::newFromNumber( 1, $unit ) ],
764
					false
765
				);
766
			case $item->isSomeValue():
767
				$qualifierId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
768
				throw new ConstraintParameterException(
769
					( new ViolationMessage( 'wbqc-violation-message-parameter-value-or-novalue' ) )
770
						->withEntityId( new PropertyId( $qualifierId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
771
				);
772
			case $item->isNoValue():
773
				return new UnitsParameter( [], [], true );
774
		}
775
	}
776
777
	/**
778
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
779
	 * @param string $constraintTypeItemId used in error messages
780
	 * @throws ConstraintParameterException if the parameter is invalid or missing
781
	 * @return UnitsParameter
782
	 */
783
	public function parseUnitsParameter( array $constraintParameters, $constraintTypeItemId ) {
784
		$items = $this->parseItemsParameter( $constraintParameters, $constraintTypeItemId, true );
785
		$unitItems = [];
786
		$unitQuantities = [];
787
		$unitlessAllowed = false;
788
789
		foreach ( $items as $item ) {
790
			$unit = $this->parseUnitItem( $item );
791
			$unitItems = array_merge( $unitItems, $unit->getUnitItemIds() );
792
			$unitQuantities = array_merge( $unitQuantities, $unit->getUnitQuantities() );
793
			$unitlessAllowed = $unitlessAllowed || $unit->getUnitlessAllowed();
794
		}
795
796
		if ( $unitQuantities === [] && !$unitlessAllowed ) {
797
			throw new LogicException(
798
				'The "units" parameter is required, and yet we seem to be missing any allowed unit'
799
			);
800
		}
801
802
		return new UnitsParameter( $unitItems, $unitQuantities, $unitlessAllowed );
803
	}
804
805
	/**
806
	 * Turn an ItemIdSnakValue into a single entity type parameter.
807
	 *
808
	 * @param ItemIdSnakValue $item
809
	 * @return EntityTypesParameter
810
	 * @throws ConstraintParameterException
811
	 */
812
	private function parseEntityTypeItem( ItemIdSnakValue $item ) {
813
		$parameterId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
814
815
		if ( !$item->isValue() ) {
816
			throw new ConstraintParameterException(
817
				( new ViolationMessage( 'wbqc-violation-message-parameter-value' ) )
818
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
819
			);
820
		}
821
822
		$itemId = $item->getItemId();
823
		switch ( $itemId->getSerialization() ) {
824
			case $this->config->get( 'WBQualityConstraintsWikibaseItemId' ):
825
				$entityType = 'item';
826
				break;
827
			case $this->config->get( 'WBQualityConstraintsWikibasePropertyId' ):
828
				$entityType = 'property';
829
				break;
830
			case $this->config->get( 'WBQualityConstraintsWikibaseLexemeId' ):
831
				$entityType = 'lexeme';
832
				break;
833
			default:
834
				$allowed = [
835
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibaseItemId' ) ),
836
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibasePropertyId' ) ),
837
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibaseLexemeId' ) ),
838
				];
839
				throw new ConstraintParameterException(
840
					( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
841
						->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
842
						->withEntityIdList( $allowed, Role::CONSTRAINT_PARAMETER_VALUE )
843
				);
844
		}
845
846
		return new EntityTypesParameter( [ $entityType ], [ $itemId ] );
0 ignored issues
show
Documentation introduced by
array($itemId) is of type array<integer,object<Wik...Entity\\ItemId>|null"}>, but the function expects a array<integer,object<Wik...taModel\Entity\ItemId>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
847
	}
848
849
	/**
850
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
851
	 * @param string $constraintTypeItemId used in error messages
852
	 * @throws ConstraintParameterException if the parameter is invalid or missing
853
	 * @return EntityTypesParameter
854
	 */
855
	public function parseEntityTypesParameter( array $constraintParameters, $constraintTypeItemId ) {
856
		$entityTypes = [];
857
		$entityTypeItemIds = [];
858
		$items = $this->parseItemsParameter( $constraintParameters, $constraintTypeItemId, true );
859
860
		foreach ( $items as $item ) {
861
			$entityType = $this->parseEntityTypeItem( $item );
862
			$entityTypes = array_merge( $entityTypes, $entityType->getEntityTypes() );
863
			$entityTypeItemIds = array_merge( $entityTypeItemIds, $entityType->getEntityTypeItemIds() );
864
		}
865
866
		if ( empty( $entityTypes ) ) {
867
			// @codeCoverageIgnoreStart
868
			throw new LogicException(
869
				'The "entity types" parameter is required, ' .
870
				'and yet we seem to be missing any allowed entity type'
871
			);
872
			// @codeCoverageIgnoreEnd
873
		}
874
875
		return new EntityTypesParameter( $entityTypes, $entityTypeItemIds );
876
	}
877
878
	/**
879
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
880
	 * @throws ConstraintParameterException if the parameter is invalid
881
	 * @return PropertyId[]
882
	 */
883
	public function parseSeparatorsParameter( array $constraintParameters ) {
884
		$separatorId = $this->config->get( 'WBQualityConstraintsSeparatorId' );
885
886
		if ( !array_key_exists( $separatorId, $constraintParameters ) ) {
887
			return [];
888
		}
889
890
		$parameters = $constraintParameters[$separatorId];
891
		$separators = [];
892
893
		foreach ( $parameters as $parameter ) {
894
			$separators[] = $this->parsePropertyIdParameter( $parameter, $separatorId );
895
		}
896
897
		return $separators;
898
	}
899
900
}
901