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);
Loading history...
|
|||
| 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);
Loading history...
|
|||
| 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: