Passed
Push — master ( 75f605...f16558 )
by Alexander
02:18
created

Locale::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 8
dl 0
loc 11
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Middleware;
6
7
use DateInterval;
8
use Psr\Http\Message\ResponseFactoryInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\ServerRequestInterface;
11
use Psr\Http\Server\MiddlewareInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Psr\Log\LoggerInterface;
14
use Yiisoft\Cookies\Cookie;
15
use Yiisoft\Http\Header;
16
use Yiisoft\Http\Method;
17
use Yiisoft\Http\Status;
18
use Yiisoft\Router\UrlGeneratorInterface;
19
use Yiisoft\Session\SessionInterface;
20
use Yiisoft\Strings\WildcardPattern;
21
use Yiisoft\Translator\TranslatorInterface;
22
23
final class Locale implements MiddlewareInterface
24
{
25
    private const DEFAULT_LOCALE = 'en';
26
    private const DEFAULT_LOCALE_NAME = '_language';
27
28
    private bool $enableSaveLocale = true;
29
    private bool $enableDetectLocale = false;
30
    private string $defaultLocale = self::DEFAULT_LOCALE;
31
    private string $queryParameterName = self::DEFAULT_LOCALE_NAME;
32
    private string $sessionName = self::DEFAULT_LOCALE_NAME;
33
    private ?DateInterval $cookieDuration;
34
35 12
    public function __construct(
36
        private TranslatorInterface $translator,
37
        private UrlGeneratorInterface $urlGenerator,
38
        private SessionInterface $session,
39
        private LoggerInterface $logger,
40
        private ResponseFactoryInterface $responseFactory,
41
        private array $locales = [],
42
        private array $ignoredRequests = [],
43
        private bool $cookieSecure = false
44
    ) {
45 12
        $this->cookieDuration = new DateInterval('P30D');
46
    }
47
48 11
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
49
    {
50 11
        if ($this->locales === []) {
51 1
            return $handler->handle($request);
52
        }
53
54 10
        $uri = $request->getUri();
55 10
        $path = $uri->getPath();
56
57 10
        [$locale, $country] = $this->getLocaleFromPath($path);
58
59 10
        if ($locale !== null) {
60 5
            $this->translator->setLocale($locale);
61 5
            $this->urlGenerator->setDefaultArgument($this->queryParameterName, $locale);
62
63 5
            $response = $handler->handle($request);
64 5
            $newPath = null;
65 5
            if ($this->isDefaultLocale($locale, $country) && $request->getMethod() === Method::GET) {
66 3
                $length = strlen($locale);
67 3
                $newPath = substr($path, $length + 1);
68
            }
69 5
            return $this->applyLocaleFromPath($locale, $response, $newPath);
70
        }
71 5
        if ($this->enableSaveLocale) {
72 5
            [$locale, $country] = $this->getLocaleFromRequest($request);
73
        }
74 5
        if ($locale === null && $this->enableDetectLocale) {
75 2
            [$locale, $country] = $this->detectLocale($request);
76
        }
77 5
        if ($locale === null || $this->isDefaultLocale($locale, $country) || $this->isRequestIgnored($request)) {
78 2
            $this->urlGenerator->setDefaultArgument($this->queryParameterName, null);
79 2
            $request = $request->withUri($uri->withPath('/' . $this->defaultLocale . $path));
80 2
            return $handler->handle($request);
81
        }
82
83 3
        $this->translator->setLocale($locale);
84 3
        $this->urlGenerator->setDefaultArgument($this->queryParameterName, $locale);
85
86 3
        if ($request->getMethod() === Method::GET) {
87 2
            return $this->responseFactory
88 2
                ->createResponse(Status::FOUND)
89 2
                ->withHeader(Header::LOCATION, '/' . $locale . $path);
90
        }
91
92
93 1
        return $handler->handle($request);
94
    }
95
96 5
    private function applyLocaleFromPath(
97
        string $locale,
98
        ResponseInterface $response,
99
        ?string $newPath = null
100
    ): ResponseInterface {
101 5
        if ($newPath === '') {
102 2
            $newPath = '/';
103
        }
104
105 5
        if ($newPath !== null) {
106 3
            $response = $this->responseFactory
107 3
                ->createResponse(Status::FOUND)
108 3
                ->withHeader(Header::LOCATION, $newPath);
109
        }
110 5
        if ($this->enableSaveLocale) {
111 5
            $response = $this->saveLocale($locale, $response);
112
        }
113 5
        return $response;
114
    }
115
116 10
    private function getLocaleFromPath(string $path): array
117
    {
118 10
        $parts = [];
119 10
        foreach ($this->locales as $code => $locale) {
120 10
            $lang = is_string($code) ? $code : $locale;
121 10
            $parts[] = $lang;
122
        }
123
124 10
        $pattern = implode('|', $parts);
125 10
        if (preg_match("#^/($pattern)\b(/?)#i", $path, $matches)) {
126 5
            $locale = $matches[1];
127 5
            [$locale, $country] = $this->parseLocale($locale);
128 5
            if (isset($this->locales[$locale])) {
129 5
                $this->logger->debug(sprintf("Locale '%s' found in URL", $locale));
130 5
                return [$locale, $country];
131
            }
132
        }
133 5
        return [null, null];
134
    }
135
136 5
    private function getLocaleFromRequest(ServerRequestInterface $request): array
137
    {
138 5
        $cookies = $request->getCookieParams();
139 5
        $queryParameters = $request->getQueryParams();
140 5
        if (isset($cookies[$this->sessionName])) {
141
            $this->logger->debug(sprintf("Locale '%s' found in cookies", $cookies[$this->sessionName]));
142
            return $this->parseLocale($cookies[$this->sessionName]);
143
        }
144 5
        if (isset($queryParameters[$this->queryParameterName])) {
145 3
            $this->logger->debug(
146 3
                sprintf("Locale '%s' found in query string", $queryParameters[$this->queryParameterName])
147
            );
148 3
            return $this->parseLocale($queryParameters[$this->queryParameterName]);
149
        }
150 2
        return [null, null];
151
    }
152
153 9
    private function isDefaultLocale(string $locale, ?string $country): bool
154
    {
155 9
        return $locale === $this->defaultLocale || ($country !== null && $this->defaultLocale === "$locale-$country");
156
    }
157
158 2
    private function detectLocale(ServerRequestInterface $request): array
159
    {
160 2
        foreach ($request->getHeader(Header::ACCEPT_LANGUAGE) as $language) {
161 1
            return $this->parseLocale($language);
162
        }
163 1
        return [null, null];
164
    }
165
166 5
    private function saveLocale(string $locale, ResponseInterface $response): ResponseInterface
167
    {
168 5
        $this->logger->debug('Saving found locale to cookies');
169 5
        $this->session->set($this->sessionName, $locale);
170 5
        $cookie = new Cookie(name: $this->sessionName, value: $locale, secure: $this->cookieSecure);
171 5
        if ($this->cookieDuration !== null) {
172 5
            $cookie = $cookie->withMaxAge($this->cookieDuration);
173
        }
174 5
        return $cookie->addToResponse($response);
175
    }
176
177 9
    private function parseLocale(string $locale): array
178
    {
179 9
        if (str_contains($locale, '-')) {
180
            return explode('-', $locale, 2);
181
        }
182
183 9
        if (str_contains($locale, '_')) {
184
            return explode('_', $locale, 2);
185
        }
186 9
        if (isset($this->locales[$locale]) && str_contains($this->locales[$locale], '-')) {
187 9
            return explode('-', $this->locales[$locale], 2);
188
        }
189
        return [$locale, null];
190
    }
191
192 4
    private function isRequestIgnored(ServerRequestInterface $request): bool
193
    {
194 4
        foreach ($this->ignoredRequests as $ignoredRequest) {
195 1
            if ((new WildcardPattern($ignoredRequest))->match($request->getUri()->getPath())) {
196 1
                return true;
197
            }
198
        }
199 3
        return false;
200
    }
201
202 1
    public function withLocales(array $locales): self
203
    {
204 1
        $new = clone $this;
205 1
        $new->locales = $locales;
206 1
        return $new;
207
    }
208
209 3
    public function withDefaultLocale(string $defaultLocale): self
210
    {
211 3
        $new = clone $this;
212 3
        $new->defaultLocale = $defaultLocale;
213 3
        return $new;
214
    }
215
216 1
    public function withQueryParameterName(string $queryParameterName): self
217
    {
218 1
        $new = clone $this;
219 1
        $new->queryParameterName = $queryParameterName;
220 1
        return $new;
221
    }
222
223 1
    public function withSessionName(string $sessionName): self
224
    {
225 1
        $new = clone $this;
226 1
        $new->sessionName = $sessionName;
227 1
        return $new;
228
    }
229
230 1
    public function withEnableSaveLocale(bool $enableSaveLocale): self
231
    {
232 1
        $new = clone $this;
233 1
        $new->enableDetectLocale = $enableSaveLocale;
234 1
        return $new;
235
    }
236
237 3
    public function withEnableDetectLocale(bool $enableDetectLocale): self
238
    {
239 3
        $new = clone $this;
240 3
        $new->enableDetectLocale = $enableDetectLocale;
241 3
        return $new;
242
    }
243
244 2
    public function withIgnoredRequests(array $ignoredRequests): self
245
    {
246 2
        $new = clone $this;
247 2
        $new->ignoredRequests = $ignoredRequests;
248 2
        return $new;
249
    }
250
251 1
    public function withCookieSecure(bool $secure): self
252
    {
253 1
        $new = clone $this;
254 1
        $new->cookieSecure = $secure;
255 1
        return $new;
256
    }
257
}
258