Completed
Push — master ( f2f6e8...470987 )
by mw
33:59
created

SemanticData   C

Complexity

Total Complexity 70

Size/Duplication

Total Lines 646
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 92.05%

Importance

Changes 0
Metric Value
dl 0
loc 646
ccs 162
cts 176
cp 0.9205
rs 5.3528
c 0
b 0
f 0
wmc 70
lcom 1
cbo 11

28 Methods

Rating   Name   Duplication   Size   Complexity  
A addError() 0 3 1
A clearSubSemanticData() 0 3 2
A hasVisibleProperties() 0 3 1
A hasVisibleSpecialProperties() 0 3 1
B addPropertyValue() 0 22 4
A addSubobject() 0 6 1
A isEmpty() 0 3 2
C importDataFrom() 0 35 8
A findSubSemanticData() 0 3 1
A addSubSemanticData() 0 4 1
A removeSubSemanticData() 0 4 1
A setLastModified() 0 3 1
A __construct() 0 6 1
A __sleep() 0 3 1
A getSubject() 0 3 1
A getProperties() 0 4 1
A hasProperty() 0 3 2
A getPropertyValues() 0 11 3
A getErrors() 0 3 1
A getHash() 0 8 2
A getSubSemanticData() 0 3 1
C addPropertyObjectValue() 0 42 11
A addDataValue() 0 21 3
D removePropertyObjectValue() 0 35 9
A clear() 0 9 1
B removeDataFrom() 0 16 5
A hasSubSemanticData() 0 3 1
A getLastModified() 0 16 3

How to fix   Complexity   

Complex Class

Complex classes like SemanticData 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 SemanticData, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace SMW;
4
5
use MWException;
6
use SMWContainerSemanticData;
7
use SMWDataItem;
8
use SMWDataValue;
9
use SMWDIContainer;
10
use SMWPropertyValue;
11
use SMW\Exception\SemanticDataImportException;
12
use SMW\Exception\SubSemanticDataException;
13
use SMW\DataModel\SubSemanticData;
14
15
/**
16
 * Class for representing chunks of semantic data for one given
17
 * subject. This consists of property-value pairs, grouped by property,
18
 * and possibly by SMWSemanticData objects about subobjects.
19
 *
20
 * Data about subobjects can be added in two ways: by directly adding it
21
 * using addSubSemanticData() or by adding a property value of type
22
 * SMWDIContainer.
23
 *
24
 * By its very design, the container is unable to hold inverse properties.
25
 * For one thing, it would not be possible to identify them with mere keys.
26
 * Since SMW cannot annotate pages with inverses, this is not a limitation.
27
 *
28
 * @ingroup SMW
29
 *
30
 * @author Markus Krötzsch
31
 * @author Jeroen De Dauw
32
 */
