Completed
Push — constructors3 ( 18c935...3f87c3 )
by no
06:01 queued 02:46
created

ReferenceList   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 277
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

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

17 Methods

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