GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — 2.6.x (#2509)
by Nicolas
04:59
created

Lang   D

Complexity

Total Complexity 80

Size/Duplication

Total Lines 564
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 564
rs 4.8717
wmc 80
lcom 1
cbo 5

19 Methods

Rating   Name   Duplication   Size   Complexity  
A Dictionary() 0 4 1
A Languages() 0 4 1
A Transliterations() 0 4 1
B initialize() 0 26 4
A createLanguage() 0 10 1
F fetch() 0 77 19
C set() 0 45 11
B isLanguageEnabled() 0 16 5
B load() 0 17 6
A get() 0 4 1
C translate() 0 23 9
B getAvailableLanguages() 0 14 5
A isLocalized() 0 4 1
A localizeDate() 0 11 3
B standardizeDate() 0 22 5
A createHandle() 0 9 2
A createFilename() 0 10 2
A applyTransliterations() 0 12 1
A isUnicodeCompiled() 0 4 2

How to fix   Complexity   

Complex Class

Complex classes like Lang often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Lang, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
* @package toolkit
4
*/
5
6
/**
7
 * The Lang class loads and manages languages
8
 */
9
class Lang
10
{
11
    /**
12
     * Array of transliterations
13
     * @var array
14
     */
15
    private static $_transliterations;
16
17
    /**
18
     * Code of active language
19
     * @var string
20
     */
21
    private static $_lang;
22
23
    /**
24
     * Context information of all available languages
25
     * @var array
26
     */
27
    private static $_languages;
28
29
    /**
30
     * Array of localized strings
31
     * @var array
32
     */
33
    private static $_dictionary;
34
35
    /**
36
     * Array of localized date and time strings
37
     * @var array
38
     */
39
    private static $_datetime_dictionary;
40
41
    /**
42
     * Get the current dictionary
43
     *
44
     * @return array
45
     *  Return the dictionary
46
     */
47
    public static function Dictionary()
48
    {
49
        return self::$_dictionary;
50
    }
51
52
    /**
53
     * Get a list of either enabled or disabled languages. Example:
54
     *
55
     *      array(
56
     *          [...]
57
     *
58
     *          'en' => array(
59
     *              'name' => 'English',
60
     *              'handle' => 'english',
61
     *              'extensions' => array()
62
     *          ),
63
     *
64
     *          'it' => array(
65
     *              'name' => 'Italiano',
66
     *              'handle' => 'italian',
67
     *              'extensions' => array(
68
     *                  [...]
69
     *              )
70
     *          ),
71
     *
72
     *          [...]
73
     *      )
74
     *
75
     * @see toolkit.Lang#createLanguage()
76
     * @since Symphony 2.3
77
     * @return array
78
     *  Return an array of languages (both enabled and disabled)
79
     */
80
    public static function Languages()
81
    {
82
        return self::$_languages;
83
    }
84
85
    /**
86
     * Get transliterations
87
     *
88
     * @return array
89
     *  Returns the transliterations array
90
     */
91
    public static function Transliterations()
92
    {
93
        return self::$_transliterations;
94
    }
95
96
    /**
97
     * Initialize transliterations, datetime dictionary and languages array.
98
     */
99
    public static function initialize()
100
    {
101
        self::$_dictionary = array();
102
103
        // Load default datetime strings
104
        if (empty(self::$_datetime_dictionary)) {
105
            include LANG . '/datetime.php';
106
107
            self::$_datetime_dictionary = $datetime_strings;
0 ignored issues
show
Bug introduced by
The variable $datetime_strings does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
108
        }
109
110
        // Load default transliterations
111
        if (empty(self::$_transliterations)) {
112
            include LANG . '/transliterations.php';
113
114
            self::$_transliterations = $transliterations;
0 ignored issues
show
Bug introduced by
The variable $transliterations does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
115
        }
116
117
        // Load default English language
118
        if (empty(self::$_languages)) {
119
            self::$_languages = self::createLanguage('en', 'English', 'english');
120
        }
121
122
        // Fetch all available languages
123
        self::fetch();
124
    }
125
126
    /**
127
     * Create an array of Language information for internal use.
128
     *
129
     * @since Symphony 2.3
130
     * @param string $code
131
     *  Language code, e. g. 'en' or 'pt-br'
132
     * @param string $name
133
     *  Language name
134
     * @param string $handle (optional)
135
     *  Handle for the given language, used to build a valid 'lang_$handle' extension's handle.
136
     *  Defaults to null.
137
     * @param array $extensions (optional)
138
     *  An array of extensions that support the given language.
139
     * @return array
140
     *  An array of Language information.
141
     */
142
    private static function createLanguage($code, $name, $handle = null, array $extensions = array())
143
    {
144
        return array(
145
            $code => array(
146
                'name' => $name,
147
                'handle' => $handle,
148
                'extensions' => $extensions
149
            )
150
        );
151
    }
152
153
    /**
154
     * Fetch all languages available in the core language folder and the language extensions.
155
     * The function stores all language information in the private variable `$_languages`.
156
     * It contains an array with the name and handle of each language and an array of all
157
     * extensions available in that language.
158
     *
159
     * @throws UnexpectedValueException
160
     * @throws RuntimeException
161
     */
162
    private static function fetch()
163
    {
164
        if (!@is_readable(EXTENSIONS)) {
165
            return;
166
        }
167
168
        // Fetch extensions
169
        $extensions = new DirectoryIterator(EXTENSIONS);
170
171
        // Language extensions
172
        foreach ($extensions as $extension) {
173
            if ($extension->isDot() || $extension->isFile()) {
174
                continue;
175
            }
176
177
            // Core translations
178
            $core_handle = (strpos($extension->getFilename(), 'lang_') !== false)
179
                ? str_replace('lang_', '', $extension->getFilename())
180
                : null;
181
182
            // Loop over the `/lang` directory of this `$extension` searching for language
183
            // files. If `/lang` isn't a directory, `UnexpectedValueException` will be
184
            // thrown.
185
            try {
186
                $path = $extension->getPathname() . '/lang';
187
                if (!is_dir($path)) {
188
                    continue;
189
                }
190
191
                $directory = new DirectoryIterator($path);
192
                foreach ($directory as $file) {
193
                    if ($file->isDot() || !preg_match('/\.php$/', $file->getPathname())) {
194
                        continue;
195
                    }
196
197
                    include($file->getPathname());
198
199
                    // Get language code
200
                    $code = explode('.', $file);
201
                    $code = $code[1];
202
                    $lang = isset(self::$_languages[$code]) ? self::$_languages[$code] : null;
203
204
                    // Available extensions
205
                    $extensions = (isset($lang)) ? $lang['extensions'] : array();
206
207
                    // Core translations
208
                    if ($core_handle) {
209
                        $handle = $core_handle;
210
211
                        // Extension translations
212
                    } else {
213
                        $handle = (isset($lang)) ? $lang['handle'] : null;
214
                        $extensions = array_merge(array($extension->getFilename()), $extensions);
215
                    }
216
217
                    // Merge languages ($about is declared inside the included $file)
218
                    $temp = self::createLanguage($code, $about['name'], $handle, $extensions);
0 ignored issues
show
Bug introduced by
The variable $about does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
219
220
                    if (isset($lang)) {
221
                        foreach ($lang as $key => $value) {
222
                            // Prevent missing or nulled values overwriting existing values
223
                            // which can occur if a translation file is not correct.
224
                            if (!isset($temp[$code][$key]) || empty($temp[$code][$key])) {
225
                                continue;
226
                            }
227
228
                            self::$_languages[$code][$key] = $temp[$code][$key];
229
                        }
230
                    } else {
231
                        self::$_languages[$code] = $temp[$code];
232
                    }
233
                }
234
            } catch (Exception $ex) {
235
                continue;
236
            }
237
        }
238
    }
239
240
    /**
241
     * Set system language, load translations for core and extensions. If the specified language
242
     * cannot be found, Symphony will default to English.
243
     *
244
     * Note: Beginning with Symphony 2.2 translations bundled with extensions will only be loaded
245
     * when the core dictionary of the specific language is available.
246
     *
247
     * @param string $code
248
     *  Language code, e. g. 'en' or 'pt-br'
249
     * @param boolean $checkStatus (optional)
250
     *  If false, set the language even if it's not enabled. Defaults to true.
251
     */
252
    public static function set($code, $checkStatus = true)
253
    {
254
        if (!$code || $code == self::get()) {
255
            return;
256
        }
257
258
        // Store current language code
259
        self::$_lang = $code;
260
261
        // Clear dictionary
262
        self::$_dictionary = array();
263
264
        // Language file available
265
        if ($code !== 'en' && (self::isLanguageEnabled($code) || $checkStatus === false)) {
266
            // Load core translations
267
            self::load(vsprintf('%s/lang_%s/lang/lang.%s.php', array(
268
                EXTENSIONS, self::$_languages[$code]['handle'], $code
269
            )));
270
271
            // Load extension translations
272
            if (is_array(self::$_languages[$code]['extensions'])) {
273
                foreach (self::$_languages[$code]['extensions'] as $extension) {
274
                    self::load(vsprintf('%s/%s/lang/lang.%s.php', array(
275
                        EXTENSIONS, $extension, $code
276
                    )));
277
                }
278
            }
279
280
            // Language file unavailable, use default language
281
        } else {
282
            self::$_lang = 'en';
283
284
            include LANG . '/transliterations.php';
285
            self::$_transliterations = $transliterations;
0 ignored issues
show
Bug introduced by
The variable $transliterations does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
286
287
            // Log error, if possible
288
            if ($code !== 'en' && class_exists('Symphony', false) && Symphony::Log() instanceof Log) {
289
                Symphony::Log()->pushToLog(
290
                    __('The selected language, %s, could not be found. Using default English dictionary instead.', array($code)),
291
                    E_ERROR,
292
                    true
293
                );
294
            }
295
        }
296
    }
297
298
    /**
299
     * Given a valid language code, this function checks if the language is enabled.
300
     *
301
     * @since Symphony 2.3
302
     * @param string $code
303
     *  Language code, e. g. 'en' or 'pt-br'
304
     * @return boolean
305
     *  If true, the language is enabled.
306
     */
307
    public static function isLanguageEnabled($code)
308
    {
309
        if ($code == 'en') {
310
            return true;
311
        }
312
313
        $handle = (isset(self::$_languages[$code])) ? self::$_languages[$code]['handle'] : '';
314
        $enabled_extensions = array();
315
316
        // Fetch list of active extensions
317
        if (class_exists('Symphony', false) && (!is_null(Symphony::ExtensionManager()))) {
318
            $enabled_extensions = Symphony::ExtensionManager()->listInstalledHandles();
319
        }
320
321
        return in_array('lang_' . $handle, $enabled_extensions);
322
    }
323
324
    /**
325
     * Load language file. Each language file contains three arrays:
326
     * about, dictionary and transliterations.
327
     *
328
     * @param string $path
329
     *  Path of the language file that should be loaded
330
     */
331
    private static function load($path)
332
    {
333
        // Load language file
334
        if (file_exists($path)) {
335
            require($path);
336
        }
337
338
        // Populate dictionary ($dictionary is declared inside $path)
339
        if (isset($dictionary) && is_array($dictionary)) {
0 ignored issues
show
Bug introduced by
The variable $dictionary seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
340
            self::$_dictionary = array_merge(self::$_dictionary, $dictionary);
341
        }
342
343
        // Populate transliterations ($transliterations is declared inside $path)
344
        if (isset($transliterations) && is_array($transliterations)) {
0 ignored issues
show
Bug introduced by
The variable $transliterations seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
345
            self::$_transliterations = array_merge(self::$_transliterations, $transliterations);
346
        }
347
    }
348
349
    /**
350
     * Get current language
351
     *
352
     * @return string
353
     */
354
    public static function get()
355
    {
356
        return self::$_lang;
357
    }
358
359
    /**
360
     * This function is an internal alias for `__()`.
361
     *
362
     * @since Symphony 2.3
363
     * @see toolkit.__()
364
     * @param string $string
365
     *  The string that should be translated
366
     * @param array $inserts (optional)
367
     *  Optional array used to replace translation placeholders, defaults to NULL
368
     * @param string $namespace (optional)
369
     *  Optional string used to define the namespace, defaults to NULL.
370
     * @return string
371
     *  Returns the translated string
372
     */
373
    public static function translate($string, array $inserts = null, $namespace = null)
374
    {
375
        if (is_null($namespace) && class_exists('Symphony', false)) {
376
            $namespace = Symphony::getPageNamespace();
377
        }
378
379
        if (isset($namespace) && isset(self::$_dictionary[$namespace][$string])) {
380
            $translated = self::$_dictionary[$namespace][$string];
381
        } elseif (isset(self::$_dictionary[$string])) {
382
            $translated = self::$_dictionary[$string];
383
        } else {
384
            $translated = $string;
385
        }
386
387
        $translated = empty($translated) ? $string : $translated;
388
389
        // Replace translation placeholders
390
        if (is_array($inserts) && !empty($inserts)) {
391
            $translated = vsprintf($translated, $inserts);
392
        }
393
394
        return $translated;
395
    }
396
397
    /**
398
     * Get an array of the codes and names of all languages that are available system wide.
399
     *
400
     * Note: Beginning with Symphony 2.2 language files are only available
401
     * when the language extension is explicitly enabled.
402
     *
403
     * @param boolean $checkStatus (optional)
404
     *  If false, retrieves a list a languages that support core translation.
405
     * @return array
406
     *  Returns an associative array of language codes and names, e. g. 'en' => 'English'
407
     */
408
    public static function getAvailableLanguages($checkStatus = true)
409
    {
410
        $languages = array();
411
412
        // Get available languages
413
        foreach (self::$_languages as $key => $language) {
414
            if (self::isLanguageEnabled($key) || ($checkStatus === false && isset($language['handle']))) {
415
                $languages[$key] = $language['name'];
416
            }
417
        }
418
419
        // Return languages codes
420
        return $languages;
421
    }
422
423
    /**
424
     * Check if Symphony is localised.
425
     *
426
     * @return boolean
427
     *  Returns true for localized system, false for English system
428
     */
429
    public static function isLocalized()
430
    {
431
        return (self::get() !== 'en');
432
    }
433
434
    /**
435
     * Localize dates.
436
     *
437
     * @param string $string
438
     *  Standard date that should be localized
439
     * @return string
440
     *  Return the given date with translated month and day names
441
     */
442
    public static function localizeDate($string)
443
    {
444
        // Only translate dates in localized environments
445
        if (self::isLocalized()) {
446
            foreach (self::$_datetime_dictionary as $value) {
447
                $string = preg_replace('/\b' . $value . '\b/i', self::translate($value), $string);
448
            }
449
        }
450
451
        return $string;
452
    }
453
454
    /**
455
     * Standardize dates.
456
     *
457
     * @param string $string
458
     *  Localized date that should be standardized
459
     * @return string
460
     *  Returns the given date with English month and day names
461
     */
462
    public static function standardizeDate($string)
463
    {
464
        // Only standardize dates in localized environments
465
        if (self::isLocalized()) {
466
467
            // Translate names to English
468
            foreach (self::$_datetime_dictionary as $values) {
469
                $string = preg_replace('/\b' . self::translate($values) . '\b/i' . (self::isUnicodeCompiled() === true ? 'u' : null), $values, $string);
470
            }
471
472
            // Replace custom date and time separator with space:
473
            // This is important, otherwise the `DateTime` constructor may break
474
            // @todo Test if this separator is still required. It's a hidden setting
475
            // and users are only aware of it if they go digging/pointed in the right direction
476
            $separator = Symphony::Configuration()->get('datetime_separator', 'region');
477
            if ($separator !== ' ') {
478
                $string = str_replace($separator, ' ', $string);
479
            }
480
        }
481
482
        return $string;
483
    }
484
485
    /**
486
     * Given a string, this will clean it for use as a Symphony handle. Preserves multi-byte characters.
487
     *
488
     * @param string $string
489
     *  String to be cleaned up
490
     * @param integer $max_length
491
     *  The maximum number of characters in the handle
492
     * @param string $delim
493
     *  All non-valid characters will be replaced with this
494
     * @param boolean $uriencode
495
     *  Force the resultant string to be uri encoded making it safe for URLs
496
     * @param boolean $apply_transliteration
497
     *  If true, this will run the string through an array of substitution characters
498
     * @param array $additional_rule_set
499
     *  An array of REGEX patterns that should be applied to the `$string`. This
500
     *  occurs after the string has been trimmed and joined with the `$delim`
501
     * @return string
502
     *  Returns resultant handle
503
     */
504
    public static function createHandle($string, $max_length = 255, $delim = '-', $uriencode = false, $apply_transliteration = true, $additional_rule_set = null)
505
    {
506
        // Use the transliteration table if provided
507
        if ($apply_transliteration === true) {
508
            $string = self::applyTransliterations($string);
509
        }
510
511
        return General::createHandle($string, $max_length, $delim, $uriencode, $additional_rule_set);
512
    }
513
514
    /**
515
     * Given a string, this will clean it for use as a filename. Preserves multi-byte characters.
516
     *
517
     * @param string $string
518
     *  String to be cleaned up
519
     * @param string $delim
520
     *  Replacement for invalid characters
521
     * @param boolean $apply_transliteration
522
     *  If true, umlauts and special characters will be substituted
523
     * @return string
524
     *  Returns created filename
525
     */
526
    public static function createFilename($string, $delim='-', $apply_transliteration = true)
527
    {
528
        // Use the transliteration table if provided
529
        if ($apply_transliteration === true) {
530
            $file = pathinfo($string);
531
            $string = self::applyTransliterations($file['filename']) . '.' . $file['extension'];
532
        }
533
534
        return General::createFilename($string, $delim);
535
    }
536
537
    /**
538
     * This function replaces special characters according to the values stored inside
539
     * `$_transliterations`.
540
     *
541
     * @since Symphony 2.3
542
     * @param string $string
543
     *  The string that should be cleaned-up
544
     * @return mixed
545
     *  Returns the transliterated string
546
     */
547
    private static function applyTransliterations($string)
548
    {
549
        // Apply the straight transliterations with strtr as it's much faster
550
        $string = strtr($string, self::$_transliterations['straight']);
551
552
        // Apply the regex rules over the resulting $string
553
        return preg_replace(
554
            array_keys(self::$_transliterations['regexp']),
555
            array_values(self::$_transliterations['regexp']),
556
            $string
557
        );
558
    }
559
560
    /**
561
     * Returns boolean if PHP has been compiled with unicode support. This is
562
     * useful to determine if unicode modifier's can be used in regular expression's
563
     *
564
     * @link http://stackoverflow.com/questions/4509576/detect-if-pcre-was-built-without-the-enable-unicode-properties-or-enable-utf8
565
     * @since Symphony 2.2.2
566
     * @return boolean
567
     */
568
    public static function isUnicodeCompiled()
569
    {
570
        return (@preg_match('/\pL/u', 'a') == 1 ? true : false);
571
    }
572
}
573