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