Completed
Push — master ( 0a57e3...259aca )
by
unknown
07:19 queued 10s
created

CachingFallbackBasedTermLookup   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 120
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 5
dl 0
loc 120
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A getLabel() 0 3 1
A getDescription() 0 3 1
B getTerm() 0 44 8
A getLabels() 0 3 1
A getDescriptions() 0 3 1
A getLookup() 0 6 2
A lookupWithoutCache() 0 11 3
1
<?php
2
3
declare( strict_types=1 );
4
namespace Wikibase\Client\DataAccess\Scribunto;
5
6
use Language;
7
use LogicException;
8
use MediaWiki\Languages\LanguageFactory;
9
use MWException;
10
use Wikibase\DataModel\Entity\EntityId;
11
use Wikibase\DataModel\Services\Lookup\TermLookup;
12
use Wikibase\DataModel\Term\TermFallback;
13
use Wikibase\DataModel\Term\TermTypes;
14
use Wikibase\Lib\ContentLanguages;
15
use Wikibase\Lib\FormatterCache\TermFallbackCacheFacade;
16
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookup;
17
use Wikibase\Lib\Store\LanguageFallbackLabelDescriptionLookupFactory;
18
use Wikibase\Lib\Store\RedirectResolvingLatestRevisionLookup;
19
20
/**
21
 * This TermLookup allows exposes language based lookups for getLabel and getDescription
22
 * and is backed by the shared TermFallbackCache which stores TermFallback objects.
23
 *
24
 * The lookup tries to find the term in the cache and if not present builds a
25
 * LanguageFallbackLabelDescriptionLookup based on the TermFallbackChain generated by the requested Language.
26
 *
27
 * If the requested language does not match the actual language of the TermFallback this value
28
 * will still be written to the cache but not returned.
29
 *
30
 * If the TermFallback returns null this value will also be written to the cache as this means no term is available
31
 * in the requested language.
32
 *
33
 * @see TermFallbackCacheFacade
34
 *
35
 * @license GPL-2.0-or-later
36
 */
37
class CachingFallbackBasedTermLookup implements TermLookup {
38
39
	/** @var TermFallbackCacheFacade */
40
	private $formatterCache;
41
42
	/** @var RedirectResolvingLatestRevisionLookup */
43
	private $redirectResolvingLatestRevisionLookup;
44
45
	/** @var LanguageFallbackLabelDescriptionLookupFactory */
46
	private $lookupFactory;
47
48
	/** @var LanguageFactory */
49
	private $languageFactory;
50
51
	/** @var ContentLanguages */
52
	private $contentLanguages;
53
54
	/** @var LanguageFallbackLabelDescriptionLookup[] */
55
	private $lookups;
56
57
	public function __construct(
58
		TermFallbackCacheFacade $formatterCache,
59
		RedirectResolvingLatestRevisionLookup $redirectResolvingLatestRevisionLookup,
60
		LanguageFallbackLabelDescriptionLookupFactory $lookupFactory,
61
		LanguageFactory $languageFactory,
62
		ContentLanguages $contentLanguages
63
	) {
64
		$this->formatterCache = $formatterCache;
65
		$this->redirectResolvingLatestRevisionLookup = $redirectResolvingLatestRevisionLookup;
66
		$this->lookupFactory = $lookupFactory;
67
		$this->languageFactory = $languageFactory;
68
		$this->contentLanguages = $contentLanguages;
69
	}
70
71
	public function getLabel( EntityId $entityId, $languageCode ) {
72
		return $this->getTerm( $entityId, $languageCode, TermTypes::TYPE_LABEL );
73
	}
74
75
	public function getDescription( EntityId $entityId, $languageCode ) {
76
		return $this->getTerm( $entityId, $languageCode, TermTypes::TYPE_DESCRIPTION );
77
	}
78
79
	private function getTerm( EntityId $entityId, string $languageCode, string $termType ): ?string {
80
		$resolutionResult = $this->redirectResolvingLatestRevisionLookup->lookupLatestRevisionResolvingRedirect( $entityId );
81
		if ( $resolutionResult === null ) {
82
			return null;
83
		}
84
85
		// try getting the requested language first
86
		// probably less expensive than hitting memcache with an invalid language
87
		if ( !$this->contentLanguages->hasLanguage( $languageCode ) ) {
88
			return null;
89
		}
90
91
		try {
92
			$language = $this->languageFactory->getLanguage( $languageCode );
93
		} catch ( MWException $e ) {
0 ignored issues
show
Bug introduced by
The class MWException does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
94
			return null;
95
		}
96
97
		list( $revisionId, $targetEntityId ) = $resolutionResult;
98
99
		$termFallback = $this->formatterCache->get( $targetEntityId, $revisionId, $languageCode, $termType );
100
101
		// We have already cached the fact that there is no value for this term
102
		if ( $termFallback === null ) {
103
			return null;
104
		}
105
106
		if ( $termFallback === TermFallbackCacheFacade::NO_VALUE ) {
107
			// fetch fresh using a term lookup
108
			$termFallback = $this->lookupWithoutCache( $targetEntityId, $language, $termType );
109
110
			// this can be stored in cache, either the term or null
111
			$this->formatterCache->set( $termFallback, $targetEntityId, $revisionId, $languageCode, $termType );
112
113
			if ( $termFallback === null ) {
114
				return null;
115
			}
116
		}
117
118
		if ( $termFallback->getActualLanguageCode() === $languageCode ) {
119
			return $termFallback->getText();
120
		}
121
		return null;
122
	}
123
124
	public function getLabels( EntityId $entityId, array $languageCodes ) {
125
		throw new LogicException( 'Not implemented' );
126
	}
127
128
	public function getDescriptions( EntityId $entityId, array $languageCodes ) {
129
		throw new LogicException( 'Not implemented' );
130
	}
131
132
	private function getLookup( Language $language ): LanguageFallbackLabelDescriptionLookup {
133
		if ( !isset( $this->lookups[ $language->getCode() ] ) ) {
134
			$this->lookups[ $language->getCode() ] = $this->lookupFactory->newLabelDescriptionLookup( $language );
135
		}
136
		return $this->lookups[ $language->getCode() ];
137
	}
138
139
	/**
140
	 * @param EntityId $entityId
141
	 * @param Language $language
142
	 * @param $termType
143
	 * @return TermFallback|null
144
	 */
145
	private function lookupWithoutCache( EntityId $entityId, Language $language, $termType ): ?TermFallback {
146
		$withoutCacheLookup = $this->getLookup( $language );
147
148
		if ( $termType === TermTypes::TYPE_LABEL ) {
149
			return $withoutCacheLookup->getLabel( $entityId );
150
		} elseif ( $termType === TermTypes::TYPE_DESCRIPTION ) {
151
			return $withoutCacheLookup->getDescription( $entityId );
152
		}
153
154
		throw new LogicException( "$termType is not supported." );
155
	}
156
}
157