Completed
Push — master ( e62317...cd1faf )
by adam
03:51 queued 01:43
created

ReferenceList::count()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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
 */
30
class ReferenceList implements Comparable, Countable, IteratorAggregate, Serializable {
31
32
	/**
33
	 * @var Reference[] Ordered list or references, indexed by SPL object hash.
34 29
	 */
35 29
	private $references = [];
36 8
37
	/**
38
	 * @param Reference[]|Traversable $references
39 21
	 *
40 12
	 * @throws InvalidArgumentException
41 4
	 */
42
	public function __construct( $references = [] ) {
43
		if ( !is_array( $references ) && !( $references instanceof Traversable ) ) {
44 8
			throw new InvalidArgumentException( '$references must be an array or an instance of Traversable' );
45 17
		}
46 17
47
		foreach ( $references as $reference ) {
48
			if ( !( $reference instanceof Reference ) ) {
49
				throw new InvalidArgumentException( 'Every element in $references must be an instance of Reference' );
50
			}
51
52
			$this->addReference( $reference );
53
		}
54
	}
55
56
	/**
57
	 * Adds the provided reference to the list.
58 14
	 * Empty references are ignored.
59 14
	 *
60
	 * @since 0.1
61
	 *
62
	 * @param Reference $reference
63 14
	 * @param int|null $index New position of the added reference, or null to append.
64
	 *
65 14
	 * @throws InvalidArgumentException
66 14
	 */
67 2
	public function addReference( Reference $reference, $index = null ) {
68
		if ( $index !== null && ( !is_int( $index ) || $index < 0 ) ) {
69 14
			throw new InvalidArgumentException( '$index must be a non-negative integer or null' );
70
		}
71
72
		if ( $reference->isEmpty() ) {
73
			return;
74
		}
75
76
		$splHash = spl_object_hash( $reference );
77 14
78 14
		if ( array_key_exists( $splHash, $this->references ) ) {
79 14
			return;
80 14
		}
81 14
82
		if ( $index === null || $index >= count( $this->references ) ) {
83
			// Append object to the end of the reference list.
84
			$this->references[$splHash] = $reference;
85
		} else {
86
			$this->insertReferenceAtIndex( $reference, $index );
87
		}
88
	}
89
90
	/**
91 6
	 * @since 1.1
92 6
	 *
93 5
	 * @param Snak[]|Snak $snaks
94 5
	 * @param Snak [$snak2,...]
95
	 *
96 6
	 * @throws InvalidArgumentException
97 5
	 */
98
	public function addNewReference( $snaks = [] /*...*/ ) {
99
		if ( $snaks instanceof Snak ) {
100
			$snaks = func_get_args();
101
		}
102
103 2
		$this->addReference( new Reference( $snaks ) );
104 2
	}
105 2
106
	/**
107
	 * @param Reference $reference
108 2
	 * @param int $index
109 2
	 */
110 2
	private function insertReferenceAtIndex( Reference $reference, $index ) {
111 2
		if ( !is_int( $index ) ) {
112 2
			throw new InvalidArgumentException( '$index must be an integer' );
113
		}
114 2
115 2
		$splHash = spl_object_hash( $reference );
116 2
117
		$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...
118
			array_slice( $this->references, 0, $index ),
119 2
			[ $splHash => $reference ],
120
			array_slice( $this->references, $index )
121 2
		);
122 2
	}
123 2
124 2
	/**
125
	 * Returns if the list contains a reference with the same hash as the provided reference.
126
	 *
127
	 * @since 0.1
128
	 *
129
	 * @param Reference $reference
130
	 *
131
	 * @return bool
132
	 */
133
	public function hasReference( Reference $reference ) {
134
		return $this->hasReferenceHash( $reference->getHash() );
135 6
	}
136 6
137 6
	/**
138
	 * Returns the index of the Reference object or false if the Reference could not be found.
139
	 *
140
	 * @since 0.5
141
	 *
142
	 * @param Reference $reference
143
	 *
144
	 * @return int|bool
145
	 */
146
	public function indexOf( Reference $reference ) {
147
		$index = 0;
148
149 2
		foreach ( $this->references as $ref ) {
150 2
			if ( $ref === $reference ) {
151
				return $index;
152 2
			}
153 1
154 1
			$index++;
155
		}
156 1
157 2
		return false;
158
	}
159 2
160
	/**
161
	 * Removes the reference with the same hash as the provided reference if such a reference exists in the list.
162
	 *
163
	 * @since 0.1
164
	 *
165
	 * @param Reference $reference
166
	 */
