Lang::addLang()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 15
rs 10
cc 1
nc 1
nop 2
1
<?php
2
3
/**
4
 * Copyright (c) 2018 Justin Kuenzel (jukusoft.com)
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
20
/**
21
 * Language Detection
22
 */
23
24
class Lang {
25
26
	protected static $supported_languages = array();
27
	protected static $initialized = false;
28
29
	//https://paulund.co.uk/auto-detect-browser-language-in-php
30
31
	public static function getPrefLangToken () : string {
32
		if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
33
			$_SERVER['HTTP_ACCEPT_LANGUAGE'] = Settings::get("default_lang");
34
		}
35
36
		return substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
37
	}
38
39
	public static function getLangToken (array $supported_lang_tokens) : string {
40
		//http://php.net/manual/fa/function.http-negotiate-language.php
41
42
		//https://stackoverflow.com/questions/6038236/using-the-php-http-accept-language-server-variable
43
44
		//https://stackoverflow.com/questions/3770513/detect-browser-language-in-php
45
46
		return self::prefered_language($supported_lang_tokens);//http_negotiate_language($supported_lang_tokens);
47
	}
48
49
	public static function loadSupportedLangs () {
50
		if (Cache::contains("supported-languages", "list")) {
51
			self::$supported_languages = Cache::get("supported-languages", "list");
52
		} else {
53
			$rows = Database::getInstance()->listRows("SELECT * FROM `{praefix}supported_languages`; ");
54
55
			$array = array();
56
57
			foreach ($rows as $row) {
58
				$array[$row['lang_token']] = $row;
59
			}
60
61
			//cache values
62
			Cache::put("supported-languages", "list", $array);
63
64
			self::$supported_languages = $array;
65
		}
66
67
		self::$initialized = true;
68
	}
69
70
	protected static function loadIfAbsent () {
71
		if (!self::$initialized) {
72
			self::loadSupportedLangs();
73
		}
74
	}
75
76
	public static function listSupportedLangTokens () {
77
		//load tokens, if not initialized
78
		self::loadIfAbsent();
79
80
		$keys = array_keys(self::$supported_languages);
81
82
		//get default language
83
		$default_lang = Settings::get("default_lang");
84
85
		if (!in_array($default_lang, $keys)) {
86
			throw new IllegalStateException("default language (in global settings) isnt a supported language");
87
		}
88
89
		//remove element from array
90
		if (($key = array_search($default_lang, $keys)) !== false) {
91
			unset($keys[$key]);
92
		}
93
94
		//add as first element
95
		array_unshift($keys, $default_lang);
96
97
		return $keys;
98
	}
99
100
	public static function addLang (string $token, string $title) {
101
		Database::getInstance()->execute("INSERT INTO `{praefix}supported_languages` (
102
			`lang_token`, `title`
103
		) VALUES (
104
			:token, :title
105
		); ", array(
106
			'token' => $token,
107
			'title' => $title
108
		));
109
110
		//clear local in-memory cache
111
		self::$initialized = false;
112
113
		//clear cache
114
		Cache::clear("supported-languages");
115
	}
116
117
	public static function addLangOrUpdate (string $token, string $title) {
118
		Database::getInstance()->execute("INSERT INTO `{praefix}supported_languages` (
119
			`lang_token`, `title`
120
		) VALUES (
121
			:token, :title
122
		) ON DUPLICATE KEY UPDATE `title` = :title; ", array(
123
			'token' => $token,
124
			'title' => $title
125
		));
126
127
		//clear local in-memory cache
128
		self::$initialized = false;
129
130
		//clear cache
131
		Cache::clear("supported-languages");
132
	}
133
134
	/**
135
	 * determine which language out of an available set the user prefers most
136
	 *
137
	 * @param $available_languages array with language-tag-strings (must be lowercase) that are available
138
	 * @param $http_accept_language a HTTP_ACCEPT_LANGUAGE string (read from $_SERVER['HTTP_ACCEPT_LANGUAGE'] if left out)
139
	 */
140
141
	/**
142
	 * determine which language out of an available set the user prefers most
143
	 *
144
	 * @param $available_languages array with language-tag-strings (must be lowercase) that are available
145
	 * @param $http_accept_language string HTTP_ACCEPT_LANGUAGE string (read from $_SERVER['HTTP_ACCEPT_LANGUAGE'] if left out)
146
	 *
147
	 * @link http://www.theserverpages.com/php/manual/en/function.http-negotiate-language.php
148
	 *
149
	 * @return prefered language
0 ignored issues
show
Bug introduced by
The type prefered was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
150
	 */
151
	protected static function prefered_language (array $available_languages, string $http_accept_language = "auto") : string {
152
		if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
153
			$_SERVER['HTTP_ACCEPT_LANGUAGE'] = Settings::get("default_lang");
154
		}
155
156
		// if $http_accept_language was left out, read it from the HTTP-Header
157
		if ($http_accept_language == "auto") $http_accept_language = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
158
159
		// standard  for HTTP_ACCEPT_LANGUAGE is defined under
160
		// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
161
		// pattern to find is therefore something like this:
162
		//    1#( language-range [ ";" "q" "=" qvalue ] )
163
		// where:
164
		//    language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
165
		//    qvalue         = ( "0" [ "." 0*3DIGIT ] )
166
		//            | ( "1" [ "." 0*3("0") ] )
167
		preg_match_all("/([[:alpha:]]{1,8})(-([[:alpha:]|-]{1,8}))?" .
168
			"(\s*;\s*q\s*=\s*(1\.0{0,3}|0\.\d{0,3}))?\s*(,|$)/i",
169
			$http_accept_language, $hits, PREG_SET_ORDER);
170
171
		// default language (in case of no hits) is the first in the array
172
		$bestlang = $available_languages[0];
173
		$bestqval = 0;
174
175
		foreach ($hits as $arr) {
176
			// read data from the array of this hit
177
			$langprefix = strtolower ($arr[1]);
178
			if (!empty($arr[3])) {
179
				$langrange = strtolower ($arr[3]);
180
				$language = $langprefix . "-" . $langrange;
181
			}
182
			else $language = $langprefix;
183
			$qvalue = 1.0;
184
			if (!empty($arr[5])) $qvalue = floatval($arr[5]);
185
186
			// find q-maximal language
187
			if (in_array($language,$available_languages) && ($qvalue > $bestqval)) {
188
				$bestlang = $language;
189
				$bestqval = $qvalue;
190
			}
191
			// if no direct hit, try the prefix only but decrease q-value by 10% (as http_negotiate_language does)
192
			else if (in_array($langprefix,$available_languages) && (($qvalue*0.9) > $bestqval)) {
193
				$bestlang = $langprefix;
194
				$bestqval = $qvalue*0.9;
195
			}
196
		}
197
		return $bestlang;
198
	}
199
200
}
201
202
?>
0 ignored issues
show
Best Practice introduced by
It is not recommended to use PHP's closing tag ?> in files other than templates.

Using a closing tag in PHP files that only contain PHP code is not recommended as you might accidentally add whitespace after the closing tag which would then be output by PHP. This can cause severe problems, for example headers cannot be sent anymore.

A simple precaution is to leave off the closing tag as it is not required, and it also has no negative effects whatsoever.

Loading history...
203