Completed
Pull Request — master (#654)
by no
06:00 queued 02:49
created

ReferenceList::addReference()   D

Complexity

Conditions 10
Paths 8

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 10

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 26
ccs 9
cts 9
cp 1
rs 4.8196
cc 10
eloc 14
nc 8
nop 2
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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