Add explicit `return null;` statements
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / language / LanguageFactory.class.php
CommitLineData
11ade432 1<?php
a9229942 2
11ade432 3namespace wcf\system\language;
a9229942 4
a935240b 5use wcf\data\language\category\LanguageCategory;
11ade432 6use wcf\data\language\Language;
e6c6346d 7use wcf\data\language\LanguageEditor;
b401cd0d 8use wcf\system\cache\builder\LanguageCacheBuilder;
61022658 9use wcf\system\SingletonFactory;
a9229942 10use wcf\system\template\TemplateScriptingCompiler;
11ade432 11use wcf\system\WCF;
11ade432
AE
12
13/**
14 * Handles language related functions.
a9229942
TD
15 *
16 * @author Alexander Ebert
17 * @copyright 2001-2019 WoltLab GmbH
18 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
19 * @package WoltLabSuite\Core\System\Language
11ade432 20 */
a9229942
TD
21class LanguageFactory extends SingletonFactory
22{
23 /**
24 * language cache
25 * @var mixed[]
26 */
27 protected $cache;
28
29 /**
30 * initialized languages
31 * @var Language[]
32 */
33 protected $languages = [];
34
35 /**
36 * active template scripting compiler
37 * @var TemplateScriptingCompiler
38 */
39 protected $scriptingCompiler;
40
41 /**
42 * @inheritDoc
43 */
44 protected function init()
45 {
46 $this->loadCache();
47 }
48
49 /**
50 * Returns a Language object for the language with the given id.
51 *
52 * @param int $languageID
c0b28aa2 53 * @return Language|null
a9229942
TD
54 */
55 public function getLanguage($languageID)
56 {
57 if (!isset($this->languages[$languageID])) {
58 if (!isset($this->cache['languages'][$languageID])) {
c0b28aa2 59 return null;
a9229942
TD
60 }
61
62 $this->languages[$languageID] = $this->cache['languages'][$languageID];
63 }
64
65 return $this->languages[$languageID];
66 }
67
68 /**
69 * Returns the preferred language of the current user.
70 *
71 * @param int $languageID
72 * @return Language
73 */
74 public function getUserLanguage($languageID = 0)
75 {
76 if ($languageID) {
77 $language = $this->getLanguage($languageID);
78 if ($language !== null) {
79 return $language;
80 }
81 }
82
83 $languageID = $this->findPreferredLanguage();
84
85 return $this->getLanguage($languageID);
86 }
87
88 /**
89 * Returns the language with the given language code or null if no such
90 * language exists.
91 *
92 * @param string $languageCode
93 * @return Language
94 */
95 public function getLanguageByCode($languageCode)
96 {
97 // called within WCFSetup
98 if ($this->cache === false || empty($this->cache['codes'])) {
99 $sql = "SELECT languageID
100 FROM wcf" . WCF_N . "_language
101 WHERE languageCode = ?";
102 $statement = WCF::getDB()->prepareStatement($sql);
103 $statement->execute([$languageCode]);
104 $row = $statement->fetchArray();
105 if (isset($row['languageID'])) {
106 return new Language($row['languageID']);
107 }
108 } elseif (isset($this->cache['codes'][$languageCode])) {
109 return $this->getLanguage($this->cache['codes'][$languageCode]);
110 }
5227ebc7
MS
111
112 return null;
a9229942
TD
113 }
114
115 /**
116 * Returns true if the language category with the given name exists.
117 *
118 * @param string $categoryName
119 * @return bool
120 */
121 public function isValidCategory($categoryName)
122 {
123 return isset($this->cache['categories'][$categoryName]);
124 }
125
126 /**
127 * Returns the language category with the given name.
128 *
129 * @param string $categoryName
5227ebc7 130 * @return LanguageCategory|null
a9229942
TD
131 */
132 public function getCategory($categoryName)
133 {
813c41ce 134 return $this->cache['categories'][$categoryName] ?? null;
a9229942
TD
135 }
136
137 /**
138 * Returns language category by id.
139 *
140 * @param int $languageCategoryID
5227ebc7 141 * @return LanguageCategory|null
a9229942
TD
142 */
143 public function getCategoryByID($languageCategoryID)
144 {
145 if (isset($this->cache['categoryIDs'][$languageCategoryID])) {
146 return $this->cache['categories'][$this->cache['categoryIDs'][$languageCategoryID]];
147 }
5227ebc7
MS
148
149 return null;
a9229942
TD
150 }
151
152 /**
153 * Returns a list of available language categories.
154 *
155 * @return LanguageCategory[]
156 */
157 public function getCategories()
158 {
159 return $this->cache['categories'];
160 }
161
162 /**
163 * Searches the preferred language of the current user.
164 */
165 protected function findPreferredLanguage()
166 {
167 // get available language codes
168 $availableLanguageCodes = [];
169 foreach ($this->getLanguages() as $language) {
170 $availableLanguageCodes[] = $language->languageCode;
171 }
172
173 // get default language
174 $defaultLanguageCode = $this->cache['languages'][$this->cache['default']]->languageCode;
175
176 // get preferred language
177 $languageCode = self::getPreferredLanguage($availableLanguageCodes, $defaultLanguageCode);
178
179 // get language id of preferred language
180 foreach ($this->cache['languages'] as $key => $language) {
181 if ($language->languageCode == $languageCode) {
182 return $key;
183 }
184 }
185 }
186
187 /**
188 * Determines the preferred language of the current user.
189 *
190 * @param array $availableLanguageCodes
191 * @param string $defaultLanguageCode
192 * @return string
193 */
194 public static function getPreferredLanguage($availableLanguageCodes, $defaultLanguageCode)
195 {
196 if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && $_SERVER['HTTP_ACCEPT_LANGUAGE']) {
197 $acceptedLanguages = \explode(',', \str_replace('_', '-', \strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE'])));
198 foreach ($acceptedLanguages as $acceptedLanguage) {
199 foreach ($availableLanguageCodes as $availableLanguageCode) {
200 $fixedCode = \strtolower(self::fixLanguageCode($availableLanguageCode));
201
202 if (
203 $fixedCode == $acceptedLanguage
204 || $fixedCode == \preg_replace('%^([a-z]{2}).*$%i', '$1', $acceptedLanguage)
205 ) {
206 return $availableLanguageCode;
207 }
208 }
209 }
210 }
211
212 return $defaultLanguageCode;
213 }
214
215 /**
216 * Returns the active scripting compiler object.
217 *
218 * @return TemplateScriptingCompiler
219 */
220 public function getScriptingCompiler()
221 {
222 if ($this->scriptingCompiler === null) {
223 $this->scriptingCompiler = new TemplateScriptingCompiler(WCF::getTPL());
224 }
225
226 return $this->scriptingCompiler;
227 }
228
229 /**
230 * Loads the language cache.
231 */
232 protected function loadCache()
233 {
234 if (\defined('WCF_N')) {
235 $this->cache = LanguageCacheBuilder::getInstance()->getData();
236 }
237 }
238
239 /**
240 * Clears languages cache.
241 */
242 public function clearCache()
243 {
244 LanguageCacheBuilder::getInstance()->reset();
245 }
246
247 /**
248 * Removes additional language identifier from given language code.
249 * Converts e.g. 'de-informal' to 'de'.
250 *
251 * @param string $languageCode
252 * @return string $languageCode
253 */
254 public static function fixLanguageCode($languageCode)
255 {
256 return \preg_replace('/-[a-z0-9]+/', '', $languageCode);
257 }
258
259 /**
260 * Returns the default language object.
261 *
262 * @return Language
263 * @since 3.0
264 */
265 public function getDefaultLanguage()
266 {
267 return $this->getLanguage($this->cache['default']);
268 }
269
270 /**
271 * Returns the default language id
272 *
273 * @return int
274 */
275 public function getDefaultLanguageID()
276 {
277 return $this->cache['default'];
278 }
279
280 /**
281 * Returns all available languages.
282 *
283 * @return Language[]
284 */
285 public function getLanguages()
286 {
287 return $this->cache['languages'];
288 }
289
290 /**
291 * Returns all available content languages for given package.
292 *
293 * @return Language[]
294 */
295 public function getContentLanguages()
296 {
297 $availableLanguages = [];
298 foreach ($this->getLanguages() as $languageID => $language) {
299 if ($language->hasContent) {
300 $availableLanguages[$languageID] = $language;
301 }
302 }
303
304 return $availableLanguages;
305 }
306
307 /**
308 * Returns the list of content language ids.
309 *
310 * @return int[]
311 * @since 3.1
312 */
313 public function getContentLanguageIDs()
314 {
315 $languageIDs = [];
316 foreach ($this->getLanguages() as $language) {
317 if ($language->hasContent) {
318 $languageIDs[] = $language->languageID;
319 }
320 }
321
322 return $languageIDs;
323 }
324
325 /**
326 * Makes given language the default language.
327 *
328 * @param int $languageID
329 */
330 public function makeDefault($languageID)
331 {
332 // remove old default language
333 $sql = "UPDATE wcf" . WCF_N . "_language
334 SET isDefault = 0
335 WHERE isDefault = 1";
336 $statement = WCF::getDB()->prepareStatement($sql);
337 $statement->execute();
338
339 // make this language to default
340 $sql = "UPDATE wcf" . WCF_N . "_language
341 SET isDefault = 1
342 WHERE languageID = ?";
343 $statement = WCF::getDB()->prepareStatement($sql);
344 $statement->execute([$languageID]);
345
346 // rebuild language cache
347 $this->clearCache();
348 }
349
350 /**
351 * Removes language cache and compiled templates.
352 */
353 public function deleteLanguageCache()
354 {
355 LanguageEditor::deleteLanguageFiles();
356
357 foreach ($this->cache['languages'] as $language) {
358 $languageEditor = new LanguageEditor($language);
359 $languageEditor->deleteCompiledTemplates();
360 }
361 }
362
363 /**
364 * Returns true if multilingualism is enabled.
365 *
366 * @return bool
367 */
368 public function multilingualismEnabled()
369 {
370 return $this->cache['multilingualismEnabled'];
371 }
372
373 /**
374 * Returns the number of phrases that have been automatically disabled in the past 7 days.
375 *
376 * @return int
377 */
378 public function countRecentlyDisabledCustomValues()
379 {
380 $sql = "SELECT COUNT(*) AS count
381 FROM wcf" . WCF_N . "_language_item
382 WHERE languageCustomItemDisableTime >= ?";
383 $statement = WCF::getDB()->prepareStatement($sql, 1);
384 $statement->execute([TIME_NOW - 86400 * 7]);
385
386 return $statement->fetchSingleColumn();
387 }
11ade432 388}