Passed
Pull Request — master (#42)
by
unknown
08:33
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 20
     * Bound paths for domains.
80
     *
81 20
     * @var array<string,string>
82 4
     */
83
    private $paths = ['' => './'];
84
85 20
    /**
86
     * Returns the singleton Loader object.
87
     *
88
     * @return Loader object
89
     */
90
    public static function getInstance(): Loader
91 8
    {
92
        if (self::$instance === null) {
93 8
            self::$instance = new self();
94 2
        }
95
96
        return self::$instance;
97
    }
98
99
    /**
100
     * Loads global localization functions.
101
     */
102
    public static function loadFunctions(): void
103
    {
104
        require_once __DIR__ . '/functions.php';
105 80
    }
106
107 80
    /**
108
     * Figure out all possible locale names and start with the most
109 80
     * 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 76
     *
112
     * @param string $locale Locale code
113
     *
114
     * @return string[] list of locales to try for any POSIX-style locale specification
115 19
     */
116 19
    public static function listLocales(string $locale): array
117 19
    {
118
        $localeNames = [];
119
120 72
        if ($locale) {
121 72
            if (
122 72
                preg_match(
123 72
                    '/^(?P<lang>[a-z]{2,3})' // language code
124
                    . '(?:_(?P<country>[A-Z]{2}))?' // country code
125 72
                    . '(?:\\.(?P<charset>[-A-Za-z0-9_]+))?' // charset
126 20
                    . '(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/', // @ modifier
127 8
                    $locale,
128 8
                    $matches
129 2
                )
130 8
            ) {
131
                $lang = $matches['lang'] ?? null;
132
                $country = $matches['country'] ?? null;
133
                $charset = $matches['charset'] ?? null;
134 8
                $modifier = $matches['modifier'] ?? null;
135 2
136 8
                if ($modifier) {
137
                    if ($country) {
138 12
                        if ($charset) {
139 4
                            array_push(
140 1
                                $localeNames,
141 4
                                sprintf('%s_%s.%s@%s', $lang, $country, $charset, $modifier)
142
                            );
143
                        }
144
145 20
                        array_push(
146 5
                            $localeNames,
147 20
                            sprintf('%s_%s@%s', $lang, $country, $modifier)
148
                        );
149
                    } elseif ($charset) {
150
                        array_push(
151 72
                            $localeNames,
152 36
                            sprintf('%s.%s@%s', $lang, $charset, $modifier)
153 12
                        );
154 3
                    }
155 12
156
                    array_push(
157
                        $localeNames,
158
                        sprintf('%s@%s', $lang, $modifier)
159 36
                    );
160 9
                }
161 36
162
                if ($country) {
163 40
                    if ($charset) {
164 8
                        array_push(
165 2
                            $localeNames,
166 8
                            sprintf('%s_%s.%s', $lang, $country, $charset)
167
                        );
168
                    }
169
170 72
                    array_push(
171
                        $localeNames,
172
                        sprintf('%s_%s', $lang, $country)
173
                    );
174 76
                } elseif ($charset) {
175 4
                    array_push(
176
                        $localeNames,
177
                        sprintf('%s.%s', $lang, $charset)
178
                    );
179 80
                }
180
181
                array_push($localeNames, $lang);
182
            }
183
184
            // If the locale name doesn't match POSIX style, just include it as-is.
185
            if (! in_array($locale, $localeNames)) {
186
                array_push($localeNames, $locale);
187 40
            }
188
        }
189 40
190 24
        return $localeNames;
191
    }
192
193 40
    /**
194 36
     * Sets factory responsible for composing a `CacheInterface`
195
     */
196
    public static function setCacheFactory(?CacheFactoryInterface $cacheFactory): void
197 40
    {
198 36
        self::$cacheFactory = $cacheFactory;
199 28
    }
200
201 8
    /**
202
     * Returns Translator object for domain or for default domain.
203
     *
204 36
     * @param string $domain Translation domain
205
     */
206 36
    public function getTranslator(string $domain = ''): Translator
207 36
    {
208 36
        if (empty($domain)) {
209 36
            $domain = $this->defaultDomain;
210 30
        }
211
212
        if (! isset($this->domains[$this->locale])) {
213
            $this->domains[$this->locale] = [];
214
        }
215
216 36
        if (! isset($this->domains[$this->locale][$domain])) {
217
            if (isset($this->paths[$domain])) {
218
                $base = $this->paths[$domain];
219 40
            } else {
220
                $base = './';
221
            }
222
223
            $localeNames = $this->listLocales($this->locale);
224
225
            $filename = '';
226
            foreach ($localeNames as $locale) {
227
                $filename = $base . '/' . $locale . '/LC_MESSAGES/' . $domain . '.mo';
228 40
                if (file_exists($filename)) {
229
                    break;
230 40
                }
231 10
            }
232
233
            // We don't care about invalid path, we will get fallback
234
            // translator here
235
            $moParser = new MoParser($filename);
236
            if (self::$cacheFactory instanceof CacheFactoryInterface) {
237
                $cache = self::$cacheFactory->getInstance($moParser, $this->locale, $domain);
238 40
            } else {
239
                $cache = new InMemoryCache($moParser);
240 40
            }
241 10
242
            $this->domains[$this->locale][$domain] = new Translator($cache);
243
        }
244
245
        return $this->domains[$this->locale][$domain];
246
    }
247
248
    /**
249
     * Sets the path for a domain.
250 40
     *
251
     * @param string $domain Domain name
252 40
     * @param string $path   Path where to find locales
253 40
     */
254
    public function bindtextdomain(string $domain, string $path): void
255
    {
256 40
        $this->paths[$domain] = $path;
257
    }
258
259
    /**
260
     * Sets the default domain.
261
     *
262
     * @param string $domain Domain name
263
     */
264
    public function textdomain(string $domain): void
265
    {
266
        $this->defaultDomain = $domain;
267
    }
268
269 8
    /**
270
     * Sets a requested locale.
271 8
     *
272 4
     * @param string $locale Locale name
273
     *
274
     * @return string Set or current locale
275 4
     */
276 4
    public function setlocale(string $locale): string
277 4
    {
278
        if (! empty($locale)) {
279
            $this->locale = $locale;
280 4
        }
281 4
282 4
        return $this->locale;
283
    }
284
285 4
    /**
286 4
     * Detects currently configured locale.
287 4
     *
288
     * It checks:
289
     *
290 4
     * - global lang variable
291
     * - environment for LC_ALL, LC_MESSAGES and LANG
292
     *
293
     * @return string with locale name
294
     */
295
    public function detectlocale(): string
296
    {
297
        if (isset($GLOBALS['lang'])) {
298
            return $GLOBALS['lang'];
299
        }
300
301
        $locale = getenv('LC_ALL');
302
        if ($locale !== false) {
303
            return $locale;
304
        }
305
306
        $locale = getenv('LC_MESSAGES');
307
        if ($locale !== false) {
308
            return $locale;
309
        }
310
311
        $locale = getenv('LANG');
312
        if ($locale !== false) {
313
            return $locale;
314
        }
315
316
        return 'en';
317
    }
318
}
319