Passed
Pull Request — master (#36)
by Rustam
02:59
created

Locale::__construct()   A

Complexity

Conditions 1
Paths 1

Size

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