Completed
Push — master ( f2f357...ccb861 )
by
unknown
02:21
created

ConstraintParameterParser::parseItemsParameter()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.4746
c 0
b 0
f 0
cc 7
nc 7
nop 3
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\Role;
26
27
/**
28
 * Helper for parsing constraint parameters
29
 * that were imported from constraint statements.
30
 *
31
 * All public methods of this class expect constraint parameters
32
 * (see {@link \WikibaseQuality\Constraint::getConstraintParameters()})
33
 * and return parameter objects or throw {@link ConstraintParameterException}s.
34
 * The results are used by the checkers,
35
 * which may include rendering them into violation messages.
36
 *
37
 * @author Lucas Werkmeister
38
 * @license GPL-2.0-or-later
39
 */
40
class ConstraintParameterParser {
41
42
	/**
43
	 * @var Config
44
	 */
45
	private $config;
46
47
	/**
48
	 * @var SnakDeserializer
49
	 */
50
	private $snakDeserializer;
51
52
	/**
53
	 * @var string[]
54
	 */
55
	private $conceptBaseUris;
56
57
	/**
58
	 * @param Config $config
59
	 *   contains entity IDs used in constraint parameters (constraint statement qualifiers)
60
	 * @param DeserializerFactory $factory
61
	 *   used to parse constraint statement qualifiers into constraint parameters
62
	 * @param string[] $conceptBaseUris
63
	 *   mapping from repository names to base URIs of concept URIs,
64
	 *   used to obtain the full unit string from an entity ID given in constraint parameters
65
	 */
66
	public function __construct(
67
		Config $config,
68
		DeserializerFactory $factory,
69
		array $conceptBaseUris
70
	) {
71
		$this->config = $config;
72
		$this->snakDeserializer = $factory->newSnakDeserializer();
73
		$this->conceptBaseUris = $conceptBaseUris;
74
	}
75
76
	/**
77
	 * Check if any errors are recorded in the constraint parameters.
78
	 * @param array $parameters
79
	 * @throws ConstraintParameterException
80
	 */
81
	public function checkError( array $parameters ) {
82
		if ( array_key_exists( '@error', $parameters ) ) {
83
			$error = $parameters['@error'];
84
			if ( array_key_exists( 'toolong', $error ) && $error['toolong'] ) {
85
				$msg = 'wbqc-violation-message-parameters-error-toolong';
86
			} else {
87
				$msg = 'wbqc-violation-message-parameters-error-unknown';
88
			}
89
			throw new ConstraintParameterException( new ViolationMessage( $msg ) );
90
		}
91
	}
92
93
	/**
94
	 * Require that $parameters contains exactly one $parameterId parameter.
95
	 * @param array $parameters
96
	 * @param string $parameterId
97
	 * @throws ConstraintParameterException
98
	 */
99 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...
100
		if ( count( $parameters[$parameterId] ) !== 1 ) {
101
			throw new ConstraintParameterException(
102
				( new ViolationMessage( 'wbqc-violation-message-parameter-single' ) )
103
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
104
			);
105
		}
106
	}
107
108
	/**
109
	 * Require that $snak is a {@link PropertyValueSnak}.
110
	 * @param Snak $snak
111
	 * @param string $parameterId
112
	 * @return void
113
	 * @throws ConstraintParameterException
114
	 */
115 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...
116
		if ( !( $snak instanceof PropertyValueSnak ) ) {
117
			throw new ConstraintParameterException(
118
				( new ViolationMessage( 'wbqc-violation-message-parameter-value' ) )
119
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
120
			);
121
		}
122
	}
123
124
	/**
125
	 * Parse a single entity ID parameter.
126
	 * @param array $snakSerialization
127
	 * @param string $parameterId
128
	 * @throws ConstraintParameterException
129
	 * @return EntityId
130
	 */
131 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...
132
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
133
		$this->requireValueParameter( $snak, $parameterId );
134
		$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...
135
		if ( $value instanceof EntityIdValue ) {
136
			return $value->getEntityId();
137
		} else {
138
			throw new ConstraintParameterException(
139
				( new ViolationMessage( 'wbqc-violation-message-parameter-entity' ) )
140
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
141
					->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
142
			);
143
		}
144
	}
