Test Setup Failed
Push — master ( be854f...06dd95 )
by
unknown
02:07
created

LanguageMiddleware::getLanguage()   C

Complexity

Conditions 11
Paths 63

Size

Total Lines 39
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 39
rs 5.2653
c 0
b 0
f 0
cc 11
eloc 22
nc 63
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
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 null|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
230
    /**
231
     * @param  RequestInterface $request The PSR-7 HTTP request.
232
     * @return null|string
233
     */
234
    private function getLanguageFromPath(RequestInterface $request)
235
    {
236
        $path = $request->getRequestTarget();
237
        if (preg_match('@'.$this->pathRegexp.'@', $path, $matches)) {
238
            $lang = $matches[1];
239
        } else {
240
            return '';
241
        }
242
243
        if (in_array($lang, $this->translator()->availableLocales())) {
244
            return $lang;
245
        } else {
246
            return '';
247
        }
248
    }
249
250
    /**
251
     * @param  RequestInterface $request The PSR-7 HTTP request.
252
     * @return string
253
     */
254
    private function getLanguageFromParams(RequestInterface $request)
255
    {
256
        if ($request instanceof ServerRequestInterface) {
257
            $locales = $this->translator()->availableLocales();
258
            $params  = $request->getQueryParams();
259
            foreach ($this->paramKey as $key) {
260
                if (isset($params[$key]) && in_array($params[$key], $locales)) {
261
                    return $params[$key];
262
                }
263
            }
264
        }
265
266
        return '';
267
    }
268
269
    /**
270
     * @return string
271
     */
272
    private function getLanguageFromSession()
273
    {
274
        $locales = $this->translator()->availableLocales();
275
        foreach ($this->sessionKey as $key) {
276
            if (isset($_SESSION[$key]) && in_array($_SESSION[$key], $locales)) {
277
                return $_SESSION[$key];
278
            }
279
        }
280
281
        return '';
282
    }
283
284
    /**
285
     * @return mixed
286
     */
287
    private function getLanguageFromBrowser()
288
    {
289
        return $this->browserLanguage;
290
    }
291
292
    /**
293
     * @param  string $lang The language code to set.
294
     * @return void
295
     */
296
    private function setLanguage($lang)
297
    {
298
        $this->translator()->setLocale($lang);
299
300
        if ($this->useSession === true) {
301
            foreach ($this->sessionKey as $key) {
302
                $_SESSION[$key] = $this->translator()->getLocale();
303
            }
304
        }
305
306
        if ($this->setLocale === true) {
307
            $this->setLocale($lang);
308
        }
309
    }
310
311
    /**
312
     * @param  string $lang The language code to set.
313
     * @return void
314
     */
315
    private function setLocale($lang)
316
    {
317
        $translator = $this->translator();
318
        $available  = $translator->locales();
319
        $fallbacks  = $translator->getFallbackLocales();
320
321
        array_unshift($fallbacks, $lang);
322
        $fallbacks = array_unique($fallbacks);
323
324
        $locales = [];
325
        foreach ($fallbacks as $code) {
326
            if (isset($available[$code])) {
327
                $locale = $available[$code];
328
                if (isset($locale['locales'])) {
329
                    $choices = (array)$locale['locales'];
330
                    array_push($locales, ...$choices);
331
                } elseif (isset($locale['locale'])) {
332
                    array_push($locales, $locale['locale']);
333
                }
334
            }
335
        }
336
337
        $locales = array_unique($locales);
338
339
        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...
340
            setlocale(LC_ALL, $locales);
341
        }
342
    }
343
}
344