167
	public function removeReference( Reference $reference ) {
168
		$this->removeReferenceHash( $reference->getHash() );
169 3
	}
170 3
171 3
	/**
172
	 * Returns if the list contains a reference with the provided hash.
173
	 *
174
	 * @since 0.3
175
	 *
176
	 * @param string $referenceHash
177
	 *
178
	 * @return bool
179
	 */
180
	public function hasReferenceHash( $referenceHash ) {
181
		return $this->getReference( $referenceHash ) !== null;
182 8
	}
183 8
184
	/**
185
	 * Looks for the first Reference object in this list with the provided hash.
186
	 * Removes all occurences of that object.
187
	 *
188
	 * @since 0.3
189
	 *
190
	 * @param string $referenceHash	`
191
	 */
192
	public function removeReferenceHash( $referenceHash ) {
193 5
		$reference = $this->getReference( $referenceHash );
194 5
195
		if ( $reference === null ) {
196 5
			return;
197 3
		}
198 3
199 5
		foreach ( $this->references as $splObjectHash => $ref ) {
200
			if ( $ref === $reference ) {
201
				unset( $this->references[$splObjectHash] );
202
			}
203
		}
204
	}
205
206
	/**
207
	 * Returns the first Reference object with the provided hash, or
208
	 * null if there is no such reference in the list.
209
	 *
210 14
	 * @since 0.3
211
	 *
212
	 * @param string $referenceHash
213
	 *
214 14
	 * @return Reference|null
215 10
	 */
216 10
	public function getReference( $referenceHash ) {
217
		foreach ( $this->references as $reference ) {
218 9
			if ( $reference->getHash() === $referenceHash ) {
219
				return $reference;
220 9
			}
221
		}
222
223
		return null;
224
	}
225
226
	/**
227
	 * @see Serializable::serialize
228
	 *
229
	 * @since 2.1
230 3
	 *
231 3
	 * @return string
232
	 */
233
	public function serialize() {
234
		return serialize( array_values( $this->references ) );
235
	}
236
237
	/**
238
	 * @see https://wiki.php.net/rfc/custom_object_serialization
239
	 *
240
	 * @return array
241 3
	 */
242 3
	public function __serialize() {
243 3
		return [
244
			'references' => array_values( $this->references )
245
		];
246
	}
247
248
	/**
249
	 * @see https://wiki.php.net/rfc/custom_object_serialization
250 3
	 *
251 3
	 * @param array $data
252
	 */
253
	public function __unserialize( array $data ) : void {
254
		$this->references = $data['references'];
255
	}
256
257
	/**
258
	 * @see Serializable::unserialize
259
	 *
260
	 * @since 2.1
261
	 *
262
	 * @param string $serialized
263
	 */
264
	public function unserialize( $serialized ) {
265
		$this->__construct( unserialize( $serialized ) );
266
	}
267
268
	/**
269
	 * @since 4.4
270
	 *
271
	 * @return bool
272
	 */
273
	public function isEmpty() {
274
		return empty( $this->references );
275
	}
276
277
	/**
278
	 * The hash is purely valuer based. Order of the elements in the array is not held into account.
279
	 *
280
	 * @since 0.3
281
	 *
282
	 * @return string
283
	 */
284
	public function getValueHash() {
285
		$hasher = new MapValueHasher();
286
		return $hasher->hash( $this->references );
287
	}
288
289
	/**
290
	 * @see Comparable::equals
291
	 *
292
	 * The comparison is done purely value based, ignoring the order of the elements in the array.
293
	 *
294
	 * @since 0.3
295
	 *
296
	 * @param mixed $target
297
	 *
298
	 * @return bool
299
	 */
300
	public function equals( $target ) {
301
		if ( $this === $target ) {
302
			return true;
303
		}
304
305
		return $target instanceof self
306
			&& $this->getValueHash() === $target->getValueHash();
307
	}
308
309
	/**
310
	 * @see Countable::count
311
	 *
312
	 * @return int
313
	 */
314
	public function count() {
315
		return count( $this->references );
316
	}
317
318
	/**
319
	 * @see IteratorAggregate::getIterator
320
	 *
321
	 * @since 5.0
322
	 *
323
	 * @return Iterator|Reference[]
324
	 */
325
	public function getIterator() {
326
		return new ArrayIterator( array_values( $this->references ) );
327
	}
328
329
}
330