Test Failed
Push — master ( c71310...b1cf76 )
by Julien
20:09
created

Locale   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Test Coverage

Coverage 96.05%

Importance

Changes 0
Metric Value
eloc 69
dl 0
loc 283
ccs 73
cts 76
cp 0.9605
rs 10
c 0
b 0
f 0
wmc 30

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getDefault() 0 3 1
A setMode() 0 3 1
A getLocale() 0 3 1
A saveIntoSession() 0 7 3
A get() 0 3 1
A getAllowed() 0 3 1
A initialize() 0 7 1
A setLocale() 0 3 1
A setDefault() 0 3 1
A getMode() 0 3 1
A setAllowed() 0 3 1
A getFromHttp() 0 6 3
A getFromRoute() 0 3 1
A getFromDispatcher() 0 3 1
A getFromSession() 0 3 1
A prepare() 0 21 1
B lookup() 0 39 10
1
<?php
2
3
/**
4
 * This file is part of the Zemit Framework.
5
 *
6
 * (c) Zemit Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zemit;
13
14
use Zemit\Di\Injectable;
15
use Zemit\Support\Options\Options;
16
use Zemit\Support\Options\OptionsInterface;
17
18
/**
19
 * Allow to manage and lookup the locale for the localisation
20
 * @property string|null $locale The current locale
21
 */
