These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace SMW; |
||
4 | |||
5 | use SMW\Query\DescriptionFactory; |
||
6 | use Onoi\Cache\Cache; |
||
7 | use SMW\Message; |
||
8 | use SMWDIBlob as DIBlob; |
||
9 | use SMWQuery as Query; |
||
10 | |||
11 | /** |
||
12 | * This class should be accessed via ApplicationFactory::getPropertySpecificationLookup |
||
13 | * to ensure a singleton instance. |
||
14 | * |
||
15 | * @license GNU GPL v2+ |
||
16 | * @since 2.4 |
||
17 | * |
||
18 | * @author mwjames |
||
19 | */ |
||
20 | class PropertySpecificationLookup { |
||
21 | |||
22 | const POOLCACHE_ID = 'property.specification.lookup'; |
||
23 | |||
24 | /** |
||
25 | * @var CachedPropertyValuesPrefetcher |
||
26 | */ |
||
27 | private $cachedPropertyValuesPrefetcher; |
||
28 | |||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | private $languageCode = 'en'; |
||
33 | |||
34 | /** |
||
35 | * @var Cache |
||
36 | */ |
||
37 | private $intermediaryMemoryCache; |
||
38 | |||
39 | /** |
||
40 | * @since 2.4 |
||
41 | * |
||
42 | * @param CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher |
||
43 | * @param Cache $intermediaryMemoryCache |
||
44 | */ |
||
45 | 243 | public function __construct( CachedPropertyValuesPrefetcher $cachedPropertyValuesPrefetcher, Cache $intermediaryMemoryCache ) { |
|
46 | 243 | $this->cachedPropertyValuesPrefetcher = $cachedPropertyValuesPrefetcher; |
|
47 | 243 | $this->intermediaryMemoryCache = $intermediaryMemoryCache; |
|
48 | 243 | } |
|
49 | |||
50 | /** |
||
51 | * @since 2.4 |
||
52 | */ |
||
53 | 158 | public function resetCacheBy( DIWikiPage $subject ) { |
|
54 | 158 | $this->cachedPropertyValuesPrefetcher->resetCacheBy( $subject ); |
|
55 | 158 | } |
|
56 | |||
57 | /** |
||
58 | * @since 2.4 |
||
59 | * |
||
60 | * @param string |
||
61 | */ |
||
62 | public function getLanguageCode() { |
||
63 | return $this->languageCode; |
||
64 | } |
||
65 | |||
66 | /** |
||
67 | * @since 2.4 |
||
68 | * |
||
69 | * @param string $languageCode |
||
70 | */ |
||
71 | 233 | public function setLanguageCode( $languageCode ) { |
|
72 | 233 | $this->languageCode = Localizer::asBCP47FormattedLanguageCode( $languageCode ); |
|
73 | 233 | } |
|
74 | |||
75 | /** |
||
76 | * @since 2.5 |
||
77 | * |
||
78 | * @param string $id |
||
79 | * @param string $languageCode |
||
80 | * |
||
81 | * @return string |
||
82 | */ |
||
83 | 232 | public function getPreferredPropertyLabelBy( $id, $languageCode = '' ) { |
|
84 | |||
85 | 232 | $languageCode = $languageCode === '' ? $this->languageCode : $languageCode; |
|
86 | 232 | $key = 'ppl:' . $languageCode . ':'. $id; |
|
87 | |||
88 | // Guard against high frequency lookup |
||
89 | 232 | if ( ( $preferredPropertyLabel = $this->intermediaryMemoryCache->fetch( $key ) ) !== false ) { |
|
90 | 203 | return $preferredPropertyLabel; |
|
91 | } |
||
92 | |||
93 | 201 | $preferredPropertyLabel = $this->findPreferredPropertyLabel( |
|
94 | 201 | new DIProperty( str_replace( ' ', '_', $id ) ), |
|
95 | $languageCode |
||
96 | ); |
||
97 | |||
98 | 201 | $this->intermediaryMemoryCache->save( $key, $preferredPropertyLabel ); |
|
99 | |||
100 | 201 | return $preferredPropertyLabel; |
|
101 | } |
||
102 | |||
103 | /** |
||
104 | * @since 2.4 |
||
105 | * |
||
106 | * @param string $displayTitle |
||
107 | * |
||
108 | * @return DIProperty|false |
||
109 | */ |
||
110 | 1 | public function getPropertyFromDisplayTitle( $displayTitle ) { |
|
111 | |||
112 | 1 | $descriptionFactory = new DescriptionFactory(); |
|
113 | |||
114 | 1 | $description = $descriptionFactory->newSomeProperty( |
|
115 | 1 | new DIProperty( '_DTITLE' ), |
|
116 | 1 | $descriptionFactory->newValueDescription( new DIBlob( $displayTitle ) ) |
|
117 | ); |
||
118 | |||
119 | 1 | $query = new Query( $description ); |
|
120 | 1 | $query->setLimit( 1 ); |
|
121 | |||
122 | 1 | $dataItems = $this->cachedPropertyValuesPrefetcher->queryPropertyValuesFor( |
|
123 | $query |
||
124 | ); |
||
125 | |||
126 | 1 | if ( is_array( $dataItems ) && $dataItems !== array() ) { |
|
127 | 1 | $dataItem = end( $dataItems ); |
|
128 | |||
129 | // Cache results as a linked list attached to |
||
130 | // the property so that it can be purged all together |
||
131 | |||
132 | 1 | return new DIProperty( $dataItem->getDBKey() ); |
|
133 | } |
||
134 | |||
135 | return false; |
||
136 | } |
||
137 | |||
138 | /** |
||
139 | * @since 2.4 |
||
140 | * |
||
141 | * @param DIProperty $property |
||
142 | * |
||
143 | * @return boolean |
||
144 | */ |
||
145 | 4 | public function hasUniquenessConstraintBy( DIProperty $property ) { |
|
146 | |||
147 | 4 | $hasUniquenessConstraint = false; |
|
148 | 4 | $key = 'uc:'. $property->getKey(); |
|
149 | |||
150 | // Guard against high frequency lookup |
||
151 | 4 | if ( $this->intermediaryMemoryCache->contains( $key ) ) { |
|
152 | 3 | return $this->intermediaryMemoryCache->fetch( $key ); |
|
153 | } |
||
154 | |||
155 | 4 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
156 | 4 | $property->getCanonicalDiWikiPage(), |
|
0 ignored issues
–
show
|
|||
157 | 4 | new DIProperty( '_PVUC' ) |
|
158 | ); |
||
159 | |||
160 | 4 | if ( is_array( $dataItems ) && $dataItems !== array() ) { |
|
161 | 4 | $hasUniquenessConstraint = end( $dataItems )->getBoolean(); |
|
162 | } |
||
163 | |||
164 | 4 | $this->intermediaryMemoryCache->save( $key, $hasUniquenessConstraint ); |
|
165 | |||
166 | 4 | return $hasUniquenessConstraint; |
|
167 | } |
||
168 | |||
169 | /** |
||
170 | * @since 2.5 |
||
171 | * |
||
172 | * @param DIProperty $property |
||
173 | * |
||
174 | * @return DataItem|null |
||
175 | */ |
||
176 | 2 | public function getExternalFormatterUriBy( DIProperty $property ) { |
|
177 | |||
178 | 2 | $dataItem = null; |
|
179 | |||
180 | 2 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
181 | 2 | $property->getCanonicalDiWikiPage(), |
|
0 ignored issues
–
show
It seems like
$property->getCanonicalDiWikiPage() can be null ; however, getPropertyValues() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
182 | 2 | new DIProperty( '_PEFU' ) |
|
183 | ); |
||
184 | |||
185 | 2 | if ( is_array( $dataItems ) && $dataItems !== array() ) { |
|
186 | 2 | $dataItem = end( $dataItems ); |
|
187 | } |
||
188 | |||
189 | 2 | return $dataItem; |
|
190 | } |
||
191 | |||
192 | /** |
||
193 | * @since 2.4 |
||
194 | * |
||
195 | * @param DIProperty $property |
||
196 | * |
||
197 | * @return string |
||
198 | */ |
||
199 | 198 | public function getAllowedPatternBy( DIProperty $property ) { |
|
200 | |||
201 | 198 | $allowsPattern = ''; |
|
202 | 198 | $key = 'ap:'. $property->getKey(); |
|
203 | |||
204 | // Guard against high frequency lookup |
||
205 | 198 | if ( $this->intermediaryMemoryCache->contains( $key ) ) { |
|
206 | 163 | return $this->intermediaryMemoryCache->fetch( $key ); |
|
207 | } |
||
208 | |||
209 | 177 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
210 | 177 | $property->getCanonicalDiWikiPage(), |
|
0 ignored issues
–
show
It seems like
$property->getCanonicalDiWikiPage() can be null ; however, getPropertyValues() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
211 | 177 | new DIProperty( '_PVAP' ) |
|
212 | ); |
||
213 | |||
214 | 177 | if ( is_array( $dataItems ) && $dataItems !== array() ) { |
|
215 | 2 | $allowsPattern = end( $dataItems )->getString(); |
|
216 | } |
||
217 | |||
218 | 177 | $this->intermediaryMemoryCache->save( $key, $allowsPattern ); |
|
219 | |||
220 | 177 | return $allowsPattern; |
|
221 | } |
||
222 | |||
223 | /** |
||
224 | * @since 2.4 |
||
225 | * |
||
226 | * @param DIProperty $property |
||
227 | * |
||
228 | * @return integer|false |
||
229 | */ |
||
230 | 206 | public function getAllowedValuesBy( DIProperty $property ) { |
|
231 | |||
232 | 206 | $allowsValues = array(); |
|
233 | 206 | $key = 'al:'. $property->getKey(); |
|
234 | |||
235 | // Guard against high frequency lookup |
||
236 | 206 | if ( $this->intermediaryMemoryCache->contains( $key ) ) { |
|
237 | 174 | return $this->intermediaryMemoryCache->fetch( $key ); |
|
238 | } |
||
239 | |||
240 | 185 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
241 | 185 | $property->getCanonicalDiWikiPage(), |
|
242 | 185 | new DIProperty( '_PVAL' ) |
|
243 | ); |
||
244 | |||
245 | 185 | if ( is_array( $dataItems ) && $dataItems !== array() ) { |
|
246 | 5 | $allowsValues = $dataItems; |
|
247 | } |
||
248 | |||
249 | 185 | $this->intermediaryMemoryCache->save( $key, $allowsValues ); |
|
250 | |||
251 | 185 | return $allowsValues; |
|
252 | } |
||
253 | |||
254 | /** |
||
255 | * @since 2.4 |
||
256 | * |
||
257 | * @param DIProperty $property |
||
258 | * |
||
259 | * @return integer|false |
||
260 | */ |
||
261 | 40 | public function getDisplayPrecisionBy( DIProperty $property ) { |
|
262 | |||
263 | 40 | $displayPrecision = false; |
|
264 | |||
265 | 40 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
266 | 40 | $property->getCanonicalDiWikiPage(), |
|
0 ignored issues
–
show
It seems like
$property->getCanonicalDiWikiPage() can be null ; however, getPropertyValues() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
267 | 40 | new DIProperty( '_PREC' ) |
|
268 | ); |
||
269 | |||
270 | 40 | if ( $dataItems !== false && $dataItems !== array() ) { |
|
271 | 4 | $dataItem = end( $dataItems ); |
|
272 | 4 | $displayPrecision = abs( (int)$dataItem->getNumber() ); |
|
273 | } |
||
274 | |||
275 | 40 | return $displayPrecision; |
|
276 | } |
||
277 | |||
278 | /** |
||
279 | * @since 2.4 |
||
280 | * |
||
281 | * @param DIProperty $property |
||
282 | * |
||
283 | * @return array |
||
284 | */ |
||
285 | 18 | public function getDisplayUnitsBy( DIProperty $property ) { |
|
286 | |||
287 | 18 | $units = array(); |
|
288 | |||
289 | 18 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
290 | 18 | $property->getCanonicalDiWikiPage(), |
|
0 ignored issues
–
show
It seems like
$property->getCanonicalDiWikiPage() can be null ; however, getPropertyValues() does not accept null , maybe add an additional type check?
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: /** @return stdClass|null */
function mayReturnNull() { }
function doesNotAcceptNull(stdClass $x) { }
// With potential error.
function withoutCheck() {
$x = mayReturnNull();
doesNotAcceptNull($x); // Potential error here.
}
// Safe - Alternative 1
function withCheck1() {
$x = mayReturnNull();
if ( ! $x instanceof stdClass) {
throw new \LogicException('$x must be defined.');
}
doesNotAcceptNull($x);
}
// Safe - Alternative 2
function withCheck2() {
$x = mayReturnNull();
if ($x instanceof stdClass) {
doesNotAcceptNull($x);
}
}
Loading history...
|
|||
291 | 18 | new DIProperty( '_UNIT' ) |
|
292 | ); |
||
293 | |||
294 | 18 | if ( $dataItems !== false && $dataItems !== array() ) { |
|
295 | 6 | foreach ( $dataItems as $dataItem ) { |
|
296 | 6 | $units = array_merge( $units, preg_split( '/\s*,\s*/u', $dataItem->getString() ) ); |
|
297 | } |
||
298 | } |
||
299 | |||
300 | 18 | return $units; |
|
301 | } |
||
302 | |||
303 | /** |
||
304 | * We try to cache anything to avoid unnecessary store connections or DB |
||
305 | * lookups. For cases where a property was changed, the EventDipatcher will |
||
306 | * receive a 'property.specification.change' event (emitted as soon as the content of |
||
307 | * a property page was altered) with PropertySpecificationLookup::resetCacheBy |
||
308 | * being invoked to remove the cache entry for that specific property. |
||
309 | * |
||
310 | * @since 2.4 |
||
311 | * |
||
312 | * @param DIProperty $property |
||
313 | * @param mixed|null $linker |
||
314 | * @param string $languageCode |
||
315 | * |
||
316 | * @return string |
||
317 | */ |
||
318 | 66 | public function getPropertyDescriptionBy( DIProperty $property, $linker = null, $languageCode = '' ) { |
|
319 | |||
320 | // Take the linker into account (Special vs. in page rendering etc.) |
||
321 | 66 | $languageCode = $languageCode === '' ? $this->languageCode : $languageCode; |
|
322 | 66 | $key = '--pdesc:' . $languageCode . ':' . ( $linker === null ? '0' : '1' ); |
|
323 | |||
324 | 66 | $blobStore = $this->cachedPropertyValuesPrefetcher->getBlobStore(); |
|
325 | |||
326 | 66 | $container = $blobStore->read( |
|
327 | 66 | $this->cachedPropertyValuesPrefetcher->getRootHashFrom( $property->getCanonicalDiWikiPage() ) |
|
328 | ); |
||
329 | |||
330 | 66 | if ( $container->has( $key ) ) { |
|
331 | 51 | return $container->get( $key ); |
|
332 | } |
||
333 | |||
334 | 65 | $localPropertyDescription = $this->tryToFindLocalPropertyDescription( |
|
335 | $property, |
||
336 | $linker, |
||
337 | $languageCode |
||
338 | ); |
||
339 | |||
340 | // If a local property description wasn't available for a predefined property |
||
341 | // the try to find a system translation |
||
342 | 65 | if ( trim( $localPropertyDescription ) === '' && !$property->isUserDefined() ) { |
|
343 | 14 | $localPropertyDescription = $this->getPredefinedPropertyDescription( $property, $linker, $languageCode ); |
|
344 | } |
||
345 | |||
346 | 65 | $container->set( $key, $localPropertyDescription ); |
|
347 | |||
348 | 65 | $blobStore->save( |
|
349 | $container |
||
350 | ); |
||
351 | |||
352 | 65 | return $localPropertyDescription; |
|
353 | } |
||
354 | |||
355 | 14 | private function getPredefinedPropertyDescription( $property, $linker, $languageCode ) { |
|
356 | |||
357 | 14 | $description = ''; |
|
358 | 14 | $key = $property->getKey(); |
|
359 | |||
360 | 14 | if ( ( $msgKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) === '' ) { |
|
361 | 14 | $msgKey = 'smw-pa-property-predefined' . strtolower( $key ); |
|
362 | } |
||
363 | |||
364 | 14 | if ( !Message::exists( $msgKey ) ) { |
|
365 | 1 | return $description; |
|
366 | } |
||
367 | |||
368 | 14 | $dataValue = DataValueFactory::getInstance()->newDataValueByItem( |
|
369 | $property |
||
370 | ); |
||
371 | |||
372 | 14 | $label = $dataValue->getFormattedLabel(); |
|
373 | |||
374 | 14 | $message = Message::get( |
|
375 | 14 | array( $msgKey, $label ), |
|
376 | 14 | $linker === null ? Message::ESCAPED : Message::PARSE, |
|
377 | $languageCode |
||
378 | ); |
||
379 | |||
380 | 14 | return $message; |
|
381 | } |
||
382 | |||
383 | 65 | private function tryToFindLocalPropertyDescription( $property, $linker, $languageCode ) { |
|
384 | |||
385 | 65 | $text = ''; |
|
386 | 65 | $descriptionProperty = new DIProperty( '_PDESC' ); |
|
387 | |||
388 | 65 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
389 | 65 | $property->getCanonicalDiWikiPage(), |
|
390 | $descriptionProperty |
||
391 | ); |
||
392 | |||
393 | 65 | if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $descriptionProperty, $languageCode ) ) !== null ) { |
|
394 | 6 | $text = $dataValue->getShortWikiText( $linker ); |
|
395 | } |
||
396 | |||
397 | 65 | return $text; |
|
398 | } |
||
399 | |||
400 | 201 | private function findPreferredPropertyLabel( $property, $languageCode ) { |
|
401 | |||
402 | 201 | $text = ''; |
|
403 | 201 | $preferredProperty = new DIProperty( '_PPLB' ); |
|
404 | |||
405 | 201 | $dataItems = $this->cachedPropertyValuesPrefetcher->getPropertyValues( |
|
406 | 201 | $property->getCanonicalDiWikiPage(), |
|
407 | $preferredProperty |
||
408 | ); |
||
409 | |||
410 | 201 | if ( ( $dataValue = $this->findTextValueByLanguage( $dataItems, $preferredProperty, $languageCode ) ) !== null ) { |
|
411 | 3 | $text = $dataValue->getShortWikiText(); |
|
412 | } |
||
413 | |||
414 | 201 | return $text; |
|
415 | } |
||
416 | |||
417 | 204 | private function findTextValueByLanguage( $dataItems, $property, $languageCode ) { |
|
418 | |||
419 | 204 | if ( $dataItems === null || $dataItems === array() ) { |
|
420 | 203 | return null; |
|
421 | } |
||
422 | |||
423 | 8 | foreach ( $dataItems as $dataItem ) { |
|
424 | |||
425 | 8 | $dataValue = DataValueFactory::getInstance()->newDataValueByItem( |
|
426 | $dataItem, |
||
427 | $property |
||
428 | ); |
||
429 | |||
430 | // Here a MonolingualTextValue was retunred therefore the method |
||
431 | // can be called without validation |
||
432 | 8 | $dv = $dataValue->getTextValueByLanguage( $languageCode ); |
|
433 | |||
434 | 8 | if ( $dv !== null ) { |
|
435 | 8 | return $dv; |
|
436 | } |
||
437 | } |
||
438 | |||
439 | 1 | return null; |
|
440 | } |
||
441 | |||
442 | } |
||
443 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: