This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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 Wikibase\Lib; |
||
4 | |||
5 | use Babel; |
||
6 | use ExtensionRegistry; |
||
7 | use IContextSource; |
||
8 | use InvalidArgumentException; |
||
9 | use Language; |
||
10 | use LanguageConverter; |
||
11 | use MediaWiki\Languages\LanguageConverterFactory; |
||
12 | use MediaWiki\Languages\LanguageFactory; |
||
13 | use MediaWiki\Languages\LanguageFallback; |
||
14 | use MediaWiki\MediaWikiServices; |
||
15 | use MWException; |
||
16 | use User; |
||
17 | |||
18 | /** |
||
19 | * Object creating TermLanguageFallbackChain objects in Wikibase. |
||
20 | * |
||
21 | * @license GPL-2.0-or-later |
||
22 | * @author Liangent < [email protected] > |
||
23 | */ |
||
24 | class LanguageFallbackChainFactory { |
||
25 | |||
26 | /** |
||
27 | * Fallback levels |
||
28 | */ |
||
29 | const FALLBACK_ALL = 0xff; |
||
30 | |||
31 | /** |
||
32 | * The language itself, e.g. 'en' for 'en'. |
||
33 | */ |
||
34 | const FALLBACK_SELF = 1; |
||
35 | |||
36 | /** |
||
37 | * Other compatible languages that can be translated into the requested language |
||
38 | * (and translation is automatically done), e.g. 'sr', 'sr-ec' and 'sr-el' for 'sr'. |
||
39 | */ |
||
40 | const FALLBACK_VARIANTS = 2; |
||
41 | |||
42 | /** |
||
43 | * All other language from the system fallback chain, e.g. 'de' and 'en' for 'de-formal'. |
||
44 | */ |
||
45 | const FALLBACK_OTHERS = 4; |
||
46 | |||
47 | /** @var LanguageFactory */ |
||
48 | private $languageFactory; |
||
49 | |||
50 | /** @var LanguageConverterFactory */ |
||
51 | private $languageConverterFactory; |
||
52 | |||
53 | /** @var LanguageFallback */ |
||
54 | private $languageFallback; |
||
55 | |||
56 | /** |
||
57 | * @var array[] |
||
58 | */ |
||
59 | private $languageCache = []; |
||
60 | |||
61 | /** |
||
62 | * @var array[] |
||
63 | */ |
||
64 | private $userLanguageCache = []; |
||
65 | |||
66 | public function __construct( |
||
67 | ?LanguageFactory $languageFactory = null, |
||
68 | ?LanguageConverterFactory $languageConverterFactory = null, |
||
69 | ?LanguageFallback $languageFallback = null |
||
70 | ) { |
||
71 | $services = MediaWikiServices::getInstance(); |
||
72 | $this->languageFactory = $languageFactory ?: $services->getLanguageFactory(); |
||
73 | $this->languageConverterFactory = $languageConverterFactory ?: $services->getLanguageConverterFactory(); |
||
74 | $this->languageFallback = $languageFallback ?: $services->getLanguageFallback(); |
||
75 | } |
||
76 | |||
77 | /** |
||
78 | * Get the fallback chain based a single language, and specified fallback level. |
||
79 | * |
||
80 | * @param Language $language |
||
81 | * @param int $mode Bitfield of self::FALLBACK_* |
||
82 | * |
||
83 | * @return TermLanguageFallbackChain |
||
84 | */ |
||
85 | public function newFromLanguage( Language $language, $mode = self::FALLBACK_ALL ) { |
||
86 | $languageCode = $language->getCode(); |
||
87 | |||
88 | if ( !isset( $this->languageCache[$languageCode][$mode] ) ) { |
||
89 | $chain = $this->buildFromLanguage( $language, $mode ); |
||
90 | $this->languageCache[$languageCode][$mode] = new TermLanguageFallbackChain( |
||
91 | $chain, |
||
0 ignored issues
–
show
|
|||
92 | WikibaseContentLanguages::getDefaultInstance()->getContentLanguages( WikibaseContentLanguages::CONTEXT_TERM ) |
||
93 | ); |
||
94 | } |
||
95 | |||
96 | return $this->languageCache[$languageCode][$mode]; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Get the fallback chain based a single language code, and specified fallback level. |
||
101 | * |
||
102 | * @param string $languageCode |
||
103 | * @param int $mode Bitfield of self::FALLBACK_* |
||
104 | * |
||
105 | * @return TermLanguageFallbackChain |
||
106 | */ |
||
107 | public function newFromLanguageCode( $languageCode, $mode = self::FALLBACK_ALL ) { |
||
108 | $languageCode = LanguageWithConversion::validateLanguageCode( $languageCode ); |
||
109 | |||
110 | if ( !isset( $this->languageCache[$languageCode][$mode] ) ) { |
||
111 | $chain = $this->buildFromLanguage( $languageCode, $mode ); |
||
112 | $this->languageCache[$languageCode][$mode] = new TermLanguageFallbackChain( |
||
113 | $chain, |
||
0 ignored issues
–
show
$chain is of type array<integer,object<Wik...anguageWithConversion>> , but the function expects a array<integer,object<Wik...anguageWithConversion>> .
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);
![]() |
|||
114 | WikibaseContentLanguages::getDefaultInstance()->getContentLanguages( WikibaseContentLanguages::CONTEXT_TERM ) |
||
115 | ); |
||
116 | } |
||
117 | |||
118 | return $this->languageCache[$languageCode][$mode]; |
||
119 | } |
||
120 | |||
121 | /** |
||
122 | * Build fallback chain array for a given language or validated language code. |
||
123 | * |
||
124 | * @param Language|string $language Language object or language code as string |
||
125 | * @param int $mode Bitfield of self::FALLBACK_* |
||
126 | * @param TermLanguageFallbackChain[] $chain for recursive calls |
||
127 | * @param bool[] $fetched for recursive calls |
||
128 | * |
||
129 | * @throws InvalidArgumentException |
||
130 | * @return LanguageWithConversion[] |
||
131 | */ |
||
132 | private function buildFromLanguage( $language, $mode, array $chain = [], array &$fetched = [] ) { |
||
133 | if ( !is_int( $mode ) ) { |
||
134 | throw new InvalidArgumentException( '$mode must be an integer' ); |
||
135 | } |
||
136 | |||
137 | if ( is_string( $language ) ) { |
||
138 | $languageCode = $language; |
||
139 | } else { |
||
140 | $languageCode = $language->getCode(); |
||
141 | } |
||
142 | |||
143 | if ( $mode & self::FALLBACK_SELF ) { |
||
144 | if ( !isset( $fetched[$languageCode] ) ) { |
||
145 | $chain[] = LanguageWithConversion::factory( $language ); |
||
146 | $fetched[$languageCode] = true; |
||
147 | } |
||
148 | } |
||
149 | |||
150 | if ( $mode & self::FALLBACK_VARIANTS ) { |
||
151 | $parentLanguage = null; |
||
152 | $pieces = explode( '-', $languageCode, 2 ); |
||
153 | |||
154 | if ( in_array( $pieces[0], LanguageConverter::$languagesWithVariants ) ) { |
||
155 | if ( is_string( $language ) ) { |
||
156 | $language = $this->languageFactory->getLanguage( $language ); |
||
157 | } |
||
158 | $parentLanguage = $this->languageFactory->getParentLanguage( $language->getCode() ); |
||
159 | } |
||
160 | |||
161 | if ( $parentLanguage ) { |
||
162 | // It's less likely to trigger conversion mistakes by converting |
||
163 | // zh-tw to zh-hk first instead of converting zh-cn to zh-tw. |
||
164 | $parentLanguageConverter = $this->languageConverterFactory->getLanguageConverter( $parentLanguage ); |
||
165 | $variantFallbacks = $parentLanguageConverter->getVariantFallbacks( $languageCode ); |
||
166 | if ( is_array( $variantFallbacks ) ) { |
||
167 | $variants = array_unique( array_merge( |
||
168 | $variantFallbacks, |
||
169 | $parentLanguageConverter->getVariants() |
||
170 | ) ); |
||
171 | } else { |
||
172 | $variants = $parentLanguageConverter->getVariants(); |
||
173 | } |
||
174 | |||
175 | foreach ( $variants as $variant ) { |
||
176 | if ( !isset( $fetched[$variant] ) |
||
177 | // The self::FALLBACK_SELF mode is already responsible for self-references. |
||
178 | && $variant !== $languageCode |
||
179 | && $parentLanguageConverter->hasVariant( $variant ) |
||
180 | ) { |
||
181 | $chain[] = LanguageWithConversion::factory( $language, $variant ); |
||
182 | $fetched[$variant] = true; |
||
183 | } |
||
184 | } |
||
185 | } |
||
186 | } |
||
187 | |||
188 | if ( $mode & self::FALLBACK_OTHERS ) { |
||
189 | // Regarding $mode in recursive calls: |
||
190 | // * self is a must to have the fallback item itself included; |
||
191 | // * respect the original caller about whether to include variants or not; |
||
192 | // * others should be excluded as they'll be handled here in loops. |
||
193 | $recursiveMode = $mode; |
||
194 | $recursiveMode &= self::FALLBACK_VARIANTS; |
||
195 | $recursiveMode |= self::FALLBACK_SELF; |
||
196 | |||
197 | $fallbacks = $this->languageFallback->getAll( $languageCode ); |
||
198 | foreach ( $fallbacks as $other ) { |
||
199 | $chain = $this->buildFromLanguage( $other, $recursiveMode, $chain, $fetched ); |
||
0 ignored issues
–
show
$chain is of type array<integer,object<Wik...anguageWithConversion>> , but the function expects a array<integer,object<Wik...LanguageFallbackChain>> .
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);
![]() |
|||
200 | } |
||
201 | } |
||
202 | |||
203 | return $chain; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Construct the fallback chain based on a context. Currently it just uses user and language info in it. |
||
208 | * |
||
209 | * @param IContextSource $context |
||
210 | * |
||
211 | * @return TermLanguageFallbackChain |
||
212 | */ |
||
213 | public function newFromContext( IContextSource $context ) { |
||
214 | return $this->newFromUserAndLanguageCode( $context->getUser(), $context->getLanguage()->getCode() ); |
||
215 | } |
||
216 | |||
217 | /** |
||
218 | * Construct the fallback chain based on a context, but ignore the language info in it and use a specified one instead. |
||
219 | * |
||
220 | * @param IContextSource $context |
||
221 | * @param string $languageCode |
||
222 | * |
||
223 | * @return TermLanguageFallbackChain |
||
224 | */ |
||
225 | public function newFromContextAndLanguageCode( IContextSource $context, $languageCode ) { |
||
226 | return $this->newFromUserAndLanguageCode( $context->getUser(), $languageCode ); |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Construct the fallback chain based on a user and a language, currently from data provided by Extension:Babel. |
||
231 | * |
||
232 | * @param User $user |
||
233 | * @param string $languageCode |
||
234 | * |
||
235 | * @return TermLanguageFallbackChain |
||
236 | */ |
||
237 | public function newFromUserAndLanguageCode( User $user, $languageCode ) { |
||
238 | if ( !ExtensionRegistry::getInstance()->isLoaded( 'Babel' ) || $user->isAnon() ) { |
||
239 | return $this->newFromLanguageCode( $languageCode, self::FALLBACK_ALL ); |
||
240 | } |
||
241 | |||
242 | $languageCode = LanguageWithConversion::validateLanguageCode( $languageCode ); |
||
243 | |||
244 | if ( isset( $this->userLanguageCache[$user->getName()][$languageCode] ) ) { |
||
245 | return $this->userLanguageCache[$user->getName()][$languageCode]; |
||
246 | } |
||
247 | |||
248 | $babel = $this->getBabel( $languageCode, $user ); |
||
249 | |||
250 | $chain = $this->buildFromBabel( $babel ); |
||
251 | $languageFallbackChain = new TermLanguageFallbackChain( |
||
252 | $chain, |
||
253 | WikibaseContentLanguages::getDefaultInstance()->getContentLanguages( WikibaseContentLanguages::CONTEXT_TERM ) |
||
254 | ); |
||
255 | |||
256 | $this->userLanguageCache[$user->getName()][$languageCode] = $languageFallbackChain; |
||
257 | |||
258 | return $languageFallbackChain; |
||
259 | } |
||
260 | |||
261 | private function getBabel( $languageCode, $user ) { |
||
262 | $babel = []; |
||
263 | |||
264 | $babelCategoryNames = $this->getBabelCategoryNames(); |
||
265 | |||
266 | if ( count( $babelCategoryNames ) ) { |
||
267 | // A little redundant but it's the only way to get required information with current Babel API. |
||
268 | $previousLevelBabel = []; |
||
269 | |||
270 | foreach ( $babelCategoryNames as $level => $_ ) { |
||
271 | // Make the current language at the top of the chain. |
||
272 | $levelBabel = array_unique( array_merge( |
||
273 | [ $languageCode ], |
||
274 | Babel::getCachedUserLanguages( $user, $level ) |
||
275 | ) ); |
||
276 | |||
277 | $babel[$level] = array_diff( $levelBabel, $previousLevelBabel ); |
||
278 | $previousLevelBabel = $levelBabel; |
||
279 | } |
||
280 | } else { |
||
281 | $babel['N'] = [ $languageCode ]; |
||
282 | } |
||
283 | |||
284 | return $babel; |
||
285 | } |
||
286 | |||
287 | private function getBabelCategoryNames() { |
||
288 | global $wgBabelCategoryNames; |
||
289 | |||
290 | $babelCategoryNames = array_filter( |
||
291 | $wgBabelCategoryNames, |
||
292 | function( $category ) { |
||
293 | return $category !== false; |
||
294 | } |
||
295 | ); |
||
296 | |||
297 | krsort( $babelCategoryNames ); |
||
298 | |||
299 | return $babelCategoryNames; |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * Build fallback chain array for a given babel array. |
||
304 | * |
||
305 | * @param array $babel |
||
306 | * |
||
307 | * @return LanguageWithConversion[] |
||
308 | */ |
||
309 | public function buildFromBabel( array $babel ) { |
||
310 | $chain = []; |
||
311 | $fetched = []; |
||
312 | |||
313 | // First pass to get "compatible" languages (self and variants) |
||
314 | foreach ( $babel as $languageCodes ) { // Already sorted when added |
||
315 | foreach ( [ self::FALLBACK_SELF, self::FALLBACK_VARIANTS ] as $mode ) { |
||
316 | foreach ( $languageCodes as $languageCode ) { |
||
317 | try { |
||
318 | $languageCode = LanguageWithConversion::validateLanguageCode( $languageCode ); |
||
319 | } catch ( MWException $e ) { |
||
0 ignored issues
–
show
|
|||
320 | continue; |
||
321 | } |
||
322 | $chain = $this->buildFromLanguage( $languageCode, $mode, $chain, $fetched ); |
||
323 | } |
||
324 | } |
||
325 | } |
||
326 | |||
327 | // Second pass to get other languages from system fallback chain |
||
328 | foreach ( $babel as $languageCodes ) { |
||
329 | foreach ( $languageCodes as $languageCode ) { |
||
330 | try { |
||
331 | $languageCode = LanguageWithConversion::validateLanguageCode( $languageCode ); |
||
332 | } catch ( MWException $e ) { |
||
0 ignored issues
–
show
|
|||
333 | continue; |
||
334 | } |
||
335 | $chain = $this->buildFromLanguage( |
||
336 | $languageCode, |
||
337 | self::FALLBACK_OTHERS | self::FALLBACK_VARIANTS, |
||
338 | $chain, |
||
339 | $fetched |
||
340 | ); |
||
341 | } |
||
342 | } |
||
343 | |||
344 | return $chain; |
||
345 | } |
||
346 | |||
347 | } |
||
348 |
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: