Passed
Push — rm-hasharray ( ce1b72...75dec0 )
by no
07:00 queued 03:29
created

ReferenceList   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 39
c 2
b 0
f 1
lcom 1
cbo 2
dl 0
loc 280
rs 8.2857

17 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 13 5
C addReference() 0 22 8
A addNewReference() 0 7 2
A insertReferenceAtIndex() 0 13 2
A hasReference() 0 3 1
A indexOf() 0 13 3
A removeReference() 0 3 1
A hasReferenceHash() 0 3 1
A removeReferenceHash() 0 13 4
A getReference() 0 9 3
A serialize() 0 3 1
A unserialize() 0 3 1
A isEmpty() 0 3 1
A getValueHash() 0 4 1
A equals() 0 8 3
A count() 0 3 1
A getIterator() 0 3 1
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 = [];
34
35
	/**
36
	 * @param Reference[]|Traversable $references
37
	 *
38
	 * @throws InvalidArgumentException
39
	 */
40
	public function __construct( $references = [] ) {
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 New position of the added reference, or null to append.
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
		$splHash = spl_object_hash( $reference );
75
76
		if ( array_key_exists( $splHash, $this->references ) ) {
77
			return;
78
		}
79
80
		if ( $index === null || $index >= count( $this->references ) ) {
81
			// Append object to the end of the reference list.
82
			$this->references[$splHash] = $reference;
83
		} else {
84
			$this->insertReferenceAtIndex( $reference, $index );
85
		}
86
	}
87
88
	/**
89
	 * @since 1.1
90
	 *
91
	 * @param Snak[]|Snak $snaks
92
	 * @param Snak [$snak2,...]
93
	 *
94
	 * @throws InvalidArgumentException
95
	 */
96
	public function addNewReference( $snaks = [] /*...*/ ) {
97
		if ( $snaks instanceof Snak ) {
98
			$snaks = func_get_args();
99
		}
100
101
		$this->addReference( new Reference( $snaks ) );
102
	}
103
104
	/**
105
	 * @param Reference $reference
106
	 * @param int $index
107
	 */
108
	private function insertReferenceAtIndex( Reference $reference, $index ) {
109
		if ( !is_int( $index ) ) {
110
			throw new InvalidArgumentException( '$index must be an integer' );
111
		}
112
113
		$splHash = spl_object_hash( $reference );
114
115
		$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...
116
			array_slice( $this->references, 0, $index ),
117
			[ $splHash => $reference ],
118
			array_slice( $this->references, $index )
119
		);
120
	}
121
122
	/**
123
	 * Returns if the list contains a reference with the same hash as the provided reference.
124
	 *
125
	 * @since 0.1
126
	 *
127
	 * @param Reference $reference
128
	 *
129
	 * @return bool
130
	 */
131
	public function hasReference( Reference $reference ) {
132
		return $this->hasReferenceHash( $reference->getHash() );
133
	}
134
135
	/**
136
	 * Returns the index of the Reference object or false if the Reference could not be found.
137
	 *
138
	 * @since 0.5
139
	 *
140
	 * @param Reference $reference
141
	 *
142
	 * @return int|bool
143
	 */
144
	public function indexOf( Reference $reference ) {
145
		$index = 0;
146
147
		foreach ( $this->references as $ref ) {
148
			if ( $ref === $reference ) {
149
				return $index;
150
			}
151
152
			$index++;
153
		}
154
155
		return false;
156
	}
157
158
	/**
159
	 * Removes the reference with the same hash as the provided reference if such a reference exists in the list.
160
	 *
161
	 * @since 0.1
162
	 *
163
	 * @param Reference $reference
164
	 */
165
	public function removeReference( Reference $reference ) {
166
		$this->removeReferenceHash( $reference->getHash() );
167
	}
168
169
	/**
170
	 * Returns if the list contains a reference with the provided hash.
171
	 *
172
	 * @since 0.3
173
	 *
174
	 * @param string $referenceHash
175
	 *
176
	 * @return bool
177
	 */
178
	public function hasReferenceHash( $referenceHash ) {
179
		return $this->getReference( $referenceHash ) !== null;
180
	}
181
182
	/**
183
	 * Looks for the first Reference object in this list with the provided hash.
184
	 * Removes all occurences of that object.
185
	 *
186
	 * @since 0.3
187
	 *
188
	 * @param string $referenceHash	`
189
	 */
190
	public function removeReferenceHash( $referenceHash ) {
191
		$reference = $this->getReference( $referenceHash );
192
193
		if ( $reference === null ) {
194
			return;
195
		}
196
197
		foreach ( $this->references as $splObjectHash => $ref ) {
198
			if ( $ref === $reference ) {
199
				unset( $this->references[$splObjectHash] );
200
			}
201
		}
202
	}
203
204
	/**
205
	 * Returns the first Reference object with the provided hash, or
206
	 * null if there is no such reference in the list.
207
	 *
208
	 * @since 0.3
209
	 *
210
	 * @param string $referenceHash
211
	 *
212
	 * @return Reference|null
213
	 */
214
	public function getReference( $referenceHash ) {
215
		foreach ( $this->references as $reference ) {
216
			if ( $reference->getHash() === $referenceHash ) {
217
				return $reference;
218
			}
219
		}
220
221
		return null;
222
	}
223
224
	/**
225
	 * @see Serializable::serialize
226
	 *
227
	 * @since 2.1
228
	 *
229
	 * @return string
230
	 */
231
	public function serialize() {
232
		return serialize( array_values( $this->references ) );
233
	}
234
235
	/**
236
	 * @see Serializable::unserialize
237
	 *
238
	 * @since 2.1
239
	 *
240
	 * @param string $serialized
241
	 */
242
	public function unserialize( $serialized ) {
243
		$this->__construct( unserialize( $serialized ) );
244
	}
245
246
	/**
247
	 * @since 4.4
248
	 *
249
	 * @return bool
250
	 */
251
	public function isEmpty() {
252
		return empty( $this->references );
253
	}
254
255
	/**
256
	 * The hash is purely valuer based. Order of the elements in the array is not held into account.
257
	 *
258
	 * @since 0.3
259
	 *
260
	 * @return string
261
	 */
262
	public function getValueHash() {
263
		$hasher = new MapValueHasher();
264
		return $hasher->hash( $this->references );
265
	}
266
267
	/**
268
	 * @see Comparable::equals
269
	 *
270
	 * The comparison is done purely value based, ignoring the order of the elements in the array.
271
	 *
272
	 * @since 0.3
273
	 *
274
	 * @param mixed $target
275
	 *
276
	 * @return bool
277
	 */
278
	public function equals( $target ) {
279
		if ( $this === $target ) {
280
			return true;
281
		}
282
283
		return $target instanceof self
284
		       && $this->getValueHash() === $target->getValueHash();
285
	}
286
287
	/**
288
	 * @see Countable::count
289
	 *
290
	 * @return int
291
	 */
292
	public function count() {
293
		return count( $this->references );
294
	}
295
296
	/**
297
	 * @see IteratorAggregate::getIterator
298
	 *
299
	 * @since 5.0
300
	 *
301
	 * @return Traversable
302
	 */
303
	public function getIterator() {
304
		return new ArrayIterator( array_values( $this->references ) );
305
	}
306
307
}
308