145
146
	/**
147
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
148
	 * @param string $constraintTypeItemId used in error messages
149
	 * @throws ConstraintParameterException if the parameter is invalid or missing
150
	 * @return string[] class entity ID serializations
151
	 */
152
	public function parseClassParameter( array $constraintParameters, $constraintTypeItemId ) {
153
		$this->checkError( $constraintParameters );
154
		$classId = $this->config->get( 'WBQualityConstraintsClassId' );
155
		if ( !array_key_exists( $classId, $constraintParameters ) ) {
156
			throw new ConstraintParameterException(
157
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
158
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
159
					->withEntityId( new PropertyId( $classId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
160
			);
161
		}
162
163
		$classes = [];
164
		foreach ( $constraintParameters[$classId] as $class ) {
165
			$classes[] = $this->parseEntityIdParameter( $class, $classId )->getSerialization();
166
		}
167
		return $classes;
168
	}
169
170
	/**
171
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
172
	 * @param string $constraintTypeItemId used in error messages
173
	 * @throws ConstraintParameterException if the parameter is invalid or missing
174
	 * @return string 'instance', 'subclass', or 'instanceOrSubclass'
175
	 */
176
	public function parseRelationParameter( array $constraintParameters, $constraintTypeItemId ) {
177
		$this->checkError( $constraintParameters );
178
		$relationId = $this->config->get( 'WBQualityConstraintsRelationId' );
179
		if ( !array_key_exists( $relationId, $constraintParameters ) ) {
180
			throw new ConstraintParameterException(
181
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
182
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
183
					->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
184
			);
185
		}
186
187
		$this->requireSingleParameter( $constraintParameters, $relationId );
188
		$relationEntityId = $this->parseEntityIdParameter( $constraintParameters[$relationId][0], $relationId );
189
		$instanceId = $this->config->get( 'WBQualityConstraintsInstanceOfRelationId' );
190
		$subclassId = $this->config->get( 'WBQualityConstraintsSubclassOfRelationId' );
191
		$instanceOrSubclassId = $this->config->get( 'WBQualityConstraintsInstanceOrSubclassOfRelationId' );
192
		switch ( $relationEntityId ) {
193
			case $instanceId:
194
				return 'instance';
195
			case $subclassId:
196
				return 'subclass';
197
			case $instanceOrSubclassId:
198
				return 'instanceOrSubclass';
199 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...
200
				throw new ConstraintParameterException(
201
					( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
202
						->withEntityId( new PropertyId( $relationId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
203
						->withEntityIdList(
204
							[
205
								new ItemId( $instanceId ),
206
								new ItemId( $subclassId ),
207
								new ItemId( $instanceOrSubclassId ),
208
							],
209
							Role::CONSTRAINT_PARAMETER_VALUE
210
						)
211
				);
212
		}
213
	}
214
215
	/**
216
	 * Parse a single property ID parameter.
217
	 * @param array $snakSerialization
218
	 * @param string $parameterId used in error messages
219
	 * @throws ConstraintParameterException
220
	 * @return PropertyId
221
	 */
222
	private function parsePropertyIdParameter( array $snakSerialization, $parameterId ) {
223
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
224
		$this->requireValueParameter( $snak, $parameterId );
225
		$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...
226
		if ( $value instanceof EntityIdValue ) {
227
			$id = $value->getEntityId();
228
			if ( $id instanceof PropertyId ) {
229
				return $id;
230
			}
231
		}
232
		throw new ConstraintParameterException(
233
			( new ViolationMessage( 'wbqc-violation-message-parameter-property' ) )
234
				->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
235
				->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
236
		);
237
	}
238
239
	/**
240
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
241
	 * @param string $constraintTypeItemId used in error messages
242
	 *
243
	 * @throws ConstraintParameterException if the parameter is invalid or missing
244
	 * @return PropertyId
245
	 */
246 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...
247
		$this->checkError( $constraintParameters );
248
		$propertyId = $this->config->get( 'WBQualityConstraintsPropertyId' );
249
		if ( !array_key_exists( $propertyId, $constraintParameters ) ) {
250
			throw new ConstraintParameterException(
251
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
252
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
253
					->withEntityId( new PropertyId( $propertyId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
254
			);
255
		}
256
257
		$this->requireSingleParameter( $constraintParameters, $propertyId );
258
		return $this->parsePropertyIdParameter( $constraintParameters[$propertyId][0], $propertyId );
259
	}
260
261
	private function parseItemIdParameter( PropertyValueSnak $snak, $parameterId ) {
262
		$dataValue = $snak->getDataValue();
263
		if ( $dataValue instanceof EntityIdValue &&
264
			$dataValue->getEntityId() instanceof ItemId
265
		) {
266
			return ItemIdSnakValue::fromItemId( $dataValue->getEntityId() );
267
		} else {
268
			throw new ConstraintParameterException(
269
				( new ViolationMessage( 'wbqc-violation-message-parameter-item' ) )
270
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
271
					->withDataValue( $dataValue, Role::CONSTRAINT_PARAMETER_VALUE )
272
			);
273
		}
274
	}
275
276
	/**
277
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
278
	 * @param string $constraintTypeItemId used in error messages
279
	 * @param bool $required whether the parameter is required (error if absent) or not ([] if absent)
280
	 * @throws ConstraintParameterException if the parameter is invalid or missing
281
	 * @return ItemIdSnakValue[] array of values
282
	 */
283
	public function parseItemsParameter( array $constraintParameters, $constraintTypeItemId, $required ) {
284
		$this->checkError( $constraintParameters );
285
		$qualifierId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
286
		if ( !array_key_exists( $qualifierId, $constraintParameters ) ) {
287
			if ( $required ) {
288
				throw new ConstraintParameterException(
289
					( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
290
						->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
291
						->withEntityId( new PropertyId( $qualifierId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
292
				);
293
			} else {
294
				return [];
295
			}
296
		}
297
298
		$values = [];
299
		foreach ( $constraintParameters[$qualifierId] as $parameter ) {
300
			$snak = $this->snakDeserializer->deserialize( $parameter );
301
			switch ( true ) {
302
				case $snak instanceof PropertyValueSnak:
303
					$values[] = $this->parseItemIdParameter( $snak, $qualifierId );
304
					break;
305
				case $snak instanceof PropertySomeValueSnak:
306
					$values[] = ItemIdSnakValue::someValue();
307
					break;
308
				case $snak instanceof PropertyNoValueSnak:
309
					$values[] = ItemIdSnakValue::noValue();
310
					break;
311
			}
312
		}
313
		return $values;
314
	}
315
316
	/**
317
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
318
	 * @param string $constraintTypeItemId used in error messages
319
	 * @throws ConstraintParameterException if the parameter is invalid or missing
320
	 * @return PropertyId[]
321
	 */
322
	public function parsePropertiesParameter( array $constraintParameters, $constraintTypeItemId ) {
323
		$this->checkError( $constraintParameters );
324
		$propertyId = $this->config->get( 'WBQualityConstraintsPropertyId' );
325
		if ( !array_key_exists( $propertyId, $constraintParameters ) ) {
326
			throw new ConstraintParameterException(
327
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
328
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
329
					->withEntityId( new PropertyId( $propertyId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
330
			);
331
		}
332
333
		$parameters = $constraintParameters[$propertyId];
334
		if ( count( $parameters ) === 1 &&
335
			$this->snakDeserializer->deserialize( $parameters[0] ) instanceof PropertyNoValueSnak
336
		) {
337
			return [];
338
		}
339
340
		$properties = [];
341
		foreach ( $parameters as $parameter ) {
342
			$properties[] = $this->parsePropertyIdParameter( $parameter, $propertyId );
343
		}
344
		return $properties;
345
	}
346
347
	/**
348
	 * @param array $snakSerialization
349
	 * @param string $parameterId
350
	 * @throws ConstraintParameterException
351
	 * @return DataValue|null
352
	 */
353
	private function parseValueOrNoValueParameter( array $snakSerialization, $parameterId ) {
354
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
355
		if ( $snak instanceof PropertyValueSnak ) {
356
			return $snak->getDataValue();
357
		} elseif ( $snak instanceof PropertyNoValueSnak ) {
358
			return null;
359
		} else {
360
			throw new ConstraintParameterException(
361
				( new ViolationMessage( 'wbqc-violation-message-parameter-value-or-novalue' ) )
362
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
363
			);
364
		}
365
	}
366
367
	/**
368
	 * @param array $snakSerialization
369
	 * @param string $parameterId
370
	 * @return DataValue|null
371
	 */
372
	private function parseValueOrNoValueOrNowParameter( array $snakSerialization, $parameterId ) {
373
		try {
374
			return $this->parseValueOrNoValueParameter( $snakSerialization, $parameterId );
375
		} catch ( ConstraintParameterException $e ) {
376
			// unknown value means “now”
377
			return new NowValue();
378
		}
379
	}
380
381
	/**
382
	 * Checks whether there is exactly one non-null quantity with the given unit.
383
	 * @param DataValue|null $min
384
	 * @param DataValue|null $max
385
	 * @param string $unit
386
	 * @return bool
387
	 */
388
	private function exactlyOneQuantityWithUnit( DataValue $min = null, DataValue $max = null, $unit ) {
389
		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...
390
			!( $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...
391
		) {
392
			return false;
393
		}
394
395
		return ( $min->getUnit() === $unit ) !== ( $max->getUnit() === $unit );
396
	}
397
398
	/**
399
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
400
	 * @param string $minimumId
401
	 * @param string $maximumId
402
	 * @param string $constraintTypeItemId used in error messages
403
	 * @param string $type 'quantity' or 'time' (can be data type or data value type)
404
	 *
405
	 * @throws ConstraintParameterException if the parameter is invalid or missing
406
	 * @return DataValue[] if the parameter is invalid or missing
407
	 */
408
	private function parseRangeParameter( array $constraintParameters, $minimumId, $maximumId, $constraintTypeItemId, $type ) {
409
		$this->checkError( $constraintParameters );
410
		if ( !array_key_exists( $minimumId, $constraintParameters ) ||
411
			!array_key_exists( $maximumId, $constraintParameters )
412
		) {
413
			throw new ConstraintParameterException(
414
				( new ViolationMessage( 'wbqc-violation-message-range-parameters-needed' ) )
415
					->withDataValueType( $type )
416
					->withEntityId( new PropertyId( $minimumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
417
					->withEntityId( new PropertyId( $maximumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
418
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
419
			);
420
		}
421
422
		$this->requireSingleParameter( $constraintParameters, $minimumId );
423
		$this->requireSingleParameter( $constraintParameters, $maximumId );
424
		$parseFunction = $type === 'time' ? 'parseValueOrNoValueOrNowParameter' : 'parseValueOrNoValueParameter';
425
		$min = $this->$parseFunction( $constraintParameters[$minimumId][0], $minimumId );
426
		$max = $this->$parseFunction( $constraintParameters[$maximumId][0], $maximumId );
427
428
		$yearUnit = $this->config->get( 'WBQualityConstraintsYearUnit' );
429
		if ( $this->exactlyOneQuantityWithUnit( $min, $max, $yearUnit ) ) {
430
			throw new ConstraintParameterException(
431
				new ViolationMessage( 'wbqc-violation-message-range-parameters-one-year' )
432
			);
433
		}
434
		if ( $min === null && $max === null ||
435
			$min !== null && $max !== null && $min->equals( $max ) ) {
436
			throw new ConstraintParameterException(
437
				( new ViolationMessage( 'wbqc-violation-message-range-parameters-same' ) )
438
					->withEntityId( new PropertyId( $minimumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
439
					->withEntityId( new PropertyId( $maximumId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
440
			);
441
		}
442
443
		return [ $min, $max ];
444
	}
445
446
	/**
447
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
448
	 * @param string $constraintTypeItemId used in error messages
449
	 *
450
	 * @throws ConstraintParameterException if the parameter is invalid or missing
451
	 * @return DataValue[] a pair of two data values, either of which may be null to signify an open range
452
	 */
453
	public function parseQuantityRangeParameter( array $constraintParameters, $constraintTypeItemId ) {
454
		return $this->parseRangeParameter(
455
			$constraintParameters,
456
			$this->config->get( 'WBQualityConstraintsMinimumQuantityId' ),
457
			$this->config->get( 'WBQualityConstraintsMaximumQuantityId' ),
458
			$constraintTypeItemId,
459
			'quantity'
460
		);
461
	}
462
463
	/**
464
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
465
	 * @param string $constraintTypeItemId used in error messages
466
	 *
467
	 * @throws ConstraintParameterException if the parameter is invalid or missing
468
	 * @return DataValue[] a pair of two data values, either of which may be null to signify an open range
469
	 */
470
	public function parseTimeRangeParameter( array $constraintParameters, $constraintTypeItemId ) {
471
		return $this->parseRangeParameter(
472
			$constraintParameters,
473
			$this->config->get( 'WBQualityConstraintsMinimumDateId' ),
474
			$this->config->get( 'WBQualityConstraintsMaximumDateId' ),
475
			$constraintTypeItemId,
476
			'time'
477
		);
478
	}
479
480
	/**
481
	 * Parse a single string parameter.
482
	 * @param array $snakSerialization
483
	 * @param string $parameterId
484
	 * @throws ConstraintParameterException
485
	 * @return string
486
	 */
487 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...
488
		$snak = $this->snakDeserializer->deserialize( $snakSerialization );
489
		$this->requireValueParameter( $snak, $parameterId );
490
		$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...
491
		if ( $value instanceof StringValue ) {
492
			return $value->getValue();
493
		} else {
494
			throw new ConstraintParameterException(
495
				( new ViolationMessage( 'wbqc-violation-message-parameter-string' ) )
496
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
497
					->withDataValue( $value, Role::CONSTRAINT_PARAMETER_VALUE )
498
			);
499
		}
500
	}
501
502
	/**
503
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
504
	 * @param string $constraintTypeItemId used in error messages
505
	 * @throws ConstraintParameterException if the parameter is invalid or missing
506
	 * @return string
507
	 */
508
	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...
509
		$this->checkError( $constraintParameters );
510
		$namespaceId = $this->config->get( 'WBQualityConstraintsNamespaceId' );
511
		if ( !array_key_exists( $namespaceId, $constraintParameters ) ) {
512
			return '';
513
		}
514
515
		$this->requireSingleParameter( $constraintParameters, $namespaceId );
516
		return $this->parseStringParameter( $constraintParameters[$namespaceId][0], $namespaceId );
517
	}
518
519
	/**
520
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
521
	 * @param string $constraintTypeItemId used in error messages
522
	 * @throws ConstraintParameterException if the parameter is invalid or missing
523
	 * @return string
524
	 */
525 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...
526
		$this->checkError( $constraintParameters );
527
		$formatId = $this->config->get( 'WBQualityConstraintsFormatAsARegularExpressionId' );
528
		if ( !array_key_exists( $formatId, $constraintParameters ) ) {
529
			throw new ConstraintParameterException(
530
				( new ViolationMessage( 'wbqc-violation-message-parameter-needed' ) )
531
					->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
532
					->withEntityId( new PropertyId( $formatId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
533
			);
534
		}
535
536
		$this->requireSingleParameter( $constraintParameters, $formatId );
537
		return $this->parseStringParameter( $constraintParameters[$formatId][0], $formatId );
538
	}
539
540
	/**
541
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
542
	 * @throws ConstraintParameterException if the parameter is invalid
543
	 * @return EntityId[]
544
	 */
545
	public function parseExceptionParameter( array $constraintParameters ) {
546
		$this->checkError( $constraintParameters );
547
		$exceptionId = $this->config->get( 'WBQualityConstraintsExceptionToConstraintId' );
548
		if ( !array_key_exists( $exceptionId, $constraintParameters ) ) {
549
			return [];
550
		}
551
552
		return array_map(
553
			function( $snakSerialization ) use ( $exceptionId ) {
554
				return $this->parseEntityIdParameter( $snakSerialization, $exceptionId );
555
			},
556
			$constraintParameters[$exceptionId]
557
		);
558
	}
559
560
	/**
561
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
562
	 * @throws ConstraintParameterException if the parameter is invalid
563
	 * @return string|null 'mandatory' or null
564
	 */
565
	public function parseConstraintStatusParameter( array $constraintParameters ) {
566
		$this->checkError( $constraintParameters );
567
		$constraintStatusId = $this->config->get( 'WBQualityConstraintsConstraintStatusId' );
568
		if ( !array_key_exists( $constraintStatusId, $constraintParameters ) ) {
569
			return null;
570
		}
571
572
		$mandatoryId = $this->config->get( 'WBQualityConstraintsMandatoryConstraintId' );
573
		$this->requireSingleParameter( $constraintParameters, $constraintStatusId );
574
		$snak = $this->snakDeserializer->deserialize( $constraintParameters[$constraintStatusId][0] );
575
		$this->requireValueParameter( $snak, $constraintStatusId );
576
		$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...
577
578
		if ( $statusId === $mandatoryId ) {
579
			return 'mandatory';
580
		} else {
581
			throw new ConstraintParameterException(
582
				( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
583
					->withEntityId( new PropertyId( $constraintStatusId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
584
					->withEntityIdList( [ new ItemId( $mandatoryId ) ], Role::CONSTRAINT_PARAMETER_VALUE )
585
			);
586
		}
587
	}
588
589
	/**
590
	 * Require that $dataValue is a {@link MonolingualTextValue}.
591
	 * @param DataValue $dataValue
592
	 * @param string $parameterId
593
	 * @return void
594
	 * @throws ConstraintParameterException
595
	 */
596
	private function requireMonolingualTextParameter( DataValue $dataValue, $parameterId ) {
597
		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...
598
			throw new ConstraintParameterException(
599
				( new ViolationMessage( 'wbqc-violation-message-parameter-monolingualtext' ) )
600
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
601
					->withDataValue( $dataValue, Role::CONSTRAINT_PARAMETER_VALUE )
602
			);
603
		}
604
	}
605
606
	/**
607
	 * Parse a series of monolingual text snaks (serialized) into a map from language code to string.
608
	 *
609
	 * @param array $snakSerializations
610
	 * @param string $parameterId
611
	 * @throws ConstraintParameterException if invalid snaks are found or a language has multiple texts
612
	 * @return MultilingualTextValue
613
	 */
614
	private function parseMultilingualTextParameter( array $snakSerializations, $parameterId ) {
615
		$result = [];
616
617
		foreach ( $snakSerializations as $snakSerialization ) {
618
			$snak = $this->snakDeserializer->deserialize( $snakSerialization );
619
			$this->requireValueParameter( $snak, $parameterId );
620
621
			$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...
622
			$this->requireMonolingualTextParameter( $value, $parameterId );
623
			/** @var MonolingualTextValue $value */
624
625
			$code = $value->getLanguageCode();
626
			if ( array_key_exists( $code, $result ) ) {
627
				throw new ConstraintParameterException(
628
					( new ViolationMessage( 'wbqc-violation-message-parameter-single-per-language' ) )
629
						->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
630
						->withLanguage( $code )
631
				);
632
			}
633
634
			$result[$code] = $value;
635
		}
636
637
		return new MultilingualTextValue( $result );
638
	}
639
640
	/**
641
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
642
	 * @throws ConstraintParameterException if the parameter is invalid
643
	 * @return MultilingualTextValue
644
	 */
645
	public function parseSyntaxClarificationParameter( array $constraintParameters ) {
646
		$syntaxClarificationId = $this->config->get( 'WBQualityConstraintsSyntaxClarificationId' );
647
648
		if ( !array_key_exists( $syntaxClarificationId, $constraintParameters ) ) {
649
			return new MultilingualTextValue( [] );
650
		}
651
652
		$syntaxClarifications = $this->parseMultilingualTextParameter(
653
			$constraintParameters[$syntaxClarificationId],
654
			$syntaxClarificationId
655
		);
656
657
		return $syntaxClarifications;
658
	}
659
660
	/**
661
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
662
	 * @param string $constraintTypeItemId used in error messages
663
	 * @param string[]|null $validScopes a list of Context::TYPE_* constants which are valid where this parameter appears.
664
	 * If this is not null and one of the specified scopes is not in this list, a ConstraintParameterException is thrown.
665
	 * @throws ConstraintParameterException if the parameter is invalid
666
	 * @return string[]|null Context::TYPE_* constants
667
	 */
668
	public function parseConstraintScopeParameter( array $constraintParameters, $constraintTypeItemId, array $validScopes = null ) {
669
		$constraintScopeId = $this->config->get( 'WBQualityConstraintsConstraintScopeId' );
670
		$mainSnakId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnMainValueId' );
671
		$qualifiersId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnQualifiersId' );
672
		$referencesId = $this->config->get( 'WBQualityConstraintsConstraintCheckedOnReferencesId' );
673
674
		if ( !array_key_exists( $constraintScopeId, $constraintParameters ) ) {
675
			return null;
676
		}
677
678
		$contextTypes = [];
679
		foreach ( $constraintParameters[$constraintScopeId] as $snakSerialization ) {
680
			$scopeEntityId = $this->parseEntityIdParameter( $snakSerialization, $constraintScopeId );
681
			switch ( $scopeEntityId->getSerialization() ) {
682
				case $mainSnakId:
683
					$contextTypes[] = Context::TYPE_STATEMENT;
684
					break;
685
				case $qualifiersId:
686
					$contextTypes[] = Context::TYPE_QUALIFIER;
687
					break;
688
				case $referencesId:
689
					$contextTypes[] = Context::TYPE_REFERENCE;
690
					break;
691 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...
692
					throw new ConstraintParameterException(
693
						( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
694
							->withEntityId( new PropertyId( $constraintScopeId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
695
							->withEntityIdList(
696
								[
697
									new ItemId( $mainSnakId ),
698
									new ItemId( $qualifiersId ),
699
									new ItemId( $referencesId ),
700
								],
701
								Role::CONSTRAINT_PARAMETER_VALUE
702
							)
703
					);
704
			}
705
		}
706
707
		if ( $validScopes !== null ) {
708
			$invalidScopes = array_diff( $contextTypes, $validScopes );
709
			if ( $invalidScopes !== [] ) {
710
				$invalidScope = array_pop( $invalidScopes );
711
				throw new ConstraintParameterException(
712
					( new ViolationMessage( 'wbqc-violation-message-invalid-scope' ) )
713
						->withConstraintScope( $invalidScope, Role::CONSTRAINT_PARAMETER_VALUE )
714
						->withEntityId( new ItemId( $constraintTypeItemId ), Role::CONSTRAINT_TYPE_ITEM )
715
						->withConstraintScopeList( $validScopes, Role::CONSTRAINT_PARAMETER_VALUE )
716
				);
717
			}
718
		}
719
720
		return $contextTypes;
721
	}
722
723
	/**
724
	 * Turn an item ID into a full unit string (using the concept URI).
725
	 *
726
	 * @param ItemId $unitId
727
	 * @return string unit
728
	 */
729
	private function parseUnitParameter( ItemId $unitId ) {
730
		$unitRepositoryName = $unitId->getRepositoryName();
731
		if ( !array_key_exists( $unitRepositoryName, $this->conceptBaseUris ) ) {
732
			throw new LogicException(
733
				'No base URI for concept URI for repository: ' . $unitRepositoryName
734
			);
735
		}
736
		$baseUri = $this->conceptBaseUris[$unitRepositoryName];
737
		return $baseUri . $unitId->getSerialization();
738
	}
739
740
	/**
741
	 * Turn an ItemIdSnakValue into a single unit parameter.
742
	 *
743
	 * @param ItemIdSnakValue $item
744
	 * @return UnitsParameter
745
	 * @throws ConstraintParameterException
746
	 */
747
	private function parseUnitItem( ItemIdSnakValue $item ) {
748
		switch ( true ) {
749
			case $item->isValue():
750
				$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...
751
				return new UnitsParameter(
752
					[ $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...
753
					[ UnboundedQuantityValue::newFromNumber( 1, $unit ) ],
754
					false
755
				);
756
			case $item->isSomeValue():
757
				$qualifierId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
758
				throw new ConstraintParameterException(
759
					( new ViolationMessage( 'wbqc-violation-message-parameter-value-or-novalue' ) )
760
						->withEntityId( new PropertyId( $qualifierId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
761
				);
762
			case $item->isNoValue():
763
				return new UnitsParameter( [], [], true );
764
		}
765
	}
766
767
	/**
768
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
769
	 * @param string $constraintTypeItemId used in error messages
770
	 * @throws ConstraintParameterException if the parameter is invalid or missing
771
	 * @return UnitsParameter
772
	 */
773
	public function parseUnitsParameter( array $constraintParameters, $constraintTypeItemId ) {
774
		$items = $this->parseItemsParameter( $constraintParameters, $constraintTypeItemId, true );
775
		$unitItems = [];
776
		$unitQuantities = [];
777
		$unitlessAllowed = false;
778
779
		foreach ( $items as $item ) {
780
			$unit = $this->parseUnitItem( $item );
781
			$unitItems = array_merge( $unitItems, $unit->getUnitItemIds() );
782
			$unitQuantities = array_merge( $unitQuantities, $unit->getUnitQuantities() );
783
			$unitlessAllowed = $unitlessAllowed || $unit->getUnitlessAllowed();
784
		}
785
786
		if ( $unitQuantities === [] && !$unitlessAllowed ) {
787
			throw new LogicException(
788
				'The "units" parameter is required, and yet we seem to be missing any allowed unit'
789
			);
790
		}
791
792
		return new UnitsParameter( $unitItems, $unitQuantities, $unitlessAllowed );
793
	}
794
795
	/**
796
	 * Turn an ItemIdSnakValue into a single entity type parameter.
797
	 *
798
	 * @param ItemIdSnakValue $item
799
	 * @return EntityTypesParameter
800
	 * @throws ConstraintParameterException
801
	 */
802
	private function parseEntityTypeItem( ItemIdSnakValue $item ) {
803
		$parameterId = $this->config->get( 'WBQualityConstraintsQualifierOfPropertyConstraintId' );
804
805
		if ( !$item->isValue() ) {
806
			throw new ConstraintParameterException(
807
				( new ViolationMessage( 'wbqc-violation-message-parameter-value' ) )
808
					->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
809
			);
810
		}
811
812
		$itemId = $item->getItemId();
813
		switch ( $itemId->getSerialization() ) {
814
			case $this->config->get( 'WBQualityConstraintsWikibaseItemId' ):
815
				$entityType = 'item';
816
				break;
817
			case $this->config->get( 'WBQualityConstraintsWikibasePropertyId' ):
818
				$entityType = 'property';
819
				break;
820
			case $this->config->get( 'WBQualityConstraintsWikibaseLexemeId' ):
821
				$entityType = 'lexeme';
822
				break;
823
			default:
824
				$allowed = [
825
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibaseItemId' ) ),
826
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibasePropertyId' ) ),
827
					new ItemId( $this->config->get( 'WBQualityConstraintsWikibaseLexemeId' ) ),
828
				];
829
				throw new ConstraintParameterException(
830
					( new ViolationMessage( 'wbqc-violation-message-parameter-oneof' ) )
831
						->withEntityId( new PropertyId( $parameterId ), Role::CONSTRAINT_PARAMETER_PROPERTY )
832
						->withEntityIdList( $allowed, Role::CONSTRAINT_PARAMETER_VALUE )
833
				);
834
		}
835
836
		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...
837
	}
838
839
	/**
840
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
841
	 * @param string $constraintTypeItemId used in error messages
842
	 * @throws ConstraintParameterException if the parameter is invalid or missing
843
	 * @return EntityTypesParameter
844
	 */
845
	public function parseEntityTypesParameter( array $constraintParameters, $constraintTypeItemId ) {
846
		$entityTypes = [];
847
		$entityTypeItemIds = [];
848
		$items = $this->parseItemsParameter( $constraintParameters, $constraintTypeItemId, true );
849
850
		foreach ( $items as $item ) {
851
			$entityType = $this->parseEntityTypeItem( $item );
852
			$entityTypes = array_merge( $entityTypes, $entityType->getEntityTypes() );
853
			$entityTypeItemIds = array_merge( $entityTypeItemIds, $entityType->getEntityTypeItemIds() );
854
		}
855
856
		if ( empty( $entityTypes ) ) {
857
			// @codeCoverageIgnoreStart
858
			throw new LogicException(
859
				'The "entity types" parameter is required, ' .
860
				'and yet we seem to be missing any allowed entity type'
861
			);
862
			// @codeCoverageIgnoreEnd
863
		}
864
865
		return new EntityTypesParameter( $entityTypes, $entityTypeItemIds );
866
	}
867
868
	/**
869
	 * @param array $constraintParameters see {@link \WikibaseQuality\Constraint::getConstraintParameters()}
870
	 * @throws ConstraintParameterException if the parameter is invalid
871
	 * @return PropertyId[]
872
	 */
873
	public function parseSeparatorsParameter( array $constraintParameters ) {
874
		$separatorId = $this->config->get( 'WBQualityConstraintsSeparatorId' );
875
876
		if ( !array_key_exists( $separatorId, $constraintParameters ) ) {
877
			return [];
878
		}
879
880
		$parameters = $constraintParameters[$separatorId];
881
		$separators = [];
882
883
		foreach ( $parameters as $parameter ) {
884
			$separators[] = $this->parsePropertyIdParameter( $parameter, $separatorId );
885
		}
886
887
		return $separators;
888
	}
889
890
}
891