Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
Language
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
7 / 7
19
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isLanguageAvailable
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
5
 setLanguageWithDetection
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setLanguageFromConfiguration
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 setLanguageByAcceptLanguage
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isASupportedLanguage
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 getLanguage
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
8
1<?php
2
3/**
4 * Manages all language stuff.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @author    Matteo scaramuccia <matteo@phpmyfaq.de>
13 * @author    Aurimas FiĊĦeras <aurimas@gmail.com>
14 * @copyright 2009-2026 phpMyFAQ Team
15 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
16 * @link      https://www.phpmyfaq.de
17 * @since     2009-05-14
18 */
19
20declare(strict_types=1);
21
22namespace phpMyFAQ;
23
24use phpMyFAQ\Language\LanguageCodes;
25use phpMyFAQ\Language\LanguageDetector;
26use Symfony\Component\HttpFoundation\Session\SessionInterface;
27
28/**
29 * Class Language
30 *
31 * @package phpMyFAQ
32 */
33class Language
34{
35    /**
36     * The current language.
37     */
38    public static string $language = '';
39
40    /**
41     * Detector helper.
42     */
43    private LanguageDetector $languageDetector;
44
45    /**
46     * Constructor.
47     */
48    public function __construct(
49        private readonly Configuration $configuration,
50        private readonly SessionInterface $session,
51    ) {
52        $this->languageDetector = new LanguageDetector($this->session);
53    }
54
55    /**
56     * Returns an array of country codes for a specific FAQ record ID,
57     * specific category ID or all languages used by FAQ records, categories.
58     *
59     * @param int    $identifier ID
60     * @param string $table      Specifies table
61     * @return string[]
62     */
63    public function isLanguageAvailable(int $identifier, string $table = 'faqdata'): array
64    {
65        $output = [];
66
67        // Avoid it else: default values for all records, override if identifier is set
68        $distinct = 'DISTINCT ';
69        $where = '';
70        if ($identifier !== 0) {
71            $distinct = '';
72            $where = ' WHERE id = ' . $identifier;
73        }
74
75        // Correct spacing: "SELECT" then optional DISTINCT
76        $query = 'SELECT ' . $distinct . 'lang FROM ' . Database::getTablePrefix() . $table . $where;
77        $result = $this->configuration->getDb()->query($query);
78
79        if ($this->configuration->getDb()->numRows($result) > 0) {
80            while (true) {
81                $row = $this->configuration->getDb()->fetchObject($result);
82                if (!$row) {
83                    break;
84                }
85
86                $output[] = (string) $row->lang;
87            }
88        }
89
90        return $output;
91    }
92
93    /**
94     * Sets language using browser detection combined with config fallback.
95     */
96    public function setLanguageWithDetection(string $configLanguage): string
97    {
98        $detected = $this->languageDetector->detectAllWithBrowser($configLanguage);
99        self::$language = $this->languageDetector->selectLanguage($detected);
100        $this->session->set(name: 'lang', value: self::$language);
101        return strtolower(self::$language);
102    }
103
104    /**
105     * Sets language only from the provided configuration string.
106     */
107    public function setLanguageFromConfiguration(string $configLanguage): string
108    {
109        $detected = $this->languageDetector->detectAllFromConfig($configLanguage);
110        self::$language = $this->languageDetector->selectLanguage($detected);
111        $this->session->set(name: 'lang', value: self::$language);
112        return strtolower(self::$language);
113    }
114
115    public function setLanguageByAcceptLanguage(): string
116    {
117        $this->languageDetector->detectAllWithBrowser(configLanguage: '');
118        return self::$language = $this->languageDetector->getAcceptLanguage();
119    }
120
121    /**
122     * True if the language is supported by the current phpMyFAQ installation.
123     *
124     * @param string|null $langCode Language code
125     */
126    public static function isASupportedLanguage(?string $langCode): bool
127    {
128        return $langCode !== null && LanguageCodes::getSupported($langCode) !== null;
129    }
130
131    /**
132     * Returns the current language.
133     */
134    public function getLanguage(): string
135    {
136        // If the language is not set, try to get it from session or use default
137        if (self::$language === '') {
138            $sessionLang = $this->session->get('lang');
139            if (is_string($sessionLang) && self::isASupportedLanguage($sessionLang)) {
140                self::$language = $sessionLang;
141            }
142
143            if (self::$language === '') {
144                // Try to fall back to the configured default language
145                try {
146                    $defaultLang = $this->configuration->getDefaultLanguage();
147                    self::$language = $defaultLang !== '' && self::isASupportedLanguage($defaultLang)
148                        ? $defaultLang
149                        : 'en';
150                } catch (\Throwable) {
151                    // If configuration is not available or throws an error, use 'en'
152                    self::$language = 'en';
153                }
154            }
155        }
156
157        return strtolower(self::$language);
158    }
159}