Passed
Push — master ( 122983...91227d )
by William
02:34 queued 26s
created

Loader::setCacheFactory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
    Copyright (c) 2005 Steven Armstrong <sa at c-area dot ch>
7
    Copyright (c) 2009 Danilo Segan <[email protected]>
8
    Copyright (c) 2016 Michal Čihař <[email protected]>
9
10
    This file is part of MoTranslator.
11
12
    This program is free software; you can redistribute it and/or modify
13
    it under the terms of the GNU General Public License as published by
14
    the Free Software Foundation; either version 2 of the License, or
15
    (at your option) any later version.
16
17
    This program is distributed in the hope that it will be useful,
18
    but WITHOUT ANY WARRANTY; without even the implied warranty of
19
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
    GNU General Public License for more details.
21
22
    You should have received a copy of the GNU General Public License along
23
    with this program; if not, write to the Free Software Foundation, Inc.,
24
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25
*/
26
27
namespace PhpMyAdmin\MoTranslator;
28
29
use PhpMyAdmin\MoTranslator\Cache\CacheFactoryInterface;
30
use PhpMyAdmin\MoTranslator\Cache\InMemoryCache;
31
32
use function array_push;
33
use function file_exists;
34
use function getenv;
35
use function in_array;
36
use function preg_match;
37
use function sprintf;
38
39
class Loader
40
{
41
    /**
42
     * Loader instance.
43
     *
44
     * @static
45
     * @var Loader
46
     */
47
    private static $instance = null;
48
49
    /**
50
     * Factory to return a factory responsible for returning a `CacheInterface`
51
     *
52
     * @static
53
     * @var CacheFactoryInterface|null
54
     */
55
    private static $cacheFactory = null;
56
57
    /**
58
     * Default gettext domain to use.
59
     *
60
     * @var string
61
     */
62
    private $defaultDomain = '';
63
64
    /**
65
     * Configured locale.
66
     *
67
     * @var string
68
     */
69
    private $locale = '';
70
71
    /**
72
     * Loaded domains.
73
     *
74
     * @var array<string,array<string,Translator>>
75
     */
76
    private $domains = [];
77
78
    /**
79
     * Bound paths for domains.
80
     *
81
     * @var array<string,string>
82
     */
83
    private $paths = ['' => './'];
84
85
    /**
86
     * Returns the singleton Loader object.
87
     *
88
     * @return Loader object
89
     */
90 24
    public static function getInstance(): Loader
91
    {
92 24
        if (self::$instance === null) {
93 4
            self::$instance = new self();
94
        }
95
96 24
        return self::$instance;
97
    }
98
99
    /**
100
     * Loads global localization functions.
101
     */
102 8
    public static function loadFunctions(): void
103
    {
104 8
        require_once __DIR__ . '/functions.php';
105 2
    }
106
107
    /**
108
     * Figure out all possible locale names and start with the most
109
     * specific ones.  I.e. for sr_CS.UTF-8@latin, look through all of
110
     * sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr.
111
     *
112
     * @param string $locale Locale code
113
     *
114
     * @return string[] list of locales to try for any POSIX-style locale specification
115
     */
116 84
    public static function listLocales(string $locale): array
117
    {
118 84
        $localeNames = [];
119
120 84
        if ($locale) {
121
            if (
122 80
                preg_match(
123
                    '/^(?P<lang>[a-z]{2,3})' // language code
124
                    . '(?:_(?P<country>[A-Z]{2}))?' // country code
125
                    . '(?:\\.(?P<charset>[-A-Za-z0-9_]+))?' // charset
126 20
                    . '(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/', // @ modifier
127 20
                    $locale,
128 20
                    $matches
129
                )
130
            ) {
131 76
                $lang = $matches['lang'] ?? null;
132 76
                $country = $matches['country'] ?? null;
133 76
                $charset = $matches['charset'] ?? null;
134 76
                $modifier = $matches['modifier'] ?? null;
135
136 76
                if ($modifier) {
137 20
                    if ($country) {
138 8
                        if ($charset) {
139 8
                            array_push(
140 2
                                $localeNames,
141 8
                                sprintf('%s_%s.%s@%s', $lang, $country, $charset, $modifier)
142
                            );
143
                        }
144
145 8
                        array_push(
146 2
                            $localeNames,
147 8
                            sprintf('%s_%s@%s', $lang, $country, $modifier)
148
                        );
149 12
                    } elseif ($charset) {
150 4
                        array_push(
151 1
                            $localeNames,
152 4
                            sprintf('%s.%s@%s', $lang, $charset, $modifier)
153
                        );
154
                    }
155
156 20
                    array_push(
157 5
                        $localeNames,
158 20
                        sprintf('%s@%s', $lang, $modifier)
159
                    );
160
                }
161
162 76
                if ($country) {
163 40
                    if ($charset) {
164 12
                        array_push(
165 3
                            $localeNames,
166 12
                            sprintf('%s_%s.%s', $lang, $country, $charset)
167
                        );
168
                    }
169
170 40
                    array_push(
171 10
                        $localeNames,
172 40
                        sprintf('%s_%s', $lang, $country)
173
                    );
174 40
                } elseif ($charset) {
175 8
                    array_push(
176 2
                        $localeNames,
177 8
                        sprintf('%s.%s', $lang, $charset)
178
                    );
179
                }
180
181 76
                array_push($localeNames, $lang);
182
            }
183
184
            // If the locale name doesn't match POSIX style, just include it as-is.
185 80
            if (! in_array($locale, $localeNames)) {
186 4
                array_push($localeNames, $locale);
187
            }
188
        }
189
190 84
        return $localeNames;
191
    }
192
193
    /**
194
     * Sets factory responsible for composing a `CacheInterface`
195
     */
196 4
    public static function setCacheFactory(?CacheFactoryInterface $cacheFactory): void
197
    {
198 4
        self::$cacheFactory = $cacheFactory;
199 1
    }
200
201
    /**
202
     * Returns Translator object for domain or for default domain.
203
     *
204
     * @param string $domain Translation domain
205
     */
206 44
    public function getTranslator(string $domain = ''): Translator
207
    {
208 44
        if (empty($domain)) {
209 24
            $domain = $this->defaultDomain;
210
        }
211
212 44
        if (! isset($this->domains[$this->locale])) {
213 36
            $this->domains[$this->locale] = [];
214
        }
215
216 44
        if (! isset($this->domains[$this->locale][$domain])) {
217 40
            if (isset($this->paths[$domain])) {
218 32
                $base = $this->paths[$domain];
219
            } else {
220 8
                $base = './';
221
            }
222
223 40
            $localeNames = $this->listLocales($this->locale);
224
225 40
            $filename = '';
226 40
            foreach ($localeNames as $locale) {
227 40
                $filename = $base . '/' . $locale . '/LC_MESSAGES/' . $domain . '.mo';
228 40
                if (file_exists($filename)) {
229 31
                    break;
230
                }
231
            }
232
233
            // We don't care about invalid path, we will get fallback
234
            // translator here
235 40
            $moParser = new MoParser($filename);
236 40
            if (self::$cacheFactory instanceof CacheFactoryInterface) {
237 4
                $cache = self::$cacheFactory->getInstance($moParser, $this->locale, $domain);
238
            } else {
239 36
                $cache = new InMemoryCache($moParser);
240
            }
241
242 40
            $this->domains[$this->locale][$domain] = new Translator($cache);
243
        }
244
245 44
        return $this->domains[$this->locale][$domain];
246
    }
247
248
    /**
249
     * Sets the path for a domain.
250
     *
251
     * @param string $domain Domain name
252
     * @param string $path   Path where to find locales
253
     */
254 44
    public function bindtextdomain(string $domain, string $path): void
255
    {
256 44
        $this->paths[$domain] = $path;
257 11
    }
258
259
    /**
260
     * Sets the default domain.
261
     *
262
     * @param string $domain Domain name
263
     */
264 40
    public function textdomain(string $domain): void
265
    {
266 40
        $this->defaultDomain = $domain;
267 10
    }
268
269
    /**
270
     * Sets a requested locale.
271
     *
272
     * @param string $locale Locale name
273
     *
274
     * @return string Set or current locale
275
     */
276 44
    public function setlocale(string $locale): string
277
    {
278 44
        if (! empty($locale)) {
279 44
            $this->locale = $locale;
280
        }
281
282 44
        return $this->locale;
283
    }
284
285
    /**
286
     * Detects currently configured locale.
287
     *
288
     * It checks:
289
     *
290
     * - global lang variable
291
     * - environment for LC_ALL, LC_MESSAGES and LANG
292
     *
293
     * @return string with locale name
294
     */
295 8
    public function detectlocale(): string
296
    {
297 8
        if (isset($GLOBALS['lang'])) {
298 4
            return $GLOBALS['lang'];
299
        }
300
301 4
        $locale = getenv('LC_ALL');
302 4
        if ($locale !== false) {
303 4
            return $locale;
304
        }
305
306 4
        $locale = getenv('LC_MESSAGES');
307 4
        if ($locale !== false) {
308 4
            return $locale;
309
        }
310
311 4
        $locale = getenv('LANG');
312 4
        if ($locale !== false) {
313 4
            return $locale;
314
        }
315
316 4
        return 'en';
317
    }
318
}
319