22
class Locale extends Injectable implements OptionsInterface
23
{
24
    use Options;
25
    
26
    /**
27
     * Default (router only)
28
     */
29
    public const string MODE_DEFAULT = 'default';
30
    
31
    /**
32
     * Router
33
     */
34
    public const string MODE_ROUTE = 'route';
35
    
36
    /**
37
     * Router -> http
38
     */
39
    public const string MODE_HTTP = 'http';
40
    
41
    /**
42
     * Router -> session -> http
43
     */
44
    public const string MODE_SESSION = 'session';
45
    
46
    /**
47
     * Locale mode
48
     * Locale::MODE_DEFAULT 'default' (Router -> http)
49
     * Locale::MODE_SESSION 'session' (Router -> session -> http)
50
     */
51
    public string $mode = self::MODE_DEFAULT;
52
    
53
    /**
54
     * The actual locale that was picked
55
     * @var string|null
56
     */
57
    public ?string $locale = null;
58
    
59
    /**
60
     * Session key for storing the locale
61
     * @var string $sessionKey The session key for storing the locale.
62
     */
63
    public string $sessionKey = 'zemit-locale';
64
    
65
    /**
66
     * Default locale
67
     *
68
     * This variable holds the default locale value for the application.
69
     * If no locale is explicitly specified, this value will be used.
70
     *
71
     * @var string $default
72
     */
73
    public string $default = 'en';
74
    
75
    /**
76
     * Array of allowed languages.
77
     *
78
     * @var array $allowed An array of allowed languages.
79
     */
80
    public array $allowed = ['en'];
81
    
82
    /**
83
     * Initializes the object by setting its properties based on the provided options.
84
     *
85
     * This method retrieves the values of the sessionKey, allowed, default, and mode options using the getOption()
86
     * method. If these options are not provided, the default values specified in the class properties are used instead.
87
     *
88
     * It then sets the obtained values to the corresponding class properties using the appropriate setter methods,
89
     * namely setAllowed(), setDefault(), and setMode(). Additionally, it assigns the obtained sessionKey value directly
90
     * to the sessionKey property.
91
     *
92
     * Finally, the initialize() method prepares the default value by calling the prepare() method with the getDefault()
93
     * method as its parameter.
94
     *
95
     * @return void
96
     */
97 21
    public function initialize(): void
98
    {
99 21
        $this->sessionKey = $this->getOption('sessionKey', $this->sessionKey);
100 21
        $this->setAllowed($this->getOption('allowed', $this->allowed));
101 21
        $this->setDefault($this->getOption('default', $this->default));
102 21
        $this->setMode($this->getOption('mode', $this->mode));
103 21
        $this->prepare($this->getDefault());
104
    }
105
    
106
    /**
107
     * Alias of the getLocale() method
108
     */
109 3
    public function get(): ?string
110
    {
111 3
        return $this->getLocale();
112
    }
113
    
114
    /**
115
     * Retrieves the locale value of the object.
116
     *
117
     * This method returns the value of the locale property, which represents the current locale of the object.
118
     * The locale property is set using the setLocale() method or may be null if no locale is set.
119
     *
120
     * @return string|null The locale value of the object, or null if no locale is set.
121
     */
122 21
    public function getLocale(): ?string
123
    {
124 21
        return $this->locale;
125
    }
126
    
127
    /**
128
     * Set the current locale value
129
     */
130 21
    public function setLocale(?string $locale = null): void
131
    {
132 21
        $this->locale = $this->lookup($locale);
133
    }
134
    
135
    /**
136
     * Get the default locale
137
     */
138 21
    public function getDefault(): string
139
    {
140 21
        return $this->default;
141
    }
142
    
143
    /**
144
     * Set the default locale value
145
     */
146 21
    public function setDefault(string $locale): void
147
    {
148 21
        $this->default = $locale;
149
    }
150
    
151
    /**
152
     * Get the list of possible locale
153
     */
154 21
    public function getAllowed(): array
155
    {
156 21
        return $this->allowed;
157
    }
158
    
159
    /**
160
     * Set the allowed locale
161
     */
162 21
    public function setAllowed(array $allowed): void
163
    {
164 21
        $this->allowed = array_values(array_unique($allowed));
165
    }
166
    
167
    /**
168
     * Get the defined mode
169
     */
170 1
    public function getMode(): string
171
    {
172 1
        return $this->mode;
173
    }
174
    
175
    /**
176
     * Set the mode
177
     */
178 21
    public function setMode(string $mode): void
179
    {
180 21
        $this->mode = $mode;
181
    }
182
    
183
    /**
184
     * Prepare and set and return the locale based on the defined mode
185
     */
186 21
    public function prepare(?string $default = null): ?string
187
    {
188 21
        $locale = match ($this->mode) {
189 21
            self::MODE_SESSION =>
190 1
                $this->getFromRoute() ??
191 1
                $this->getFromSession() ??
192 1
                $this->getFromHttp() ??
193 1
                $default,
194 21
            self::MODE_HTTP =>
195 1
                $this->getFromRoute() ??
196 1
                $this->getFromHttp() ??
197 1
                $default,
198 21
            default =>
199 21
                $this->getFromRoute() ??
200 21
                $default,
201 21
        };
202
        
203 21
        $locale ??= $this->locale;
204 21
        $this->setLocale($locale);
205 21
        $this->saveIntoSession($locale);
206 21
        return $this->getLocale();
207
    }
208
    
209
    /**
210
     * Retrieves the locale from the route
211
     */
212 21
    public function getFromRoute(?string $default = null): ?string
213
    {
214 21
        return $this->lookup($this->router->getParams()['locale'] ?? $default);
215
    }
216
    
217
    /**
218
     * Retrieves the locale from the dispatcher
219
     */
220
    public function getFromDispatcher(?string $default = null): ?string
221
    {
222
        return $this->lookup($this->dispatcher->getParams()['locale'] ?? $default);
223
    }
224
    
225
    /**
226
     * Retrieves the locale from the session
227
     */
228 2
    public function getFromSession(?string $default = null): ?string
229
    {
230 2
        return $this->lookup($this->session->get($this->sessionKey, $default));
231
    }
232
    
233
    /**
234
     * Retrieves the locale from the request
235
     * of getBestLanguage() header
236
     * or HTTP_ACCEPT_LANGUAGE header
237
     */
238 3
    public function getFromHttp(?string $default = null): ?string
239
    {
240 3
        return
241 3
            $this->lookup($this->request->getBestLanguage()) ?:
242 3
            \Locale::acceptFromHttp($this->request->getHeader('HTTP_ACCEPT_LANGUAGE')) ?:
243 3
            $default;
244
    }
245
    
246
    /**
247
     * Save locale into session if mode contain session handling
248
     */
249 21
    public function saveIntoSession(?string $locale = null, bool $force = false): void
250
    {
251 21
        $locale ??= $this->getLocale();
252
        
253
        // save into session
254 21
        if ($force || $this->mode === self::MODE_SESSION) {
255 2
            $this->session->set($this->sessionKey, $locale);
256
        }
257
    }
258
    
259
    /**
260
     * @param string|null $locale The locale to use as the language range when matching.
261
     * @param array|null $allowed An array containing a list of language tags to compare to locale. Maximum 100 items allowed.
262
     * @param bool $canonicalize If true, the arguments will be converted to canonical form before matching.
263
     * @param string|null $default The locale to use if no match is found.
264
     * @return string|null The closest matching language tag or default value.
265
     */
266 21
    public function lookup(?string $locale = null, ?array $allowed = null, bool $canonicalize = false, ?string $default = null): ?string
267
    {
268 21
        if (is_null($locale)) {
269 21
            return null;
270
        }
271
        
272 21
        $allowed ??= $this->getAllowed();
273
        
274
        // lookup first
275 21
        $lookup = \Locale::lookup($allowed, $locale, $canonicalize, $default);
276
        
277
        // base locale found without the region
278 21
        $force = false;
279 21
        if (isset($lookup) && ($locale === $lookup || strlen($lookup) === 2)) {
280 21
            $locale = $lookup;
281 21
            $force = true;
282
        }
283
        
284
        // lookup for the first configured region based on the locale without region
285 21
        if (empty($lookup) || $force) {
286
            
287
            // matches all the possible regions from the locale
288 21
            $matches = array_filter($allowed, function ($haystack) use ($locale) {
289 21
                $needle = $locale . '_';
290 21
                return stripos($haystack, $needle) === 0;
291 21
            });
292
            
293
            // some matches
294 21
            if (count($matches)) {
295
                // lookup again with the first match
296
                $lookup = \Locale::lookup($matches, array_shift($matches), $canonicalize, $default);
297
            }
298
            else {
299
                // otherwise keep the lookup if set or set the default if not
300 21
                $lookup = empty($lookup) ? $default : $lookup;
301
            }
302
        }
303
        
304 21
        return empty($lookup) ? null : $lookup;
305
    }
306
}
307