Passed
Push — currentLimits ( af2764...193e29 )
by no
03:56
created

StatementList::getFirstStatementWithGuid()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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