LanguageMiddleware::getLanguageFromBrowser()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Charcoal\Translator\Middleware;
4
5
// From PSR-7
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr\Http\Message\RequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
10
// From Pimple
11
use Pimple\Container;
12
13
// From 'charcoal-translator'
14
use Charcoal\Translator\LocalesManager;
15
use Charcoal\Translator\TranslatorAwareTrait;
16
17
/**
18
 * Class LanguageMiddleware
19
 */
20
class LanguageMiddleware
21
{
22
    use TranslatorAwareTrait;
23
24
    /**
25
     * @var string
26
     */
27
    private $defaultLanguage;
28
29
    /**
30
     * @var string
31
     */
32
    private $browserLanguage;
33
34
    /**
35
     * @var array
36
     */
37
    private $excludedPath;
38
39
    /**
40
     * @var boolean
41
     */
42
    private $usePath;
43
44
    /**
45
     * @var string
46
     */
47
    private $pathRegexp;
48
49
    /**
50
     * @var boolean
51
     */
52
    private $useBrowser;
53
54
    /**
55
     * @var boolean
56
     */
57
    private $useSession;
58
59
    /**
60
     * @var string[]
61
     */
62
    private $sessionKey;
63
64
    /**
65
     * @var boolean
66
     */
67
    private $useParams;
68
69
    /**
70
     * @var string[]
71
     */
72
    private $paramKey;
73
74
    /**
75
     * @var boolean
76
     */
77
    private $useHost;
78
79
    /**
80
     * @var array
81
     */
82
    private $hostMap;
83
84
    /**
85
     * @var boolean
86
     */
87
    private $setLocale;
88
89
    /**
90
     * @param array $data The middleware options.
91
     */
92
    public function __construct(array $data)
93
    {
94
        $this->setTranslator($data['translator']);
95
96
        $data = array_replace($this->defaults(), $data);
97
98
        $this->defaultLanguage = $data['default_language'];
99
        $this->browserLanguage = $data['browser_language'];
100
101
        $this->usePath      = !!$data['use_path'];
102
        $this->excludedPath = (array)$data['excluded_path'];
103
        $this->pathRegexp   = $data['path_regexp'];
104
105
        $this->useParams    = !!$data['use_params'];
106
        $this->paramKey     = (array)$data['param_key'];
107
108
        $this->useSession   = !!$data['use_session'];
109
        $this->sessionKey   = (array)$data['session_key'];
110
111
        $this->useBrowser   = !!$data['use_browser'];
112
113
        $this->useHost      = !!$data['use_host'];
114
        $this->hostMap      = (array)$data['host_map'];
115
116
        $this->setLocale    = !!$data['set_locale'];
117
    }
118
119
    /**
120
     * Default middleware options.
121
     *
122
     * @return array
123
     */
124
    public function defaults()
125
    {
126
        return [
127
            'default_language' => null,
128
            'browser_language' => null,
129
130
            'use_path'         => true,
131
            'excluded_path'    => [ '^/admin\b' ],
132
            'path_regexp'      => '^/([a-z]{2})\b',
133
134
            'use_params'       => false,
135
            'param_key'        => 'current_language',
136
137
            'use_session'      => true,
138
            'session_key'      => 'current_language',
139
140
            'use_browser'      => true,
141
142
            'use_host'         => false,
143
            'host_map'         => [],
144
145
            'set_locale'       => true
146
        ];
147
    }
148
149
    /**
150
     * @param  RequestInterface  $request  The PSR-7 HTTP request.
151
     * @param  ResponseInterface $response The PSR-7 HTTP response.
152
     * @param  callable          $next     The next middleware callable in the stack.
153
     * @return ResponseInterface
154
     */
155
    public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next)
156
    {
157
        // Test if path is excluded from middleware.
158
        $uri  = $request->getUri();
159
        $path = $uri->getPath();
160
        foreach ($this->excludedPath as $excluded) {
161
            if (preg_match('@'.$excluded.'@', $path)) {
162
                return $next($request, $response);
163
            }
164
        }
165
166
        $language = $this->getLanguage($request);
167
        $this->setLanguage($language);
168
169
        return $next($request, $response);
170
    }
171
172
    /**
173
     * @param  RequestInterface $request The PSR-7 HTTP request.
174
     * @return null|string
175
     */
176
    private function getLanguage(RequestInterface $request)