33
class SemanticData {
34
35
	/**
36
	 * Cache for the localized version of the namespace prefix "Property:".
37
	 *
38
	 * @var string
39
	 */
40
	static protected $mPropertyPrefix = '';
41
42
	/**
43
	 * States whether this is a stub object. Stubbing might happen on
44
	 * serialisation to save DB space.
45
	 *
46
	 * @todo Check why this is public and document this here. Or fix it.
47
	 *
48
	 * @var boolean
49
	 */
50
	public $stubObject;
51
52
	/**
53
	 * Array mapping property keys (string) to arrays of SMWDataItem
54
	 * objects.
55
	 *
56
	 * @var SMWDataItem[]
57
	 */
58
	protected $mPropVals = array();
59
60
	/**
61
	 * Array mapping property keys (string) to DIProperty objects.
62
	 *
63
	 * @var DIProperty[]
64
	 */
65
	protected $mProperties = array();
66
67
	/**
68
	 * States whether the container holds any normal properties.
69
	 *
70
	 * @var boolean
71
	 */
72
	protected $mHasVisibleProps = false;
73
74
	/**
75
	 * States whether the container holds any displayable predefined
76
	 * $mProperties (as opposed to predefined properties without a display
77
	 * label). For some settings we need this to decide if a Factbox is
78
	 * displayed.
79
	 *
80
	 * @var boolean
81
	 */
82
	protected $mHasVisibleSpecs = false;
83
84
	/**
85
	 * States whether repeated values should be avoided. Not needing
86
	 * duplicate elimination (e.g. when loading from store) can save some
87
	 * time, especially in subclasses like SMWSqlStubSemanticData, where
88
	 * the first access to a data item is more costy.
89
	 *
90
	 * @note This setting is merely for optimization. The SMW data model
91
	 * never cares about the multiplicity of identical data assignments.
92
	 *
93
	 * @var boolean
94
	 */
95
	protected $mNoDuplicates;
96
97
	/**
98
	 * DIWikiPage object that is the subject of this container.
99
	 * Subjects can never be null (and this is ensured in all methods setting
100
	 * them in this class).
101
	 *
102
	 * @var DIWikiPage
103
	 */
104
	protected $mSubject;
105
106
	/**
107
	 * Semantic data associated to subobjects of the subject of this
108
	 * SMWSemanticData.
109
	 * These key-value pairs of subObjectName (string) =>SMWSemanticData.
110
	 *
111
	 * @since 1.8
112
	 * @var SubSemanticData
113
	 */
114
	protected $subSemanticData;
115
116
	/**
117
	 * Internal flag that indicates if this semantic data will accept
118
	 * subdata. Semantic data objects that are subdata already do not allow
119
	 * (second level) subdata to be added. This ensures that all data is
120
	 * collected on the top level, and in particular that there is only one
121
	 * way to represent the same data with subdata. This is also useful for
122
	 * diff computation.
123
	 */
124
	protected $subDataAllowed = true;
125
126
	/**
127
	 * @var array
128
	 */
129
	protected $errors = array();
130
131
	/**
132
	 * Cache the hash to ensure a minimal impact in case of repeated usage. Any
133
	 * removal or insert action will reset the hash to null to ensure it is
134
	 * recreated in corresponds to changed nature of the data.
135
	 *
136
	 * @var string|null
137
	 */
138
	private $hash = null;
139
140
	/**
141
	 * @var integer|null
142
	 */
143
	private $lastModified = null;
144
145
	/**
146
	 * This is kept public to keep track of the depth during a recursive processing
147
	 * when accessed through the SubSemanticData instance.
148
	 *
149
	 * @var integer
150
	 */
151
	public $subContainerDepthCounter = 0;
152
153
	/**
154
	 * Constructor.
155
	 *
156
	 * @param DIWikiPage $subject to which this data refers
157
	 * @param boolean $noDuplicates stating if duplicate data should be avoided
158
	 */
159
	public function __construct( DIWikiPage $subject, $noDuplicates = true ) {
160
		$this->clear();
161 326
		$this->mSubject = $subject;
162 326
		$this->mNoDuplicates = $noDuplicates;
163 326
		$this->subSemanticData = new SubSemanticData( $subject, $noDuplicates );
164 326
	}
165 326
166
	/**
167
	 * This object is added to the parser output of MediaWiki, but it is
168
	 * not useful to have all its data as part of the parser cache since
169
	 * the data is already stored in more accessible format in SMW. Hence
170
	 * this implementation of __sleep() makes sure only the subject is
171
	 * serialised, yielding a minimal stub data container after
172
	 * unserialisation. This is a little safer than serialising nothing:
173
	 * if, for any reason, SMW should ever access an unserialised parser
174
	 * output, then the Semdata container will at least look as if properly
175
	 * initialised (though empty).
176
	 *
177
	 * @return array
178
	 */
179
	public function __sleep() {
180 270
		return array( 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mHasVisibleProps', 'mHasVisibleSpecs', 'lastModified' );
181 270
	}
182
183
	/**
184
	 * Return subject to which the stored semantic annotations refer to.
185
	 *
186
	 * @return DIWikiPage subject
187
	 */
188
	public function getSubject() {
189 308
		return $this->mSubject;
190 308
	}
191
192
	/**
193
	 * Get the array of all properties that have stored values.
194
	 *
195
	 * @return array of DIProperty objects
196
	 */
197
	public function getProperties() {
198 300
		ksort( $this->mProperties, SORT_STRING );
199 300
		return $this->mProperties;
200 300
	}
201
202
	/**
203
	 * @since 2.4
204
	 *
205
	 * @param DIProperty $property
206
	 *
207
	 * @return boolean
208
	 */
209
	public function hasProperty( DIProperty $property ) {
210 268
		return isset( $this->mProperties[$property->getKey()] ) || array_key_exists( $property->getKey(), $this->mProperties );
211 268
	}
212
213
	/**
214
	 * Get the array of all stored values for some property.
215
	 *
216
	 * @param DIProperty $property
217
	 * @return SMWDataItem[]
218
	 */
219
	public function getPropertyValues( DIProperty $property ) {
220 310
		if ( $property->isInverse() ) { // we never have any data for inverses
221 310
			return array();
222 1
		}
223
224
		if ( array_key_exists( $property->getKey(), $this->mPropVals ) ) {
225 310
			return $this->mPropVals[$property->getKey()];
226 279
		}
227
228
		return array();
229 301
	}
230
231
	/**
232
	 * Returns collected errors occurred during processing
233
	 *
234
	 * @since 1.9
235
	 *
236
	 * @return array
237
	 */
238
	public function getErrors() {
239 89
		return $this->errors;
240 89
	}
241
242
	/**
243
	 * Adds an error array
244
	 *
245
	 * @since  1.9
246
	 *
247
	 * @return array|string
248
	 */
249
	public function addError( $error ) {
250 41
		$this->errors = array_merge( $this->errors, (array)$error );
251 41
	}
252 41
253
	/**
254
	 * Generate a hash value to simplify the comparison of this data
255
	 * container with other containers. Subdata is taken into account.
256
	 *
257
	 * The hash uses PHP's md5 implementation, which is among the fastest
258
	 * hash algorithms that PHP offers.
259
	 *
260
	 * @note This function may be used to obtain keys for SemanticData
261
	 * objects or to do simple equality tests. Equal hashes with very
262
	 * high probability indicate equal data.
263
	 *
264
	 * @return string
265
	 */
266
	public function getHash() {
267 13
268
		if ( $this->hash !== null ) {
269 13
			return $this->hash;
270
		}
271
272
		return $this->hash = Hash::createFromSemanticData( $this );
273 13
	}
274
275
	/**
276
	 * Return the array of subSemanticData objects for this SemanticData
277
	 *
278
	 * @since 1.8
279
	 * @return SMWContainerSemanticData[] subobject => SMWContainerSemanticData
280
	 */
281
	public function getSubSemanticData() {
282 300
		return $this->subSemanticData->getSubSemanticData();
283 300
	}
284
285
	/**
286
	 * Return the array of subSemanticData objects for this SemanticData
287
	 *
288
	 * @since 2.5
289
	 */
290
	public function clearSubSemanticData() {
291
		$this->subSemanticData !== null ? $this->subSemanticData->clear() : '';
292
	}
293
294
	/**
295 26
	 * Return true if there are any visible properties.
296 26
	 *
297
	 * @note While called "visible" this check actually refers to the
298
	 * function DIProperty::isShown(). The name is kept for
299
	 * compatibility.
300
	 *
301
	 * @return boolean
302
	 */
303
	public function hasVisibleProperties() {
304
		return $this->mHasVisibleProps;
305
	}
306
307
	/**
308
	 * Return true if there are any special properties that can
309 25
	 * be displayed.
310 25
	 *
311
	 * @note While called "visible" this check actually refers to the
312
	 * function DIProperty::isShown(). The name is kept for
313
	 * compatibility.
314
	 *
315
	 * @return boolean
316
	 */
317
	public function hasVisibleSpecialProperties() {
318
		return $this->mHasVisibleSpecs;
319
	}
320
321
	/**
322
	 * Store a value for a property identified by its SMWDataItem object.
323
	 *
324 293
	 * @note There is no check whether the type of the given data item
325
	 * agrees with the type of the property. Since property types can
326 293
	 * change, all parts of SMW are prepared to handle mismatched data item
327
	 * types anyway.
328 293
	 *
329 175
	 * @param $property DIProperty
330 174
	 * @param $dataItem SMWDataItem
331
	 */
332
	public function addPropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
333 293
334
		$this->hash = null;
335
336
		if( $dataItem instanceof SMWDIContainer ) {
337 293
			$this->addSubSemanticData( $dataItem->getSemanticData() );
338 293
			$dataItem = $dataItem->getSemanticData()->getSubject();
339 293
		}
340
341
		if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
342 293
			return;
343 293
		}
344
345
		if ( !array_key_exists( $property->getKey(), $this->mPropVals ) ) {
346
			$this->mPropVals[$property->getKey()] = array();
347
			$this->mProperties[$property->getKey()] = $property;
348 293
		}
349 264
350 224
		if ( $this->mNoDuplicates ) {
351 264
			$this->mPropVals[$property->getKey()][$dataItem->getHash()] = $dataItem;
352
		} else {
353
			$this->mPropVals[$property->getKey()][] = $dataItem;
354 245
		}
355
356
		if ( !$property->isUserDefined() ) {
357
			if ( $property->isShown() ) {
358 293
				$this->mHasVisibleSpecs = true;
359 217
				$this->mHasVisibleProps = true;
360 139
			}
361 139
		} else {
362
			$this->mHasVisibleProps = true;
363
		}
364
365 293
		// Inherit the sortkey from the root if not explicitly given
366
		if ( $this->mSubject->getSubobjectName() === '' && $property->getKey() === DIProperty::TYPE_SORTKEY ) {
367
			foreach ( $this->getSubSemanticData() as $subSemanticData ) {
368
				if ( !$subSemanticData->hasProperty( $property ) ) {
369
					$subSemanticData->addPropertyObjectValue( $property, $dataItem );
370
				}
371
			}
372
		}
373
	}
