Issues (55)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/ByPropertyIdArray.php (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Wikibase\DataModel;
4
5
use ArrayObject;
6
use InvalidArgumentException;
7
use OutOfBoundsException;
8
use RuntimeException;
9
use Traversable;
10
use Wikibase\DataModel\Entity\PropertyId;
11
12
/**
13
 * Helper for managing objects indexed by property id.
14
 *
15
 * This is a light weight alternative approach to using something
16
 * like GenericArrayObject with the advantages that no extra interface
17
 * is needed and that indexing does not happen automatically.
18
 *
19
 * Lack of automatic indexing means that you will need to call the
20
 * buildIndex method before doing any look-ups.
21
 *
22
 * Since no extra interface is used, the user is responsible for only
23
 * adding objects that have a getPropertyId method that returns either
24
 * a string or integer when called with no arguments.
25
 *
26
 * Objects may be added or moved within the structure. Absolute indices (indices according to the
27
 * flat list of objects) may be specified to add or move objects. These management operations take
28
 * the property grouping into account. Adding or moving objects outside their "property groups"
29
 * shifts the whole group towards that index.
30
 *
31
 * Example of moving an object within its "property group":
32
 * o1 (p1)                           o1 (p1)
33
 * o2 (p2)                       /-> o3 (p2)
34
 * o3 (p2) ---> move to index 1 -/   o2 (p2)
35
 *
36
 * Example of moving an object that triggers moving the whole "property group":
37
 * o1 (p1)                       /-> o3 (p2)
38
 * o2 (p2)                       |   o2 (p2)
39
 * o3 (p2) ---> move to index 0 -/   o1 (p1)
40
 *
41
 * @since 0.2
42
 * @deprecated since 5.0, use a DataModel Service instead
43
 *
44
 * @license GPL-2.0-or-later
45
 * @author H. Snater < [email protected] >
46
 */
47
class ByPropertyIdArray extends ArrayObject {
48
49
	/**
50
	 * @var array[]|null
51
	 */
52
	private $byId = null;
53
54
	/**
55
	 * @deprecated since 5.0, use a DataModel Service instead
56 65
	 * @see ArrayObject::__construct
57 65
	 *
58 65
	 * @param PropertyIdProvider[]|Traversable|null $input
59
	 *
60
	 * @throws InvalidArgumentException
61
	 */
62
	public function __construct( $input = null ) {
63
		if ( is_object( $input ) && !( $input instanceof Traversable ) ) {
64
			throw new InvalidArgumentException( '$input must be an array, Traversable or null' );
65 62
		}
66 62
67
		parent::__construct( (array)$input );
68 62
	}
69 61
70
	/**
71 61
	 * Builds the index for doing look-ups by property id.
72 61
	 *
73 61
	 * @since 0.2
74
	 */
75 61
	public function buildIndex() {
76 62
		$this->byId = [];
77 62
78
		/** @var PropertyIdProvider $object */
79
		foreach ( $this as $object ) {
80
			$propertyId = $object->getPropertyId()->getSerialization();
81
82
			if ( !array_key_exists( $propertyId, $this->byId ) ) {
83
				$this->byId[$propertyId] = [];
84 64
			}
85 64
86 2
			$this->byId[$propertyId][] = $object;
87
		}
88 62
	}
89
90
	/**
91
	 * Checks whether id indexed array has been generated.
92
	 *
93
	 * @throws RuntimeException
94
	 */
95
	private function assertIndexIsBuild() {
96
		if ( $this->byId === null ) {
97
			throw new RuntimeException( 'Index not build, call buildIndex first' );
98 38
		}
99 38
	}
100
101 37
	/**
102 37
	 * Returns the property ids used for indexing.
103 37
	 *
104 37
	 * @since 0.2
105 37
	 *
106 37
	 * @return PropertyId[]
107
	 * @throws RuntimeException
108
	 */
109
	public function getPropertyIds() {
110
		$this->assertIndexIsBuild();
111
112
		return array_map(
113
			function( $serializedPropertyId ) {
114
				return new PropertyId( $serializedPropertyId );
115
			},
116
			array_keys( $this->byId )
117
		);
118
	}
119
120 27
	/**
121 27
	 * Returns the objects featuring the provided property id in the index.
122
	 *
123 26
	 * @since 0.2
124 1
	 *
125
	 * @param PropertyId $propertyId
126
	 *
127 25
	 * @throws OutOfBoundsException
128
	 * @throws RuntimeException
129
	 * @return PropertyIdProvider[]
130
	 */
131
	public function getByPropertyId( PropertyId $propertyId ) {
132
		$this->assertIndexIsBuild();
133
134
		if ( !( array_key_exists( $propertyId->getSerialization(), $this->byId ) ) ) {
135
			throw new OutOfBoundsException( "Object with propertyId \"$propertyId\" not found" );
136
		}
137
138
		return $this->byId[$propertyId->getSerialization()];
139 47
	}
140 47
141
	/**
142 47
	 * Returns the absolute index of an object or false if the object could not be found.
143 47
	 * @since 0.5
144 47
	 *
145 47
	 * @param PropertyIdProvider $object
146
	 *
147 40
	 * @return bool|int
148 40
	 * @throws RuntimeException
149
	 */
150
	public function getFlatArrayIndexOfObject( $object ) {
151
		$this->assertIndexIsBuild();
152
153
		$i = 0;
154
		foreach ( $this as $o ) {
155
			if ( $o === $object ) {
156
				return $i;
157
			}
158
			$i++;
159
		}
160 55
		return false;
161 55
	}
162
163 55
	/**
164 55
	 * Returns the objects in a flat array (using the indexed form for generating the array).
165 55
	 * @since 0.5
166 55
	 *
167 55
	 * @return PropertyIdProvider[]
168
	 * @throws RuntimeException
169
	 */
170
	public function toFlatArray() {
171
		$this->assertIndexIsBuild();
172
173
		$array = [];
174
		foreach ( $this->byId as $objects ) {
0 ignored issues
show
The expression $this->byId of type array<integer,array>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
175
			$array = array_merge( $array, $objects );
176
		}
177
		return $array;
178 43
	}
179 43
180
	/**
181 43
	 * Returns the absolute numeric indices of objects featuring the same property id.
182 43
	 *
183
	 * @param PropertyId $propertyId
184 43
	 *
185 41
	 * @throws RuntimeException
186 41
	 * @return int[]
187 41
	 */
188
	private function getFlatArrayIndices( PropertyId $propertyId ) {
189 29
		$this->assertIndexIsBuild();
190
191 43
		$propertyIndices = [];
192
		$i = 0;
193 43
194
		foreach ( $this->byId as $serializedPropertyId => $objects ) {
0 ignored issues
show
The expression $this->byId of type array<integer,array>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
195
			if ( $serializedPropertyId === $propertyId->getSerialization() ) {
196
				$propertyIndices = range( $i, $i + count( $objects ) - 1 );
197
				break;
198
			} else {
199
				$i += count( $objects );
200
			}
201
		}
202
203
		return $propertyIndices;
204 38
	}
205 38
206
	/**
207 38
	 * Moves an object within its "property group".
208 17
	 *
209
	 * @param PropertyIdProvider $object
210
	 * @param int $toIndex Absolute index within a "property group".
211 21
	 *
212
	 * @throws OutOfBoundsException
213 21
	 */
214 21
	private function moveObjectInPropertyGroup( $object, $toIndex ) {
215
		$currentIndex = $this->getFlatArrayIndexOfObject( $object );
216 21
217
		if ( $toIndex === $currentIndex ) {
218
			return;
219
		}
220 21
221 12
		$propertyId = $object->getPropertyId();
222 12
223 9
		$numericIndices = $this->getFlatArrayIndices( $propertyId );
224
		$lastIndex = end( $numericIndices );
225 9
226 9
		if ( $toIndex > $lastIndex + 1 || $toIndex < $numericIndices[0] ) {
227 9
			throw new OutOfBoundsException( 'Object cannot be moved to ' . $toIndex );
228 9
		}
229
230 9
		if ( $toIndex >= $lastIndex ) {
231 9
			$this->moveObjectToEndOfPropertyGroup( $object );
232
		} else {
233 21
			$this->removeObject( $object );
234
235
			$propertyGroup = array_combine(
236
				$this->getFlatArrayIndices( $propertyId ),
237
				$this->getByPropertyId( $propertyId )
238
			);
239
240 12
			$insertBefore = $propertyGroup[$toIndex];
241 12
			$this->insertObjectAtIndex( $object, $this->getFlatArrayIndexOfObject( $insertBefore ) );
0 ignored issues
show
It seems like $this->getFlatArrayIndexOfObject($insertBefore) targeting Wikibase\DataModel\ByPro...latArrayIndexOfObject() can also be of type false; however, Wikibase\DataModel\ByPro...::insertObjectAtIndex() does only seem to accept integer, did you maybe forget to handle an error condition?
Loading history...
242
		}
243
	}
244 12
245 12
	/**
246
	 * Moves an object to the end of its "property group".
247 12
	 *
248 12
	 * @param PropertyIdProvider $object
249 12
	 */
250
	private function moveObjectToEndOfPropertyGroup( $object ) {
251 12
		$this->removeObject( $object );
252 12
253
		$propertyId = $object->getPropertyId();
254 12
		$propertyIdSerialization = $propertyId->getSerialization();
255 12
256
		$propertyGroup = in_array( $propertyIdSerialization, $this->getPropertyIds() )
257
			? $this->getByPropertyId( $propertyId )
258
			: [];
259
260
		$propertyGroup[] = $object;
261
		$this->byId[$propertyIdSerialization] = $propertyGroup;
262 23
263 23
		$this->exchangeArray( $this->toFlatArray() );
264 23
	}
265 23
266 23
	/**
267 23
	 * Removes an object from the array structures.
268
	 *
269
	 * @param PropertyIdProvider $object
270
	 */
271
	private function removeObject( $object ) {
272
		$flatArray = $this->toFlatArray();
273
		$this->exchangeArray( $flatArray );
274
		$this->offsetUnset( array_search( $object, $flatArray ) );
275 9
		$this->buildIndex();
276 9
	}
277
278 9
	/**
279 9
	 * Inserts an object at a specific index.
280 9
	 *
281 9
	 * @param PropertyIdProvider $object
282 9
	 * @param int $index Absolute index within the flat list of objects.
283
	 */
284 9
	private function insertObjectAtIndex( $object, $index ) {
285 9
		$flatArray = $this->toFlatArray();
286
287
		$this->exchangeArray( array_merge(
288
			array_slice( $flatArray, 0, $index ),
289
			[ $object ],
290
			array_slice( $flatArray, $index )
291 32
		) );
292 32
293
		$this->buildIndex();
294
	}
295
296 32
	/**
297
	 * @param PropertyId $propertyId
298 32
	 * @param int $toIndex
299 32
	 */
300
	private function movePropertyGroup( PropertyId $propertyId, $toIndex ) {
301
		if ( $this->getPropertyGroupIndex( $propertyId ) === $toIndex ) {
302 32
			return;
303
		}
304 32
305
		$insertBefore = null;
306
307 17
		$oldIndex = $this->getPropertyGroupIndex( $propertyId );
308 17
		$byIdClone = $this->byId;
309
310 32
		// Remove "property group" to calculate the groups new index:
311
		unset( $this->byId[$propertyId->getSerialization()] );
312
313 32
		if ( $toIndex > $oldIndex ) {
314 24
			// If the group shall be moved towards the bottom, the number of objects within the
315 24
			// group needs to be subtracted from the absolute toIndex:
316
			$toIndex -= count( $byIdClone[$propertyId->getSerialization()] );
317 32
		}
318
319 32
		foreach ( $this->getPropertyIds() as $pId ) {
320 32
			// Accepting other than the exact index by using <= letting the "property group" "latch"
321
			// in the next slot.
322 32
			if ( $toIndex <= $this->getPropertyGroupIndex( $pId ) ) {
323 32
				$insertBefore = $pId;
324 32
				break;
325 32
			}
326 32
		}
327 24
328 24
		$serializedPropertyId = $propertyId->getSerialization();
329 32
		$this->byId = [];
330 32
331
		foreach ( $byIdClone as $serializedPId => $objects ) {
0 ignored issues
show
The expression $byIdClone of type array<integer,array>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
332 32
			$pId = new PropertyId( $serializedPId );
333 8
			if ( $pId->equals( $propertyId ) ) {
334 8
				continue;
335
			} elseif ( $pId->equals( $insertBefore ) ) {
336 32
				$this->byId[$serializedPropertyId] = $byIdClone[$serializedPropertyId];
337 32
			}
338
			$this->byId[$serializedPId] = $objects;
339
		}
340
341
		if ( $insertBefore === null ) {
342
			$this->byId[$serializedPropertyId] = $byIdClone[$serializedPropertyId];
343
		}
344
345
		$this->exchangeArray( $this->toFlatArray() );
346
	}
347 32
348 32
	/**
349
	 * Returns the index of a "property group" (the first object in the flat array that features
350 32
	 * the specified property). Returns false if property id could not be found.
351 32
	 *
352 32
	 * @param PropertyId $propertyId
353 32
	 *
354
	 * @return bool|int
355 30
	 */
356 30
	private function getPropertyGroupIndex( PropertyId $propertyId ) {
357
		$i = 0;
358
359
		foreach ( $this->byId as $serializedPropertyId => $objects ) {
0 ignored issues
show
The expression $this->byId of type array<integer,array>|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
360
			$pId = new PropertyId( $serializedPropertyId );
361
			if ( $pId->equals( $propertyId ) ) {
362
				return $i;
363
			}
364
			$i += count( $objects );
365
		}
366
367
		return false;
368
	}
369
370
	/**
371
	 * Moves an existing object to a new index. Specifying an index outside the object's "property
372
	 * group" will move the object to the edge of the "property group" and shift the whole group
373 47
	 * to achieve the designated index for the object to move.
374 47
	 * @since 0.5
375
	 *
376 47
	 * @param PropertyIdProvider $object
377 1
	 * @param int $toIndex Absolute index where to move the object to.
378 46
	 *
379 1
	 * @throws OutOfBoundsException
380 45
	 * @throws RuntimeException
381 7
	 */
382
	public function moveObjectToIndex( $object, $toIndex ) {
383
		$this->assertIndexIsBuild();
384
385 38
		if ( !in_array( $object, $this->toFlatArray() ) ) {
386
			throw new OutOfBoundsException( 'Object not present in array' );
387 38
		} elseif ( $toIndex < 0 || $toIndex > count( $this ) ) {
388 8
			throw new OutOfBoundsException( 'Specified index is out of bounds' );
389 8
		} elseif ( $this->getFlatArrayIndexOfObject( $object ) === $toIndex ) {
390 30
			return;
391 30
		}
392 30
393
		// Determine whether to simply reindex the object within its "property group":
394 30
		$propertyIndices = $this->getFlatArrayIndices( $object->getPropertyId() );
395 30
396
		if ( in_array( $toIndex, $propertyIndices ) ) {
397
			$this->moveObjectInPropertyGroup( $object, $toIndex );
398 38
		} else {
399 38
			$edgeIndex = ( $toIndex <= $propertyIndices[0] )
400
				? $propertyIndices[0]
401
				: end( $propertyIndices );
402
403
			$this->moveObjectInPropertyGroup( $object, $edgeIndex );
0 ignored issues
show
It seems like $edgeIndex defined by $toIndex <= $propertyInd...: end($propertyIndices) on line 399 can also be of type false; however, Wikibase\DataModel\ByPro...ObjectInPropertyGroup() does only seem to accept integer, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
404
			$this->movePropertyGroup( $object->getPropertyId(), $toIndex );
405
		}
406
407
		$this->exchangeArray( $this->toFlatArray() );
408
	}
409
410
	/**
411
	 * Adds an object at a specific index. If no index is specified, the object will be append to
412
	 * the end of its "property group" or - if no objects featuring the same property exist - to the
413
	 * absolute end of the array.
414 7
	 * Specifying an index outside a "property group" will place the new object at the specified
415 7
	 * index with the existing "property group" objects being shifted towards the new object.
416
	 *
417 7
	 * @since 0.5
418 7
	 *
419
	 * @param PropertyIdProvider $object
420 7
	 * @param int|null $index Absolute index where to place the new object.
421
	 *
422 2
	 * @throws OutOfBoundsException
423 7
	 * @throws RuntimeException
424
	 */
425
	public function addObjectAtIndex( $object, $index = null ) {
426 2
		$this->assertIndexIsBuild();
427 2
428 2
		$propertyId = $object->getPropertyId();
429 2
		$validIndices = $this->getFlatArrayIndices( $propertyId );
430 2
431 2
		if ( count( $this ) === 0 ) {
432
			// Array is empty, just append object.
433
			$this->append( $object );
434 3
		} elseif ( empty( $validIndices ) ) {
435
			// No objects featuring that property exist. The object may be inserted at a place
436
			// between existing "property groups".
437 7
			$this->append( $object );
438 7
			if ( $index !== null ) {
439
				$this->buildIndex();
440
				$this->moveObjectToIndex( $object, $index );
441
			}
442
		} else {
443
			// Objects featuring the same property as the object which is about to be added already
444
			// exist in the array.
445
			$this->addObjectToPropertyGroup( $object, $index );
446
		}
447
448 3
		$this->buildIndex();
449
	}
450 3
451 3
	/**
452
	 * Adds an object to an existing property group at the specified absolute index.
453 3
	 *
454
	 * @param PropertyIdProvider $object
455
	 * @param int|null $index
456
	 *
457
	 * @throws OutOfBoundsException
458 3
	 */
459
	private function addObjectToPropertyGroup( $object, $index = null ) {
460 3
		$propertyId = $object->getPropertyId();
461
		$validIndices = $this->getFlatArrayIndices( $propertyId );
462 1
463 1
		if ( empty( $validIndices ) ) {
464
			throw new OutOfBoundsException( 'No objects featuring the object\'s property exist' );
465 3
		}
466
467 1
		// Add index to allow placing object after the last object of the "property group":
468 1
		$validIndices[] = end( $validIndices ) + 1;
469 1
470
		if ( $index === null ) {
471 1
			// If index is null, append object to "property group".
472
			$index = end( $validIndices );
473 2
		}
474
475
		if ( in_array( $index, $validIndices ) ) {
476
			// Add object at index within "property group".
477 2
			$this->byId[$propertyId->getSerialization()][] = $object;
478 2
			$this->exchangeArray( $this->toFlatArray() );
479 2
			$this->moveObjectToIndex( $object, $index );
0 ignored issues
show
It seems like $index can also be of type double or false; however, Wikibase\DataModel\ByPro...ay::moveObjectToIndex() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
480
481
		} else {
482
			// Index is out of the "property group"; The whole group needs to be moved.
483
			$this->movePropertyGroup( $propertyId, $index );
0 ignored issues
show
It seems like $index can also be of type double or false; however, Wikibase\DataModel\ByPro...ay::movePropertyGroup() does only seem to accept integer, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
484 3
485 3
			// Move new object to the edge of the "property group" to receive its designated
486
			// index:
487
			if ( $index < $validIndices[0] ) {
488
				array_unshift( $this->byId[$propertyId->getSerialization()], $object );
489
			} else {
490
				$this->byId[$propertyId->getSerialization()][] = $object;
491
			}
492
		}
493
494
		$this->exchangeArray( $this->toFlatArray() );
495
	}
496
497
}
498