Test Failed
Pull Request — master (#356)
by Rustam
03:17
created

LocaleMiddleware::isDefaultLocale()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 1
c 0
b 0
f 0
nc 3
nop 2
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 12
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\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\Status;
17 10
use Yiisoft\Router\UrlGeneratorInterface;
18
use Yiisoft\Session\SessionInterface;
19 10
use Yiisoft\Translator\TranslatorInterface;
20 10
21
final class LocaleMiddleware implements MiddlewareInterface
22 10
{
23
    private const DEFAULT_LOCALE = 'en';
24 10
    private const DEFAULT_LOCALE_NAME = 'language';
25 10
26
    private TranslatorInterface $translator;
27
    private UrlGeneratorInterface $urlGenerator;
28 10
    private SessionInterface $session;
29
    private ResponseFactoryInterface $responseFactory;
30
    private LoggerInterface $logger;
31
    private array $locales;
32
    private bool $enableSaveLocale = true;
33
    private bool $enableDetectLocale = true;
34
    private string $defaultLocale = self::DEFAULT_LOCALE;
35
    private string $queryParameterName = self::DEFAULT_LOCALE_NAME;
36
    private string $sessionName = self::DEFAULT_LOCALE_NAME;
37
    private ?DateInterval $cookieDuration;
38
39
    public function __construct(
40
        TranslatorInterface $translator,
41
        UrlGeneratorInterface $urlGenerator,
42
        SessionInterface $session,
43
        LoggerInterface $logger,
44
        ResponseFactoryInterface $responseFactory,
45
        array $locales = []
46
    ) {
47
        $this->translator = $translator;
48
        $this->urlGenerator = $urlGenerator;
49
        $this->session = $session;
50
        $this->logger = $logger;
51
        $this->responseFactory = $responseFactory;
52
        $this->locales = $locales;
53
        $this->cookieDuration = new DateInterval('P30D');
54
    }
55
56
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
57
    {
58
        if ($this->locales === []) {
59
            return $handler->handle($request);
60
        }
61
        $this->urlGenerator->setLocales($this->locales);
0 ignored issues
show
Bug introduced by
The method setLocales() does not exist on Yiisoft\Router\UrlGeneratorInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

61
        $this->urlGenerator->/** @scrutinizer ignore-call */ 
62
                             setLocales($this->locales);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
62
63
        $uri = $request->getUri();
64
        $path = $uri->getPath();
65
        [$locale, $country] = $this->getLocaleFromPath($path);
66
67
        if ($locale !== null) {
68
            $length = strlen($locale);
69
            $newPath = substr($path, $length + 1);
70
            if ($newPath === '') {
71
                $newPath = '/';
72
            }
73
            $request = $request->withUri($uri->withPath($newPath));
74
            $this->translator->setLocale($locale);
75
            $this->urlGenerator->setUriPrefix('/' . $locale);
76
77
            $response = $handler->handle($request);
78
            if ($this->isDefaultLocale($locale, $country)) {
79
                $response = $this->responseFactory->createResponse(Status::FOUND)
80
                    ->withHeader(Header::LOCATION, $newPath);
81
            }
82
            if ($this->enableSaveLocale) {
83
                $response = $this->saveLocale($locale, $response);
84
            }
85
            return $response;
86
        }
87
        if ($this->enableSaveLocale) {
88
            $locale = $this->getLocaleFromRequest($request);
89
        }
90
        if ($locale === null && $this->enableDetectLocale) {
91
            // TODO: detect locale from headers
92
        }
93
        if ($locale === null || $this->isDefaultLocale($locale, $country)) {
94
            return $handler->handle($request);
95
        }
96
        return $this->responseFactory->createResponse(Status::FOUND)
97
            ->withHeader(Header::LOCATION, '/' . $locale . rtrim($path, '/'));
98
    }
99
100
    private function getLocaleFromPath(string $path): array
101
    {
102
        $parts = [];
103
        foreach ($this->locales as $code => $locale) {
104
            $lang = is_string($code) ? $code : $locale;
105
            $parts[] = $lang;
106
        }
107
108
        $pattern = implode('|', $parts);
109
        if (preg_match("#^/($pattern)\b(/?)#i", $path, $matches)) {
110
            $locale = $matches[1];
111
            $country = null;
112
            if (strpos($locale, '-') !== false) {
113
                [$locale, $country] = explode('-', $locale, 2);
114
            }
115
            if (isset($this->locales[$locale])) {
116
                $this->logger->info(sprintf("Locale '%s' found in URL", $locale));
117
                return [$locale, $country];
118
            }
119
        }
120
        return [null, null];
121
    }
122
123
    private function getLocaleFromRequest(ServerRequestInterface $request)
124
    {
125
        $cookies = $request->getCookieParams();
126
        $queryParameters = $request->getQueryParams();
127
        if (isset($cookies[$this->sessionName])) {
128
            $this->logger->info(sprintf("Locale '%s' found in cookies", $cookies[$this->sessionName]));
129
            return $cookies[$this->sessionName];
130
        }
131
        if (isset($queryParameters[$this->queryParameterName])) {
132
            $this->logger->info(
133
                sprintf("Locale '%s' found in query string", $queryParameters[$this->queryParameterName])
134
            );
135
            return $queryParameters[$this->queryParameterName];
136
        }
137
        return null;
138
    }
139
140
    private function isDefaultLocale(string $locale, ?string $country): bool
141
    {
142
        return $locale === $this->defaultLocale || ($country !== null && $this->defaultLocale === "$locale-$country");
143
    }
144
145
    private function detectLocale(ServerRequestInterface $request)
0 ignored issues
show
Unused Code introduced by
The method detectLocale() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

145
    private function detectLocale(/** @scrutinizer ignore-unused */ ServerRequestInterface $request)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
146
    {
147
    }
148
149
    private function saveLocale(string $locale, ResponseInterface $response): ResponseInterface
150
    {
151
        $this->logger->info('Saving found locale to cookies');
152
        $this->session->set($this->sessionName, $locale);
153
        $cookie = (new Cookie($this->sessionName, $locale));
154
        if ($this->cookieDuration !== null) {
155
            $cookie = $cookie->withMaxAge($this->cookieDuration);
156
        }
157
        return $cookie->addToResponse($response);
158
    }
159
}
160