374 1
375 1
	/**
376
	 * Store a value for a given property identified by its text label
377 1
	 * (without namespace prefix).
378
	 *
379
	 * @param $propertyName string
380 1
	 * @param $dataItem SMWDataItem
381 1
	 */
382 1
	public function addPropertyValue( $propertyName, SMWDataItem $dataItem ) {
383
		$propertyKey = smwfNormalTitleDBKey( $propertyName );
384
385 1
		if ( array_key_exists( $propertyKey, $this->mProperties ) ) {
386
			$property = $this->mProperties[$propertyKey];
387 1
		} else {
388
			if ( self::$mPropertyPrefix === '' ) {
389
				global $wgContLang;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
390
				self::$mPropertyPrefix = $wgContLang->getNsText( SMW_NS_PROPERTY ) . ':';
391 1
			} // explicitly use prefix to cope with things like [[Property:User:Stupid::somevalue]]
392
393
			$propertyDV = SMWPropertyValue::makeUserProperty( self::$mPropertyPrefix . $propertyName );
394 1
395 1
			if ( !$propertyDV->isValid() ) { // error, maybe illegal title text
396
				return;
397
			}
398
399
			$property = $propertyDV->getDataItem();
400
		}
401
402 238
		$this->addPropertyObjectValue( $property, $dataItem );
0 ignored issues
show
Compatibility introduced by
$property of type object<SMWDataItem> is not a sub-type of object<SMW\DIProperty>. It seems like you assume a child class of the class SMWDataItem to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
403
	}
