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