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

taToInternalCache()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 4
nc 3
nop 1
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 5
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
use SMW\DataTypeRegistry;
4
use SMW\DIProperty;
5
use SMW\DIWikiPage;
6
use SMW\StoreFactory;
7
use SMW\SQLStore\EntityStore\Exception\DataItemHandlerException;
8
9
/**
10
 * This class provides a subclass of SMWSemanticData that can store
11
 * prefetched values from SMW's SQL stores, and unstub this data on demand when
12
 * it is accessed.
13
 *
14
 * @since 1.8
15
 * @author Markus Krötzsch
16
 *
17
 * @ingroup SMWStore
18
 */
19
class SMWSql3StubSemanticData extends SMWSemanticData {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
20
21
	/**
22
	 * The store object.
23
	 *
24
	 * @since 1.8
25
	 *
26
	 * @var SMWSQLStore3
27
	 */
28
	protected $store;
29
30
	/**
31
	 * Stub property data that is not part of $mPropVals and $mProperties
32
	 * yet. Entries use property keys as keys. The value is an array of
33
	 * DBkey-arrays that define individual datavalues. The stubs will be
34
	 * set up when first accessed.
35
	 *
36
	 * @since 1.8
37
	 *
38
	 * @var array
39
	 */
40
	protected $mStubPropVals = array();
41
42
	/**
43
	 * SMWDIWikiPage object that is the subject of this container.
44
	 * Subjects that are null are used to represent "internal objects"
45
	 * only.
46
	 *
47
	 * @since 1.8
48
	 *
49
	 * @var SMWDIWikiPage
50
	 */
51
	protected $mSubject;
52
53
	/**
54
	 * Whether SubSemanticData have been requested and added
55
	 *
56
	 * @var boolean
57
	 */
58
	private $subSemanticDataInit = false;
59
60
	/**
61
	 * Constructor.
62
	 *
63
	 * @since 1.8
64
	 *
65
	 * @param SMWDIWikiPage $subject to which this data refers
66
	 * @param SMWSQLStore3 $store (the parent store)
67
	 * @param boolean $noDuplicates stating if duplicate data should be avoided
68
	 */
69 278
	public function __construct( SMWDIWikiPage $subject, SMWSQLStore3 $store, $noDuplicates = true ) {
70 278
		$this->store = $store;
71 278
		parent::__construct( $subject, $noDuplicates );
72 278
	}
73
74
	/**
75
	 * Required to support php-serialization
76
	 *
77
	 * @since 2.3
78
	 *
79
	 * @return array
80
	 */
81 263
	public function __sleep() {
82 263
		return array( 'mSubject', 'mPropVals', 'mProperties', 'subSemanticData', 'mStubPropVals' );
83
	}
84
85
	/**
86
	 * @since 2.3
87
	 *
88
	 * @return array
89
	 */
90 1
	public function __wakeup() {
91 1
		$this->store = StoreFactory::getStore( 'SMW\SQLStore\SQLStore' );
92 1
	}
93
94
	/**
95
	 * Create a new SMWSql3StubSemanticData object that holds the data of a
96
	 * given SMWSemanticData object. Array assignments create copies in PHP
97
	 * so the arrays are distinct in input and output object. The object
98
	 * references are copied as references in a shallow way. This is
99
	 * sufficient as the data items used there are immutable.
100
	 *
101
	 * @since 1.8
102
	 *
103
	 * @param $semanticData SMWSemanticData
104
	 * @param SMWSQLStore3 $store
105
	 *
106
	 * @return SMWSql3StubSemanticData
107
	 */
108 275
	public static function newFromSemanticData( SMWSemanticData $semanticData, SMWSQLStore3 $store ) {
109 275
		$result = new SMWSql3StubSemanticData( $semanticData->getSubject(), $store );
0 ignored issues
show
Compatibility introduced by
$semanticData->getSubject() of type object<SMW\DIWikiPage> is not a sub-type of object<SMWDIWikiPage>. It seems like you assume a child class of the class SMW\DIWikiPage 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...
110 275
		$result->mPropVals = $semanticData->mPropVals;
111 275
		$result->mProperties = $semanticData->mProperties;
112 275
		$result->mHasVisibleProps = $semanticData->mHasVisibleProps;
113 275
		$result->mHasVisibleSpecs = $semanticData->mHasVisibleSpecs;
114 275
		$result->stubObject = $semanticData->stubObject;
115 275
		return $result;
116
	}
117
118
	/**
119
	 * Get the array of all properties that have stored values.
120
	 *
121
	 * @since 1.8
122
	 *
123
	 * @return array of SMWDIProperty objects
124
	 */
125 237
	public function getProperties() {
126 237
		$this->unstubProperties();
127 237
		return parent::getProperties();
128
	}
129
130
	/**
131
	 * Get the array of all stored values for some property.
132
	 *
133
	 * @since 1.8
134
	 *
135
	 * @param DIProperty $property
136
	 *
137
	 * @return array of SMWDataItem
138
	 */
139 269
	public function getPropertyValues( DIProperty $property ) {
140 269
		if ( $property->isInverse() ) { // we never have any data for inverses
141 1
			return array();
142
		}
143
144 269
		if ( array_key_exists( $property->getKey(), $this->mStubPropVals ) ) {
145
			// Not catching exception here; the
146 111
			$this->unstubProperty( $property->getKey(), $property );
0 ignored issues
show
Documentation introduced by
$property is of type object<SMW\DIProperty>, but the function expects a object<SMWDIProperty>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
147 111
			$propertyTypeId = $property->findPropertyTypeID();
148 111
			$propertyDiId = DataTypeRegistry::getInstance()->getDataItemId( $propertyTypeId );
149
150 111
			foreach ( $this->mStubPropVals[$property->getKey()] as $dbkeys ) {
151
				try {
152 111
					$diHandler = $this->store->getDataItemHandlerForDIType( $propertyDiId );
153 111
					$di = $diHandler->dataItemFromDBKeys( $dbkeys );
154
155 111
					if ( $this->mNoDuplicates ) {
156 42
						$this->mPropVals[$property->getKey()][$di->getHash()] = $di;
157
					} else {
158 111
						$this->mPropVals[$property->getKey()][] = $di;
159
					}
160 111
				} catch ( DataItemHandlerException $e ) {
161
					// ignore data
162
				}
163
			}
164
165 111
			unset( $this->mStubPropVals[$property->getKey()] );
166
		}
167
168 269
		return parent::getPropertyValues( $property );
169
	}
170
171
	/**
172
	 * @see SemanticData::getSubSemanticData
173
	 *
174
	 * @note SubSemanticData are added only on request to avoid unnecessary DB
175
	 * transactions
176
	 *
177
	 * @since 2.0
178
	 */
179 232
	public function getSubSemanticData() {
180
181 232
		if ( $this->subSemanticDataInit ) {
182 147
			return parent::getSubSemanticData();
183
		}
184
185 231
		$this->subSemanticDataInit = true;
186
187 231
		foreach ( $this->getProperties() as $property ) {
188
189
			// #619 Do not resolve subobjects for redirects
190 230
			if ( !DataTypeRegistry::getInstance()->isSubDataType( $property->findPropertyTypeID() ) || $this->isRedirect() ) {
191 230
				continue;
192
			}
193
194 144
			$this->initSubSemanticData( $property );
195
		}
196
197 231
		return parent::getSubSemanticData();
198
	}
199
200
	/**
201
	 * @see SemanticData::hasSubSemanticData
202
	 *
203
	 * @note This method will initialize SubSemanticData first if it wasn't done
204
	 * yet to ensure data consistency
205
	 *
206
	 * @since 2.0
207
	 */
208 145
	public function hasSubSemanticData( $subobjectName = null ) {
209
210 145
		if ( !$this->subSemanticDataInit ) {
211 3
			$this->getSubSemanticData();
212
		}
213
214 145
		return parent::hasSubSemanticData( $subobjectName );
215
	}
216
217
	/**
218
	 * @see SemanticData::findSubSemanticData
219
	 *
220
	 * @since 2.5
221
	 */
222
	public function findSubSemanticData( $subobjectName ) {
223
224
		if ( !$this->subSemanticDataInit ) {
225
			$this->getSubSemanticData();
226
		}
227
228
		return parent::findSubSemanticData( $subobjectName );
229
	}
230
231
	/**
232
	 * Remove a value for a property identified by its SMWDataItem object.
233 2
	 * This method removes a property-value specified by the property and
234 2
	 * dataitem. If there are no more property-values for this property it
235 2
	 * also removes the property from the mProperties.
236 2
	 *
237 2
	 * @note There is no check whether the type of the given data item
238
	 * agrees with the type of the property. Since property types can
239
	 * change, all parts of SMW are prepared to handle mismatched data item
240
	 * types anyway.
241
	 *
242
	 * @param $property SMWDIProperty
243
	 * @param $dataItem SMWDataItem
244
	 *
245
	 * @since 1.8
246 21
	 */
247 21
	public function removePropertyObjectValue( DIProperty $property, SMWDataItem $dataItem ) {
248 21
		$this->unstubProperties();
249
		$this->getPropertyValues( $property );
250
		parent::removePropertyObjectValue($property, $dataItem);
251
	}
252
253
	/**
254
	 * Return true if there are any visible properties.
255
	 *
256
	 * @since 1.8
257
	 *
258
	 * @return boolean
259 21
	 */
260 21
	public function hasVisibleProperties() {
261 21
		$this->unstubProperties();
262
		return parent::hasVisibleProperties();
263
	}
264
265
	/**
266
	 * Return true if there are any special properties that can
267
	 * be displayed.
268
	 *
269
	 * @since 1.8
270
	 *
271
	 * @return boolean
272
	 */
273
	public function hasVisibleSpecialProperties() {
274
		$this->unstubProperties();
275 202
		return parent::hasVisibleSpecialProperties();
276 202
	}
277 202
278
	/**
279
	 * Add data in abbreviated form so that it is only expanded if needed.
280
	 * The property key is the DB key (string) of a property value, whereas
281
	 * valuekeys is an array of DBkeys for the added value that will be
282
	 * used to initialize the value if needed at some point. If there is
283
	 * only one valuekey, a single string can be used.
284 278
	 *
285 278
	 * @since 1.8
286 278
	 * @param string $propertyKey
287 278
	 * @param array|string $valueKeys
288
	 */
289
	public function addPropertyStubValue( $propertyKey, $valueKeys ) {
290
		$this->mStubPropVals[$propertyKey][] = $valueKeys;
291
	}
292
293
	/**
294
	 * Delete all data other than the subject.
295 237
	 *
296 237
	 * @since 1.8
297
	 */
298 106
	public function clear() {
299 106
		$this->mStubPropVals = array();
300
		parent::clear();
301
	}
302
303
	/**
304
	 * Process all mProperties that have been added as stubs.
305 237
	 * Associated data may remain in stub form.
306
	 *
307
	 * @since 1.8
308
	 */
309
	protected function unstubProperties() {
310
		foreach ( $this->mStubPropVals as $pkey => $values ) { // unstub property values only, the value lists are still kept as stubs
311
			try {
312
				$this->unstubProperty( $pkey );
313
			} catch ( SMWDataItemException $e ) {
314
				// Likely cause: a property name from the DB is no longer valid.
315
				// Do nothing; we could unset the data, but it will never be
316
				// unstubbed anyway if there is no valid property DI for it.
317
			}
318
		}
319
	}
320 112
321 112
	/**
322 111
	 * Unstub a single property from the stub data array. If available, an
323 105
	 * existing object for that property might be provided, so we do not
324
	 * need to make a new one. It is not checked if the object matches the
325
	 * property name.
326 111
	 *
327
	 * @since 1.8
328 111
	 *
329 111
	 * @param string $propertyKey
330 52
	 * @param SMWDIProperty $diProperty if available
331 111
	 * @throws SMWDataItemException if property key is not valid
332
	 * 	and $diProperty is null
333
	 */
334 52
	protected function unstubProperty( $propertyKey, $diProperty = null ) {
335
		if ( !array_key_exists( $propertyKey, $this->mProperties ) ) {
336
			if ( is_null( $diProperty ) ) {
337 112
				$diProperty = new DIProperty( $propertyKey, false );
338
			}
339 144
340 144
			$this->mProperties[$propertyKey] = $diProperty;
341
342
			if ( !$diProperty->isUserDefined() ) {
343 144
				if ( $diProperty->isShown() ) {
344
					$this->mHasVisibleSpecs = true;
345 144
					$this->mHasVisibleProps = true;
346 144
				}
347 144
			} else {
348
				$this->mHasVisibleProps = true;
349
			}
350 144
		}
351
	}
352
353
	protected function isRedirect() {
354
		return $this->store->getObjectIds()->checkIsRedirect( $this->mSubject );
355
	}
356
357
	private function initSubSemanticData( DIProperty $property ) {
358
		foreach ( $this->getPropertyValues( $property ) as $value ) {
0 ignored issues
show
Bug introduced by
The expression $this->getPropertyValues($property) 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...
359
360
			if ( !$value instanceof DIWikiPage || $value->getSubobjectName() === '' ) {
361
				continue;
362
			}
363
364
			if ( $this->hasSubSemanticData( $value->getSubobjectName() ) ) {
365
				continue;
366
			}
367
368
			$this->addSubSemanticData( $this->store->getSemanticData( $value ) );
369
		}
370
	}
371
372
}
373