404 238
405
	/**
406 34
	 * @since 1.9
407 34
	 *
408
	 * @param SMWDataValue $dataValue
409
	 */
410 34
	public function addDataValue( SMWDataValue $dataValue ) {
411
412 34
		if ( !$dataValue->getProperty() instanceof DIProperty || !$dataValue->isValid() ) {
413
414
			$processingErrorMsgHandler = new ProcessingErrorMsgHandler(
415 34
				$this->getSubject()
416
			);
417
418 234
			$processingErrorMsgHandler->addToSemanticData(
419 234
				$this,
420 234
				$processingErrorMsgHandler->newErrorContainerFromDataValue( $dataValue )
421
			);
422 234
423
			return $this->addError( $dataValue->getErrors() );
424
		}
425
426
		$this->addPropertyObjectValue(
427
			$dataValue->getProperty(),
428
			$dataValue->getDataItem()
429 72
		);
430 72
	}
431 72
432 72
	/**
433
	 * @since 2.1
434 71
	 *
435
	 * @param Subobject $subobject
436
	 */
437
	public function addSubobject( Subobject $subobject ) {
438
		$this->addPropertyObjectValue(
439
			$subobject->getProperty(),
440
			$subobject->getContainer()
441
		);
442
	}
443
444
	/**
445
	 * Remove a value for a property identified by its SMWDataItem object.
446
	 * This method removes a property-value specified by the property and
447
	 * dataitem. If there are no more property-values for this property it
448
	 * also removes the property from the mProperties.
449
	 *
450
	 * @note There is no check whether the type of the given data item
451
	 * agrees with the type of the property. Since property types can
452 7
	 * change, all parts of SMW are prepared to handle mismatched data item
453
	 * types anyway.
454 7
	 *
455
	 * @param $property DIProperty
456
	 * @param $dataItem SMWDataItem
457 7
	 *
458 1
	 * @since 1.8
459 1
	 */
