Lines 90.00% 45 / 50
Methods 83.33% 10 / 12
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 detectAllWithBrowser 100.00% 2 / 2 100.00% 1 / 1 1
 detectAllFromConfig 100.00% 1 / 1 100.00% 1 / 1 1
 getAcceptLanguage 0.00% 0 / 1 0.00% 0 / 1 2
 selectLanguage 100.00% 6 / 6 100.00% 1 / 1 4
 aggregateWithBrowser 100.00% 8 / 8 100.00% 1 / 1 1
 aggregateWithoutBrowser 100.00% 8 / 8 100.00% 1 / 1 1
 fetchFilteredLanguage 100.00% 3 / 3 100.00% 1 / 1 3
 getSessionLanguage 100.00% 3 / 3 100.00% 1 / 1 3
 getConfigLanguage 100.00% 2 / 2 100.00% 1 / 1 2
 getDetectionLanguage 100.00% 3 / 3 100.00% 1 / 1 2
 initUserAgentLanguage 66.66% 8 / 12 0.00% 0 / 1 7.33
27class LanguageDetector
28{
29    private string $acceptLanguage = '';
30
31    public function __construct(
32        private readonly SessionInterface $session,
33    ) {
34    }
35
36    /** @return array<string, string|null> */
37    public function detectAllWithBrowser(string $configLanguage): array
38    {
39        $this->initUserAgentLanguage();
40        return $this->aggregateWithBrowser($configLanguage);
41    }
42
43    /** @return array<string, string|null> */
44    public function detectAllFromConfig(string $configLanguage): array
45    {
46        return $this->aggregateWithoutBrowser($configLanguage);
47    }
48
49    public function getAcceptLanguage(): string
50    {
51        return $this->acceptLanguage;
52    }
53
54    /**
55     * Selects the first valid language from the detected list in a defined priority
56     * and falls back to 'en' if none was found.
57     *
58     * Priority: POST > GET(lang) > GET(artlang) > Session > Browser detection > Config
59     *
60     * @param array<string, string|null> $detected
61     */
62    public function selectLanguage(array $detected): string
63    {
64        $order = ['post', 'get', 'artget', 'session', 'detection', 'config'];
65        foreach ($order as $key) {
66            $lang = $detected[$key] ?? null;
67            if ($lang !== null && Language::isASupportedLanguage($lang)) {
68                return strtolower($lang);
69            }
70        }
71
72        return 'en';
73    }
74
75    /** @return array<string, string|null> */
76    private function aggregateWithBrowser(string $configLanguage): array
77    {
78        return [
79            'post' => $this->fetchFilteredLanguage(INPUT_POST, variable: 'language'),
80            'get' => $this->fetchFilteredLanguage(INPUT_GET, variable: 'lang'),
81            'artget' => $this->fetchFilteredLanguage(INPUT_GET, variable: 'artlang'),
82            'session' => $this->getSessionLanguage(),
83            'config' => $this->getConfigLanguage($configLanguage),
84            'detection' => $this->getDetectionLanguage(),
85        ];
86    }
87
88    /** @return array<string, string|null> */
89    private function aggregateWithoutBrowser(string $configLanguage): array
90    {
91        return [
92            'post' => $this->fetchFilteredLanguage(INPUT_POST, variable: 'language'),
93            'get' => $this->fetchFilteredLanguage(INPUT_GET, variable: 'lang'),
94            'artget' => $this->fetchFilteredLanguage(INPUT_GET, variable: 'artlang'),
95            'session' => $this->getSessionLanguage(),
96            'config' => $this->getConfigLanguage($configLanguage),
97            'detection' => null,
98        ];
99    }
100
101    private function fetchFilteredLanguage(int $inputType, string $variable): ?string
102    {
103        $filteredLang = Filter::filterInput($inputType, variableName: $variable, filter: FILTER_SANITIZE_SPECIAL_CHARS);
104        $lang = is_string($filteredLang) ? $filteredLang : null;
105        return Language::isASupportedLanguage($lang) ? $lang : null;
106    }
107
108    private function getSessionLanguage(): ?string
109    {
110        $sessionLang = $this->session->get(name: 'lang');
111        $lang = is_string($sessionLang) ? $sessionLang : null;
112        return Language::isASupportedLanguage($lang) ? trim((string) $lang) : null;
113    }
114
115    private function getConfigLanguage(string $configLanguage): ?string
116    {
117        $lang = str_replace(['language_', '.php'], replace: '', subject: $configLanguage);
118        return Language::isASupportedLanguage($lang) ? $lang : null;
119    }
120
121    private function getDetectionLanguage(): ?string
122    {
123        return Language::isASupportedLanguage(strtoupper($this->acceptLanguage))
124            ? strtolower($this->acceptLanguage)
125            : null;
126    }
127
128    private function initUserAgentLanguage(): void
129    {
130        $languages = Request::createFromGlobals()->getLanguages();
131        foreach ($languages as $language) {
132            if (!Language::isASupportedLanguage(strtoupper($language))) {
133                continue;
134            }
135
136            $this->acceptLanguage = strtolower($language);
137            break;
138        }
139
140        if ($this->acceptLanguage === '') {
141            foreach ($languages as $language) {
142                $short = substr($language, offset: 0, length: 2);
143                if (Language::isASupportedLanguage(strtoupper($short))) {
144                    $this->acceptLanguage = strtolower($short);
145                    break;
146                }
147            }
148        }
149    }
150}