Test Failed
Pull Request — master (#36)
by Rustam
02:34
created

Locale::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
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
c 1
b 0
f 0
nc 1
nop 10
dl 0
loc 13
ccs 2
cts 2
cp 1
crap 1
rs 10

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