MediaWiki  master
LanguageFactory.php
Go to the documentation of this file.
1 <?php
30 
31 use Language;
36 use MWException;
37 
45  private $options;
46 
49 
51  private $langNameUtils;
52 
54  private $langFallback;
55 
57  private $langObjCache = [];
58 
60  private $parentLangCache = [];
61 
66  public const CONSTRUCTOR_OPTIONS = [
67  'DummyLanguageCodes',
68  'LangObjCacheSize',
69  ];
70 
77  public function __construct(
82  ) {
83  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
84 
85  $this->options = $options;
86  $this->localisationCache = $localisationCache;
87  $this->langNameUtils = $langNameUtils;
88  $this->langFallback = $langFallback;
89  }
90 
97  public function getLanguage( $code ) : Language {
98  $code = $this->options->get( 'DummyLanguageCodes' )[$code] ?? $code;
99 
100  // This is horrible, horrible code, but is necessary to support Language::$mLangObjCache
101  // per the deprecation policy. Kill with fire in 1.36!
102  if (
104  $this === MediaWikiServices::getInstance()->getLanguageFactory()
105  ) {
106  $this->langObjCache = Language::$mLangObjCache;
107  }
108 
109  // Get the language object to process
110  $langObj = $this->langObjCache[$code] ?? $this->newFromCode( $code );
111 
112  // Merge the language object in to get it up front in the cache
113  $this->langObjCache = array_merge( [ $code => $langObj ], $this->langObjCache );
114  // Get rid of the oldest ones in case we have an overflow
115  $this->langObjCache =
116  array_slice( $this->langObjCache, 0, $this->options->get( 'LangObjCacheSize' ), true );
117 
118  // As above, remove this in 1.36
119  if (
121  $this === MediaWikiServices::getInstance()->getLanguageFactory()
122  ) {
124  }
125 
126  return $langObj;
127  }
128 
136  private function newFromCode( $code, $fallback = false ) : Language {
137  if ( !$this->langNameUtils->isValidCode( $code ) ) {
138  throw new MWException( "Invalid language code \"$code\"" );
139  }
140 
141  $constructorArgs = [
142  $code,
146  ];
147 
148  if ( !$this->langNameUtils->isValidBuiltInCode( $code ) ) {
149  // It's not possible to customise this code with class files, so
150  // just return a Language object. This is to support uselang= hacks.
151  return new Language( ...$constructorArgs );
152  }
153 
154  // Check if there is a language class for the code
155  $class = $this->classFromCode( $code, $fallback );
156  // LanguageCode does not inherit Language
157  if ( class_exists( $class ) && is_a( $class, 'Language', true ) ) {
158  return new $class( ...$constructorArgs );
159  }
160 
161  // Keep trying the fallback list until we find an existing class
162  $fallbacks = $this->langFallback->getAll( $code );
163  foreach ( $fallbacks as $fallbackCode ) {
164  $class = $this->classFromCode( $fallbackCode );
165  if ( class_exists( $class ) ) {
166  // TODO allow additional dependencies to be injected for subclasses somehow
167  return new $class( ...$constructorArgs );
168  }
169  }
170 
171  throw new MWException( "Invalid fallback sequence for language '$code'" );
172  }
173 
179  private function classFromCode( $code, $fallback = true ) {
180  if ( $fallback && $code == 'en' ) {
181  return 'Language';
182  } else {
183  return 'Language' . str_replace( '-', '_', ucfirst( $code ) );
184  }
185  }
186 
195  public function getParentLanguage( $code ) {
196  // We deliberately use array_key_exists() instead of isset() because we cache null.
197  if ( !array_key_exists( $code, $this->parentLangCache ) ) {
198  $codeBase = explode( '-', $code )[0];
199  if ( !in_array( $codeBase, LanguageConverter::$languagesWithVariants ) ) {
200  $this->parentLangCache[$code] = null;
201  return null;
202  }
203 
204  $lang = $this->getLanguage( $codeBase );
205  if ( !$lang->hasVariant( $code ) ) {
206  $this->parentLangCache[$code] = null;
207  return null;
208  }
209 
210  $this->parentLangCache[$code] = $lang;
211  }
212 
213  return $this->parentLangCache[$code];
214  }
215 }
static hasInstance()
Returns true if an instance has already been initialized.
static array $mLangObjCache
Definition: Language.php:101
Internationalisation code.
if(!isset( $args[0])) $lang
static getInstance()
Returns the global default instance of the top level service locator.
A class for passing options to services.
getLanguage( $code)
Get a cached or new language object for a given language code.
newFromCode( $code, $fallback=false)
Create a language object for a given language code.
getParentLanguage( $code)
Get the "parent" language which has a converter to convert a "compatible" language (in another varian...
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys, without regard for order.
classFromCode( $code, $fallback=true)
$fallback
Definition: MessagesAb.php:11
static array $languagesWithVariants
languages supporting variants
A service that provides utilities to do with language names and codes.
return true
Definition: router.php:92
__construct(ServiceOptions $options, LocalisationCache $localisationCache, LanguageNameUtils $langNameUtils, LanguageFallback $langFallback)