460
	public function removePropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
461
462 7
		$this->hash = null;
463
464
		//delete associated subSemanticData
465
		if( $dataItem instanceof SMWDIContainer ) {
466 7
			$this->removeSubSemanticData( $dataItem->getSemanticData() );
467
			$dataItem = $dataItem->getSemanticData()->getSubject();
468
		}
469
470 7
		if ( $property->isInverse() ) { // inverse properties cannot be used for annotation
471
			return;
472 7
		}
473
474
		if ( !array_key_exists( $property->getKey(), $this->mPropVals ) || !array_key_exists( $property->getKey(), $this->mProperties ) ) {
475
			return;
476
		}
477
478
		if ( $this->mNoDuplicates ) {
479
			//this didn't get checked for my tests, but should work
480
			unset( $this->mPropVals[$property->getKey()][$dataItem->getHash()] );
481
		} else {
482 7
			foreach( $this->mPropVals[$property->getKey()] as $index => $di ) {
0 ignored issues
show
Bug introduced by
The expression $this->mPropVals[$property->getKey()] of type object<SMWDataItem> is not traversable.
Loading history...
483 7
				if( $di->equals( $dataItem ) ) {
484 7
					unset( $this->mPropVals[$property->getKey()][$index] );
485
				}
486 7
			}
487
			$this->mPropVals[$property->getKey()] = array_values( $this->mPropVals[$property->getKey()] );
488
		}
489
490
		if ( $this->mPropVals[$property->getKey()] === array() ) {
491 326
			unset( $this->mProperties[$property->getKey()] );
492 326
			unset( $this->mPropVals[$property->getKey()] );
493 326
		}
494 326
	}
495 326
496 326
	/**
497 326
	 * Delete all data other than the subject.
498 326
	 */
499 326
	public function clear() {
500
		$this->mPropVals = array();
501
		$this->mProperties = array();
502
		$this->mHasVisibleProps = false;
503
		$this->mHasVisibleSpecs = false;
504
		$this->stubObject = false;
505
		$this->clearSubSemanticData();
506
		$this->hash = null;
507
	}
508
509
	/**
510 224
	 * Return true if this SemanticData is empty.
511 224
	 * This is the case when the subject has neither property values nor
512
	 * data for subobjects.
513
	 *
514
	 * @since 1.8
515
	 *
516
	 * @return boolean
517
	 */
518
	public function isEmpty() {
519
		return $this->getProperties() === array() && $this->getSubSemanticData() === array();
520
	}
521
522
	/**
523
	 * Add all data from the given SMWSemanticData.
524
	 * Only works if the imported SMWSemanticData has the same subject as
525 14
	 * this SMWSemanticData; an exception is thrown otherwise.
526
	 *
527 14
	 * @since 1.7
528 1
	 *
529
	 * @param SemanticData $semanticData object to copy from
530
	 *
531 13
	 * @throws SemanticDataImportException
532
	 */
533
	public function importDataFrom( SemanticData $semanticData ) {
534
535 13
		if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
536 13
			throw new SemanticDataImportException( "SemanticData can only represent data about one subject. Importing data for another subject is not possible." );
537 3
		}
538 3
539
		$this->hash = null;
540 3
541 3
		// Shortcut when copying into empty objects that don't ask for
542
		// more duplicate elimination:
543
		if ( count( $this->mProperties ) == 0 &&
544 3
		     ( $semanticData->mNoDuplicates >= $this->mNoDuplicates ) ) {
545 3
			$this->mProperties = $semanticData->getProperties();
546
			$this->mPropVals = array();
547 10
548 10
			foreach ( $this->mProperties as $property ) {
549
				$this->mPropVals[$property->getKey()] = $semanticData->getPropertyValues( $property );
550 10
			}
551 10
552
			$this->mHasVisibleProps = $semanticData->hasVisibleProperties();
553
			$this->mHasVisibleSpecs = $semanticData->hasVisibleSpecialProperties();
554
		} else {
555
			foreach ( $semanticData->getProperties() as $property ) {
556 13
				$values = $semanticData->getPropertyValues( $property );
557 2
558
				foreach ( $values as $dataItem ) {
0 ignored issues
show
Bug introduced by
The expression $values of type array|object<SMWDataItem> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
559 13
					$this->addPropertyObjectValue( $property, $dataItem);
560
				}
561
			}
562
		}
