Passed
Push — rankClass ( 1b9cf4...7f76c7 )
by no
04:15 queued 16s
created

StatementList   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 334
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 6
dl 0
loc 334
rs 6.96
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 17 6
A getPropertyIds() 0 9 2
A addStatement() 0 9 4
A addNewStatement() 0 9 3
A removeStatementsWithGuid() 0 9 3
A getWithUniqueMainSnaks() 0 9 2
A getByPropertyId() 0 11 3
A getByRank() 0 12 3
A getBestStatements() 0 9 2
A getAllSnaks() 0 11 3
A getMainSnaks() 0 9 2
A getIterator() 0 3 1
A toArray() 0 3 1
A count() 0 3 1
A equals() 0 13 4
A statementsEqual() 0 13 3
A isEmpty() 0 3 1
A getFirstStatementWithGuid() 0 9 3
A filter() 0 11 3
A clear() 0 3 1
A __clone() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like StatementList often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StatementList, and based on these observations, apply Extract Interface, too.

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 Kreuz
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 );
0 ignored issues
show
Deprecated Code introduced by
The constant Wikibase\DataModel\State...atement::RANK_PREFERRED has been deprecated with message: since 4.4, use StatementRank::PREFERRED instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
196
197
		if ( !$statements->isEmpty() ) {
198
			return $statements;
199
		}
200
201
		return $this->getByRank( Statement::RANK_NORMAL );
0 ignored issues
show
Deprecated Code introduced by
The constant Wikibase\DataModel\State...\Statement::RANK_NORMAL has been deprecated with message: since 4.4, use StatementRank::NORMAL instead.

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
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