Completed
Pull Request — master (#824)
by
unknown
17:59
created

ReferenceList::addNewReference()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 7
ccs 3
cts 3
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Wikibase\DataModel;
4
5
use ArrayIterator;
6
use Comparable;
7
use Countable;
8
use InvalidArgumentException;
9
use IteratorAggregate;
10
use Serializable;
11
use Traversable;
12
use Wikibase\DataModel\Internal\MapValueHasher;
13
use Wikibase\DataModel\Snak\Snak;
14
15
/**
16
 * List of Reference objects.
17
 *
18
 * @since 0.1
19
 * Does not implement References anymore since 2.0
20
 * Does not extend SplObjectStorage since 5.0
21
 *
22
 * @license GPL-2.0-or-later
23
 * @author Jeroen De Dauw < [email protected] >
24
 * @author H. Snater < [email protected] >
25
 * @author Thiemo Kreuz
26
 * @author Bene* < [email protected] >
27
 *
28
 * @phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName
29
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
30
 */
31
class ReferenceList implements Comparable, Countable, IteratorAggregate, Serializable {
32
33
	/**
34 29
	 * @var Reference[] Ordered list or references, indexed by SPL object hash.
35 29
	 */
36 8
	private $references = [];
37
38
	/**
39 21
	 * @param Reference[]|Traversable $references
40 12
	 *
41 4
	 * @throws InvalidArgumentException
42
	 */
43
	public function __construct( $references = [] ) {
44 8
		if ( !is_array( $references ) && !( $references instanceof Traversable ) ) {
45 17
			throw new InvalidArgumentException( '$references must be an array or an instance of Traversable' );
46 17
		}
47
48
		foreach ( $references as $reference ) {
49
			if ( !( $reference instanceof Reference ) ) {
50
				throw new InvalidArgumentException( 'Every element in $references must be an instance of Reference' );
51
			}
52
53
			$this->addReference( $reference );
54
		}
55
	}
56
57
	/**
58 14
	 * Adds the provided reference to the list.
59 14
	 * Empty references are ignored.
60
	 *
61
	 * @since 0.1
62
	 *
63 14
	 * @param Reference $reference
64
	 * @param int|null $index New position of the added reference, or null to append.
65 14
	 *
66 14
	 * @throws InvalidArgumentException
67 2
	 */
68
	public function addReference( Reference $reference, $index = null ) {
69 14
		if ( $index !== null && ( !is_int( $index ) || $index < 0 ) ) {
70
			throw new InvalidArgumentException( '$index must be a non-negative integer or null' );
71
		}
72
73
		if ( $reference->isEmpty() ) {
74
			return;
75
		}
76
77 14
		$splHash = spl_object_hash( $reference );
78 14
79 14
		if ( array_key_exists( $splHash, $this->references ) ) {
80 14
			return;
81 14
		}
82
83
		if ( $index === null || $index >= count( $this->references ) ) {
84
			// Append object to the end of the reference list.
85
			$this->references[$splHash] = $reference;
86
		} else {
87
			$this->insertReferenceAtIndex( $reference, $index );
88
		}
89
	}
90
91 6
	/**
92 6
	 * @since 1.1
93 5
	 *
94 5
	 * @param Snak[]|Snak $snaks
95
	 * @param Snak [$snak2,...]
96 6
	 *
97 5
	 * @throws InvalidArgumentException
98
	 */
99
	public function addNewReference( $snaks = [] /*...*/ ) {
100
		if ( $snaks instanceof Snak ) {
101
			$snaks = func_get_args();
102
		}
103 2
104 2
		$this->addReference( new Reference( $snaks ) );
105 2
	}
106
107
	/**
108 2
	 * @param Reference $reference
109 2
	 * @param int $index
110 2
	 */
111 2
	private function insertReferenceAtIndex( Reference $reference, $index ) {
112 2
		if ( !is_int( $index ) ) {
113
			throw new InvalidArgumentException( '$index must be an integer' );
114 2
		}
115 2
116 2
		$splHash = spl_object_hash( $reference );
117
118
		$this->references = array_merge(
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge(array_slice(...s->references, $index)) of type array<integer|string,obj...e\DataModel\Reference>> is incompatible with the declared type array<integer,object<Wik...e\DataModel\Reference>> of property $references.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
119 2
			array_slice( $this->references, 0, $index ),
120
			[ $splHash => $reference ],
121 2
			array_slice( $this->references, $index )
122 2
		);
123 2
	}
124 2
125
	/**
126
	 * Returns if the list contains a reference with the same hash as the provided reference.
127
	 *
128
	 * @since 0.1
129
	 *
130
	 * @param Reference $reference
131
	 *
132
	 * @return bool
133
	 */
134
	public function hasReference( Reference $reference ) {
135 6
		return $this->hasReferenceHash( $reference->getHash() );
136 6
	}
137 6
138
	/**
139
	 * Returns the index of the Reference object or false if the Reference could not be found.
140
	 *
141
	 * @since 0.5
142
	 *
143
	 * @param Reference $reference
144
	 *
145
	 * @return int|bool
146
	 */
147
	public function indexOf( Reference $reference ) {
148
		$index = 0;
149 2
150 2
		foreach ( $this->references as $ref ) {
151
			if ( $ref === $reference ) {
152 2
				return $index;
153 1
			}
154 1
155
			$index++;
156 1
		}
157 2
158
		return false;
159 2
	}
160
161
	/**
162
	 * Removes the reference with the same hash as the provided reference if such a reference exists in the list.
163
	 *
164
	 * @since 0.1
165
	 *
166
	 * @param Reference $reference
167
	 */
168
	public function removeReference( Reference $reference ) {
169 3
		$this->removeReferenceHash( $reference->getHash() );
170 3
	}
171 3
172
	/**
173
	 * Returns if the list contains a reference with the provided hash.
174
	 *
175
	 * @since 0.3
176
	 *
177
	 * @param string $referenceHash
178
	 *
179
	 * @return bool
180
	 */
181
	public function hasReferenceHash( $referenceHash ) {
182 8
		return $this->getReference( $referenceHash ) !== null;
183 8
	}
184
185
	/**
186
	 * Looks for the first Reference object in this list with the provided hash.
187
	 * Removes all occurences of that object.
188
	 *
189
	 * @since 0.3
190
	 *
191
	 * @param string $referenceHash	`
192
	 */
193 5
	public function removeReferenceHash( $referenceHash ) {
194 5
		$reference = $this->getReference( $referenceHash );
195
196 5
		if ( $reference === null ) {
197 3
			return;
198 3
		}
199 5
200
		foreach ( $this->references as $splObjectHash => $ref ) {
201
			if ( $ref === $reference ) {
202
				unset( $this->references[$splObjectHash] );
203
			}
204
		}
205
	}
206
207
	/**
208
	 * Returns the first Reference object with the provided hash, or
209
	 * null if there is no such reference in the list.
210 14
	 *
211
	 * @since 0.3
212
	 *
213
	 * @param string $referenceHash
214 14
	 *
215 10
	 * @return Reference|null
216 10
	 */
217
	public function getReference( $referenceHash ) {
218 9
		foreach ( $this->references as $reference ) {
219
			if ( $reference->getHash() === $referenceHash ) {
220 9
				return $reference;
221
			}
222
		}
223
224
		return null;
225
	}
226
227
	/**
228
	 * @see Serializable::serialize
229
	 *
230 3
	 * @since 2.1
231 3
	 *
232
	 * @return string
233
	 */
234
	public function serialize() {
235
		return serialize( array_values( $this->references ) );
236
	}
237
238
	/**
239
	 * @see https://wiki.php.net/rfc/custom_object_serialization
240
	 *
241 3
	 * @return array
242 3
	 */
243 3
	public function __serialize() {
244
		return [
245
			'references' => array_values( $this->references )
246
		];
247
	}
248
249
	/**
250 3
	 * @see https://wiki.php.net/rfc/custom_object_serialization
251 3
	 *
252
	 * @param array $data
253
	 */
254
	public function __unserialize( array $data ) : void {
255
		$this->references = $data['references'];
256
	}
257
258
	/**
259
	 * @see Serializable::unserialize
260
	 *
261
	 * @since 2.1
262
	 *
263
	 * @param string $serialized
264
	 */
265
	public function unserialize( $serialized ) {
266
		$this->__construct( unserialize( $serialized ) );
267
	}
268
269
	/**
270
	 * @since 4.4
271
	 *
272
	 * @return bool
273
	 */
274
	public function isEmpty() {
275
		return empty( $this->references );
276
	}
277
278
	/**
279
	 * The hash is purely valuer based. Order of the elements in the array is not held into account.
280
	 *
281
	 * @since 0.3
282
	 *
283
	 * @return string
284
	 */
285
	public function getValueHash() {
286
		$hasher = new MapValueHasher();
287
		return $hasher->hash( $this->references );
288
	}
289
290
	/**
291
	 * @see Comparable::equals
292
	 *
293
	 * The comparison is done purely value based, ignoring the order of the elements in the array.
294
	 *
295
	 * @since 0.3
296
	 *
297
	 * @param mixed $target
298
	 *
299
	 * @return bool
300
	 */
301
	public function equals( $target ) {
302
		if ( $this === $target ) {
303
			return true;
304
		}
305
306
		return $target instanceof self
307
			&& $this->getValueHash() === $target->getValueHash();
308
	}
309
310
	/**
311
	 * @see Countable::count
312
	 *
313
	 * @return int
314
	 */
315
	public function count() {
316
		return count( $this->references );
317
	}
318
319
	/**
320
	 * @see IteratorAggregate::getIterator
321
	 *
322
	 * @since 5.0
323
	 *
324
	 * @return Iterator|Reference[]
325
	 */
326
	public function getIterator() {
327
		return new ArrayIterator( array_values( $this->references ) );
328
	}
329
330
}
331