177
    {
178
        if ($this->useHost === true) {
179
            $lang = $this->getLanguageFromHost($request);
180
            if ($lang) {
181
                return $lang;
182
            }
183
        }
184
185
        if ($this->usePath === true) {
186
            $lang = $this->getLanguageFromPath($request);
187
            if ($lang) {
188
                return $lang;
189
            }
190
        }
191
192
        if ($this->useParams === true) {
193
            $lang = $this->getLanguageFromParams($request);
194
            if ($lang) {
195
                return $lang;
196
            }
197
        }
198
199
        if ($this->useSession === true) {
200
            $lang = $this->getLanguageFromSession();
201
            if ($lang) {
202
                return $lang;
203
            }
204
        }
205
206
        if ($this->useBrowser === true) {
207
            $lang = $this->getLanguageFromBrowser();
208
            if ($lang) {
209
                return $lang;
210
            }
211
        }
212
213
        return $this->defaultLanguage;
214
    }
215
216
    /**
217
     * @param  RequestInterface $request The PSR-7 HTTP request.
218
     * @return string
219
     */
220
    private function getLanguageFromHost(RequestInterface $request)
221
    {
222
        $uriHost = $request->getUri()->getHost();
223
        foreach ($this->hostMap as $lang => $host) {
224
            if (stripos($uriHost, $host) !== false) {
225
                return $lang;
226
            }
227
        }
228
229
        return '';
230
    }
231
232
    /**
233
     * @param  RequestInterface $request The PSR-7 HTTP request.
234
     * @return string
235
     */
236
    private function getLanguageFromPath(RequestInterface $request)
237
    {
238
        $path = $request->getRequestTarget();
239
        if (preg_match('@'.$this->pathRegexp.'@', $path, $matches)) {
240
            $lang = $matches[1];
241
        } else {
242
            return '';
243
        }
244
245
        if (in_array($lang, $this->translator()->availableLocales())) {
246
            return $lang;
247
        } else {
248
            return '';
249
        }
250
    }
251
252
    /**
253
     * @param  RequestInterface $request The PSR-7 HTTP request.
254
     * @return string
255
     */
256
    private function getLanguageFromParams(RequestInterface $request)
257
    {
258
        if ($request instanceof ServerRequestInterface) {
259
            $locales = $this->translator()->availableLocales();
260
            $params  = $request->getQueryParams();
261
            foreach ($this->paramKey as $key) {
262
                if (isset($params[$key]) && in_array($params[$key], $locales)) {
263
                    return $params[$key];
264
                }
265
            }
266
        }
267
268
        return '';
269
    }
270
271
    /**
272
     * @return string
273
     */
274
    private function getLanguageFromSession()
275
    {
276
        $locales = $this->translator()->availableLocales();
277
        foreach ($this->sessionKey as $key) {
278
            if (isset($_SESSION[$key]) && in_array($_SESSION[$key], $locales)) {
279
                return $_SESSION[$key];
280
            }
281
        }
282
283
        return '';
284
    }
285
286
    /**
287
     * @return mixed
288
     */
289
    private function getLanguageFromBrowser()
290
    {
291
        return $this->browserLanguage;
292
    }
293
294
    /**
295
     * @param  string $lang The language code to set.
296
     * @return void
297
     */
298
    private function setLanguage($lang)
299
    {
300
        $this->translator()->setLocale($lang);
301
302
        if ($this->useSession === true) {
303
            foreach ($this->sessionKey as $key) {
304
                $_SESSION[$key] = $this->translator()->getLocale();
305
            }
306
        }
307
308
        if ($this->setLocale === true) {
309
            $this->setLocale($lang);
310
        }
311
    }
312
313
    /**
314
     * @param  string $lang The language code to set.
315
     * @return void
316
     */
317
    private function setLocale($lang)
318
    {
319
        $translator = $this->translator();
320
        $available  = $translator->locales();
321
        $fallbacks  = $translator->getFallbackLocales();
322
323
        array_unshift($fallbacks, $lang);
324
        $fallbacks = array_unique($fallbacks);
325
326
        $locales = [];
327
        foreach ($fallbacks as $code) {
328
            if (isset($available[$code])) {
329
                $locale = $available[$code];
330
                if (isset($locale['locales'])) {
331
                    $choices = (array)$locale['locales'];
332
                    array_push($locales, ...$choices);
333
                } elseif (isset($locale['locale'])) {
334
                    array_push($locales, $locale['locale']);
335
                }
336
            }
337
        }
338
339
        $locales = array_unique($locales);
340
341
        if ($locales) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $locales of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
342
            setlocale(LC_ALL, $locales);
343
        }
344
    }
345
}
346