Passed
Push — int32EntityId ( fa80fb )
by no
05:13
created

ReferenceList::insertReferenceAtIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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