563
564
		foreach( $semanticData->getSubSemanticData() as $semData ) {
565
			$this->addSubSemanticData( $semData );
566
		}
567
	}
568
569
	/**
570
	 * Removes data from the given SMWSemanticData.
571
	 * If the subject of the data that is to be removed is not equal to the
572 3
	 * subject of this SMWSemanticData, it will just be ignored (nothing to
573 3
	 * remove). Likewise, removing data that is not present does not change
574
	 * anything.
575
	 *
576
	 * @since 1.8
577 3
	 *
578 3
	 * @param SemanticData $semanticData
579 3
	 */
580 3
	public function removeDataFrom( SemanticData $semanticData ) {
581
		if( !$this->mSubject->equals( $semanticData->getSubject() ) ) {
582
			return;
583
		}
584 3
585
		foreach ( $semanticData->getProperties() as $property ) {
586
			$values = $semanticData->getPropertyValues( $property );
587 3
			foreach ( $values as $dataItem ) {
0 ignored issues
show
Bug introduced by
The expression $values of type array|object<SMWDataItem> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
588
				$this->removePropertyObjectValue( $property, $dataItem );
589
			}
590
		}
591
592
		foreach( $semanticData->getSubSemanticData() as $semData ) {
593
			$this->removeSubSemanticData( $semData );
594
		}
595
	}
596
597
	/**
598
	 * @see SubSemanticData::hasSubSemanticData
599 178
	 * @since 1.9
600
	 *
601 178
	 * @param string $subobjectName|null
0 ignored issues
show
Documentation introduced by
There is no parameter named $subobjectName|null. Did you maybe mean $subobjectName?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
602 178
	 *
603
	 * @return boolean
604
	 */
605 121
	public function hasSubSemanticData( $subobjectName = null ) {
606
		return $this->subSemanticData->hasSubSemanticData( $subobjectName );
607
	}
608
609
	/**
610
	 * @see SubSemanticData::findSubSemanticData
611
	 * @since 1.9
612
	 *
613
	 * @param string $subobjectName
614
	 *
615
	 * @return SMWContainerSemanticData|[]
0 ignored issues
show
Documentation introduced by
The doc-type SMWContainerSemanticData|[] could not be parsed: Unknown type name "" at position 25. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
616
	 */
617 6
	public function findSubSemanticData( $subobjectName ) {
618
		return $this->subSemanticData->findSubSemanticData( $subobjectName );
619 6
	}
620 5
621
	/**
622
	 * @see SubSemanticData::addSubSemanticData
623 4
	 * @since 1.8
624
	 *
625
	 * @param SemanticData $semanticData
626
	 * @throws SubSemanticDataException
627
	 */
628
	public function addSubSemanticData( SemanticData $semanticData ) {
629
		$this->hash = null;
630
		$this->subSemanticData->addSubSemanticData( $semanticData );
631
	}
632
633
	/**
634
	 * @see SubSemanticData::removeSubSemanticData
635
	 * @since 1.8
636
	 *
637
	 * @param SemanticData $semanticData
638
	*/
639 178
	public function removeSubSemanticData( SemanticData $semanticData ) {
640
		$this->hash = null;
641 178
		$this->subSemanticData->removeSubSemanticData( $semanticData );
642 178
	}
643
644 178
	/**
645
	 * Returns the last modified timestamp the data were stored to the Store or
646
	 * have been fetched from cache.
647
	 *
648 178
	 * @since  2.3
649
	 *
650 178
	 * @return integer|null
651 1
	 */
652
	public function getLastModified() {
653
654 177
		if ( $this->lastModified !== null ) {
655 1
			return $this->lastModified;
656
		}
657
658 176
		$lastModified = $this->getPropertyValues( new DIProperty( '_MDAT' ) );
659 10
660
		if ( $lastModified === array() ) {
661 10
			return null;
662 10
		}
663
664
		$lastModified = end( $lastModified );
665 176
666 176
		return $this->lastModified = $lastModified->getMwTimestamp();
667
	}
668
669 17
	/**
670 5
	 * @since  2.3
671
	 *
672
	 * @param string|null $lastModified
673 13
	 */
674
	public function setLastModified( $lastModified ) {
675 176
		$this->lastModified = $lastModified;
0 ignored issues
show
Documentation Bug introduced by
It seems like $lastModified can also be of type string. However, the property $lastModified is declared as type integer|null. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
676 176
	}
677
678
}
679