1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Wikibase\Lib\Store; |
4
|
|
|
|
5
|
|
|
use Wikibase\DataModel\Entity\EntityId; |
6
|
|
|
use Wikibase\DataModel\Services\Lookup\LabelDescriptionLookupException; |
7
|
|
|
use Wikibase\DataModel\Term\TermFallback; |
8
|
|
|
use Wikibase\Lib\FormatterCache\TermFallbackCacheFacade; |
9
|
|
|
use Wikibase\Lib\TermLanguageFallbackChain; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* @license GPL-2.0-or-later |
13
|
|
|
* |
14
|
|
|
* @note The class uses immutable cache approach: cached data never changes once persisted. |
15
|
|
|
* For this purpose we not only include Item ID in cache key construction, but also |
16
|
|
|
* Item's current revision ID. Revisions never change, the cached data does not need |
17
|
|
|
* to change either, which means that we don't need to purge caches. As soon as new revision |
18
|
|
|
* is created, cache key will change and old cache data will eventually be purged by |
19
|
|
|
* the caching system (eg. APC, Memcached, ...) based on a Least Recently Used strategy |
20
|
|
|
* as soon as no code will request it anymore. |
21
|
|
|
*/ |
22
|
|
|
class CachingFallbackLabelDescriptionLookup implements FallbackLabelDescriptionLookup { |
23
|
|
|
|
24
|
|
|
use TermCacheKeyBuilder; |
25
|
|
|
|
26
|
|
|
private const LABEL = 'label'; |
27
|
|
|
private const DESCRIPTION = 'description'; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* @var TermFallbackCacheFacade |
31
|
|
|
*/ |
32
|
|
|
private $cache; |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* @var RedirectResolvingLatestRevisionLookup |
36
|
|
|
*/ |
37
|
|
|
private $redirectResolvingRevisionLookup; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var FallbackLabelDescriptionLookup |
41
|
|
|
*/ |
42
|
|
|
private $labelDescriptionLookup; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var TermLanguageFallbackChain |
46
|
|
|
*/ |
47
|
|
|
private $termLanguageFallbackChain; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @param TermFallbackCacheFacade $fallbackCache |
51
|
|
|
* @param RedirectResolvingLatestRevisionLookup $redirectResolvingRevisionLookup |
52
|
|
|
* @param FallbackLabelDescriptionLookup $labelDescriptionLookup |
53
|
|
|
* @param TermLanguageFallbackChain $termLanguageFallbackChain |
54
|
|
|
*/ |
55
|
|
|
public function __construct( |
56
|
|
|
TermFallbackCacheFacade $fallbackCache, |
57
|
|
|
RedirectResolvingLatestRevisionLookup $redirectResolvingRevisionLookup, |
58
|
|
|
FallbackLabelDescriptionLookup $labelDescriptionLookup, |
59
|
|
|
TermLanguageFallbackChain $termLanguageFallbackChain |
60
|
|
|
) { |
61
|
|
|
$this->cache = $fallbackCache; |
62
|
|
|
$this->redirectResolvingRevisionLookup = $redirectResolvingRevisionLookup; |
63
|
|
|
$this->labelDescriptionLookup = $labelDescriptionLookup; |
64
|
|
|
$this->termLanguageFallbackChain = $termLanguageFallbackChain; |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @param EntityId $entityId |
69
|
|
|
* |
70
|
|
|
* @throws LabelDescriptionLookupException |
71
|
|
|
* @return TermFallback|null |
72
|
|
|
*/ |
73
|
|
|
public function getDescription( EntityId $entityId ) { |
74
|
|
|
$languageCodes = $this->termLanguageFallbackChain->getFetchLanguageCodes(); |
75
|
|
|
if ( !$languageCodes ) { |
|
|
|
|
76
|
|
|
// Can happen when the current interface language is not a valid term language, e.g. "de-formal" |
77
|
|
|
return null; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
$languageCode = $languageCodes[0]; |
81
|
|
|
$description = $this->getTerm( $entityId, $languageCode, self::DESCRIPTION ); |
|
|
|
|
82
|
|
|
|
83
|
|
|
return $description; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @param EntityId $entityId |
88
|
|
|
* |
89
|
|
|
* @throws LabelDescriptionLookupException |
90
|
|
|
* @return TermFallback|null |
91
|
|
|
*/ |
92
|
|
|
public function getLabel( EntityId $entityId ) { |
93
|
|
|
$languageCodes = $this->termLanguageFallbackChain->getFetchLanguageCodes(); |
94
|
|
|
if ( !$languageCodes ) { |
|
|
|
|
95
|
|
|
// Can happen when the current interface language is not a valid term language, e.g. "de-formal" |
96
|
|
|
return null; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
$languageCode = $languageCodes[0]; |
100
|
|
|
$label = $this->getTerm( $entityId, $languageCode, self::LABEL ); |
|
|
|
|
101
|
|
|
|
102
|
|
|
return $label; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
private function getTerm( EntityId $entityId, $languageCode, $termName = self::LABEL ) { |
106
|
|
|
$resolutionResult = $this->redirectResolvingRevisionLookup->lookupLatestRevisionResolvingRedirect( $entityId ); |
107
|
|
|
if ( $resolutionResult === null ) { |
108
|
|
|
return null; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
list( $revisionId, $targetEntityId ) = $resolutionResult; |
112
|
|
|
|
113
|
|
|
$termFallback = $this->cache->get( $targetEntityId, $revisionId, $languageCode, $termName ); |
114
|
|
|
if ( $termFallback === TermFallbackCacheFacade::NO_VALUE ) { |
115
|
|
|
$termFallback = $termName === self::LABEL |
116
|
|
|
? $this->labelDescriptionLookup->getLabel( $targetEntityId ) |
117
|
|
|
: $this->labelDescriptionLookup->getDescription( $targetEntityId ); |
118
|
|
|
|
119
|
|
|
$this->cache->set( $termFallback, $targetEntityId, $revisionId, $languageCode, $termName ); |
120
|
|
|
|
121
|
|
|
return $termFallback; |
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
return $termFallback; |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.