Test Setup Failed
Push — master ( 3fcd2d...fb41c8 )
by
unknown
01:02 queued 11s
created

StatementList::__construct()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 11
cts 11
cp 1
rs 8.4444
c 0
b 0
f 0
cc 8
nc 8
nop 1
crap 8
1
<?php
2
3
namespace Wikibase\DataModel\Statement;
4
5
use ArrayIterator;
6
use Comparable;
7
use Countable;
8
use InvalidArgumentException;
9
use IteratorAggregate;
10
use Traversable;
11
use Wikibase\DataModel\Entity\PropertyId;
12
use Wikibase\DataModel\Reference;
13
use Wikibase\DataModel\ReferenceList;
14
use Wikibase\DataModel\Snak\Snak;
15
use Wikibase\DataModel\Snak\SnakList;
16
17
/**
18
 * Ordered and non-unique collection of Statement objects.
19
 * Provides various filter operations.
20
 *
21
 * Does not do any indexing by default.
22
 * Does not provide complex modification functionality.
23
 *
24
 * @since 1.0
25
 *
26
 * @license GPL-2.0-or-later
27
 * @author Jeroen De Dauw < [email protected] >
28
 * @author Bene* < [email protected] >
29
 * @author Thiemo Kreuz
30
 */
31
class StatementList implements IteratorAggregate, Comparable, Countable {
32
33
	/**
34
	 * @var Statement[]
35
	 */
36
	private $statements = [];
37
38
	/**
39
	 * @param Statement ...$statements
40
	 * (passing a single Statement[] or Traversable is still supported but deprecated)
41
	 *
42
	 * @throws InvalidArgumentException
43 50
	 */
44 50
	public function __construct( ...$statements ) {
45 32
		if ( count( $statements ) === 1 && (
46 32
			is_array( $statements[0] ) || $statements[0] instanceof Traversable
47
		) ) {
48 50
			// TODO stop supporting this
49 1
			$statements = $statements[0];
50
		}
51
52 49
		if ( !is_array( $statements ) && !( $statements instanceof Traversable ) ) {
53 38
			throw new InvalidArgumentException( '$statements must be an array or an instance of Traversable' );
54 2
		}
55
56
		foreach ( $statements as $statement ) {
57 38
			if ( !( $statement instanceof Statement ) ) {
58 49
				throw new InvalidArgumentException( 'Every element in $statements must be an instance of Statement' );
59 47
			}
60
61
			$this->statements[] = $statement;
62
		}
63
	}
64
65
	/**
66
	 * Returns the property ids used by the statements.
67 2
	 * The keys of the returned array hold the serializations of the property ids.
68 2
	 *
69
	 * @return PropertyId[] Array indexed by property id serialization.
70 2
	 */
71 1
	public function getPropertyIds() {
72 2
		$propertyIds = [];
73
74 2
		foreach ( $this->statements as $statement ) {
75
			$propertyIds[$statement->getPropertyId()->getSerialization()] = $statement->getPropertyId();
76
		}
77 11
78 11
		return $propertyIds;
79 11
	}
80
81
	/**
82
	 * @since 1.0, setting an index is supported since 6.1
83
	 * @see ReferenceList::addReference
84
	 *
85
	 * @param Statement $statement
86
	 * @param int|null $index New position of the added statement, or null to append.
87 7
	 *
88 7
	 * @throws InvalidArgumentException
89 7
	 */
90
	public function addStatement( Statement $statement, $index = null ) {
91 7
		if ( $index === null ) {
92 7
			$this->statements[] = $statement;
93
		} elseif ( is_int( $index ) && $index >= 0 ) {
94 7
			array_splice( $this->statements, $index, 0, [ $statement ] );
95 7
		} else {
96
			throw new InvalidArgumentException( '$index must be a non-negative integer or null' );
97
		}
98
	}
99
100
	/**
101
	 * @param Snak $mainSnak
102 4
	 * @param Snak[]|SnakList|null $qualifiers
103 4
	 * @param Reference[]|ReferenceList|null $references
104 4
	 * @param string|null $guid
105 3
	 */
106 3
	public function addNewStatement( Snak $mainSnak, $qualifiers = null, $references = null, $guid = null ) {
107 4
		$qualifiers = is_array( $qualifiers ) ? new SnakList( $qualifiers ) : $qualifiers;
108
		$references = is_array( $references ) ? new ReferenceList( $references ) : $references;
109 4
110 4
		$statement = new Statement( $mainSnak, $qualifiers, $references );
111
		$statement->setGuid( $guid );
112
113
		$this->statements[] = $statement;
114
	}
115
116
	/**
117
	 * @since 3.0
118
	 *
119
	 * @param string|null $guid
120 1
	 */
121 1
	public function removeStatementsWithGuid( $guid ) {
122
		foreach ( $this->statements as $index => $statement ) {
123 1
			if ( $statement->getGuid() === $guid ) {
124 1
				unset( $this->statements[$index] );
125 1
			}
126
		}
127 1
128
		$this->statements = array_values( $this->statements );
129
	}
130
131
	/**
132
	 * Statements that have a main snak already in the list are filtered out.
133
	 * The last occurrences are retained.
134
	 *
135
	 * @since 1.0
136
	 *
137 2
	 * @return self
138 2
	 */
139
	public function getWithUniqueMainSnaks() {
140 2
		$statements = [];
141 2
142 1
		foreach ( $this->statements as $statement ) {
143 1
			$statements[$statement->getMainSnak()->getHash()] = $statement;
144 2
		}
145
146 2
		return new self( $statements );
147
	}
148
149
	/**
150
	 * @since 3.0
151
	 *
152
	 * @param PropertyId $id
153
	 *
154
	 * @return self
155
	 */
156 6
	public function getByPropertyId( PropertyId $id ) {
157 6
		$statementList = new self();
158 6
159
		foreach ( $this->statements as $statement ) {
160 6
			if ( $statement->getPropertyId()->equals( $id ) ) {
161 4
				$statementList->statements[] = $statement;
162 3
			}
163 3
		}
164 6
165
		return $statementList;
166 6
	}
167
168
	/**
169
	 * @since 3.0
170
	 *
171
	 * @param int|int[] $acceptableRanks
172
	 *
173
	 * @return self
174
	 */
175
	public function getByRank( $acceptableRanks ) {
176
		$acceptableRanks = array_flip( (array)$acceptableRanks );
177
		$statementList = new self();
178 4
179 4
		foreach ( $this->statements as $statement ) {
180
			if ( array_key_exists( $statement->getRank(), $acceptableRanks ) ) {
181 4
				$statementList->statements[] = $statement;
182 1
			}
183
		}
184
185 3
		return $statementList;
186
	}
187
188
	/**
189
	 * Returns the so called "best statements".
190
	 * If there are preferred statements, then this is all the preferred statements.
191
	 * If there are no preferred statements, then this is all normal statements.
192
	 *
193
	 * @since 2.4
194
	 *
195
	 * @return self
196
	 */
197
	public function getBestStatements() {
198
		$statements = $this->getByRank( Statement::RANK_PREFERRED );
199 1
200 1
		if ( !$statements->isEmpty() ) {
201
			return $statements;
202 1
		}
203 1
204 1
		return $this->getByRank( Statement::RANK_NORMAL );
205 1
	}
206 1
207
	/**
208 1
	 * Returns a list of all Snaks on this StatementList. This includes at least the main snaks of
209
	 * all statements, the snaks from qualifiers, and the snaks from references.
210
	 *
211
	 * This is a convenience method for use in code that needs to operate on all snaks, e.g.
212
	 * to find all referenced Entities.
213
	 *
214
	 * @since 1.1
215
	 *
216 1
	 * @return Snak[] Numerically indexed (non-sparse) array.
217 1
	 */
218
	public function getAllSnaks() {
219 1
		$snaks = [];
220 1
221 1
		foreach ( $this->statements as $statement ) {
222
			foreach ( $statement->getAllSnaks() as $snak ) {
223 1
				$snaks[] = $snak;
224
			}
225
		}
226
227
		return $snaks;
228
	}
229 1
230 1
	/**
231
	 * @since 2.3
232
	 *
233
	 * @return Snak[] Numerically indexed (non-sparse) array.
234
	 */
235
	public function getMainSnaks() {
236 4
		$snaks = [];
237 4
238
		foreach ( $this->statements as $statement ) {
239
			$snaks[] = $statement->getMainSnak();
240
		}
241
242
		return $snaks;
243
	}
244
245 12
	/**
246 12
	 * @return Iterator|Statement[]
247
	 */
248
	public function getIterator() {
249
		return new ArrayIterator( $this->statements );
250
	}
251
252
	/**
253
	 * @return Statement[] Numerically indexed (non-sparse) array.
254
	 */
255
	public function toArray() {
256 8
		return $this->statements;
257 8
	}
258
259
	/**
260
	 * @see Countable::count
261 8
	 *
262 8
	 * @return int
263 8
	 */
264 2
	public function count() {
265
		return count( $this->statements );
266
	}
267 6
268
	/**
269
	 * @see Comparable::equals
270 6
	 *
271 6
	 * @param mixed $target
272
	 *
273 6
	 * @return bool
274 5
	 */
275 3
	public function equals( $target ) {
276
		if ( $this === $target ) {
277
			return true;
278 5
		}
279 6
280
		if ( !( $target instanceof self )
281 3
			|| $this->count() !== $target->count()
282
		) {
283
			return false;
284
		}
285
286
		return $this->statementsEqual( $target->statements );
287 6
	}
288 6
289
	private function statementsEqual( array $statements ) {
290
		reset( $statements );
291
292
		foreach ( $this->statements as $statement ) {
293
			if ( !$statement->equals( current( $statements ) ) ) {
294
				return false;
295
			}
296
297
			next( $statements );
298
		}
299 5
300 5
		return true;
301 3
	}
302 3
303
	/**
304 4
	 * @return bool
305
	 */
306 2
	public function isEmpty() {
307
		return empty( $this->statements );
308
	}
309
310
	/**
311
	 * @since 3.0
312
	 * @see StatementByGuidMap
313
	 *
314
	 * @param string|null $statementGuid
315
	 *
316 1
	 * @return Statement|null The first statement with the given GUID or null if not found.
317 1
	 */
318
	public function getFirstStatementWithGuid( $statementGuid ) {
319 1
		foreach ( $this->statements as $statement ) {
320 1
			if ( $statement->getGuid() === $statementGuid ) {
321 1
				return $statement;
322 1
			}
323 1
		}
324
325 1
		return null;
326
	}
327
328
	/**
329
	 * @since 4.1
330
	 *
331
	 * @param StatementFilter $filter
332
	 *
333
	 * @return self
334
	 */
335
	public function filter( StatementFilter $filter ) {
336
		$statementList = new self();
337
338
		foreach ( $this->statements as $statement ) {
339
			if ( $filter->statementMatches( $statement ) ) {
340
				$statementList->statements[] = $statement;
341
			}
342
		}
343
344
		return $statementList;
345
	}
346
347
	/**
348
	 * Removes all statements from this list.
349
	 *
350
	 * @since 7.0
351
	 */
352
	public function clear() {
353
		$this->statements = [];
354
	}
355
356
	/**
357
	 * @see http://php.net/manual/en/language.oop5.cloning.php
358
	 *
359
	 * @since 5.1
360
	 */
361
	public function __clone() {
362
		foreach ( $this->statements as &$statement ) {
363
			$statement = clone $statement;
364
		}
365
	}
366
367
}
368