Completed
Push — master ( a9f1b5...4192ac )
by Harald
15s
created

Language::getDef()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 9
c 1
b 0
f 0
nc 3
nop 3
dl 0
loc 16
rs 8.8571
1
<?php
2
/**
3
  * osCommerce Online Merchant
4
  *
5
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
6
  * @license GPL; https://www.oscommerce.com/gpllicense.txt
7
  */
8
9
namespace OSC\OM;
10
11
use OSC\OM\Cache;
12
use OSC\OM\OSCOM;
13
use OSC\OM\Registry;
14
15
class Language
16
{
17
    protected $language;
18
    protected $languages = [];
19
    protected $definitions = [];
20
    protected $detectors = [];
21
    protected $use_cache = false;
22
    protected $db;
23
24
    public function __construct($code = null)
25
    {
26
        $this->db = Registry::get('Db');
27
28
        $Qlanguages = $this->db->prepare('select languages_id, name, code, image, directory from :table_languages order by sort_order');
29
        $Qlanguages->setCache('languages-system');
30
        $Qlanguages->execute();
31
32
        while ($Qlanguages->fetch()) {
33
            $this->languages[$Qlanguages->value('code')] = [
34
                'id' => $Qlanguages->valueInt('languages_id'),
35
                'code' => $Qlanguages->value('code'),
36
                'name' => $Qlanguages->value('name'),
37
                'image' => $Qlanguages->value('image'),
38
                'directory' => $Qlanguages->value('directory')
39
            ];
40
        }
41
42
        $this->detectors = [
43
            'af' => 'af|afrikaans',
44
            'ar' => 'ar([-_][[:alpha:]]{2})?|arabic',
45
            'be' => 'be|belarusian',
46
            'bg' => 'bg|bulgarian',
47
            'br' => 'pt[-_]br|brazilian portuguese',
48
            'ca' => 'ca|catalan',
49
            'cs' => 'cs|czech',
50
            'da' => 'da|danish',
51
            'de' => 'de([-_][[:alpha:]]{2})?|german',
52
            'el' => 'el|greek',
53
            'en' => 'en([-_][[:alpha:]]{2})?|english',
54
            'es' => 'es([-_][[:alpha:]]{2})?|spanish',
55
            'et' => 'et|estonian',
56
            'eu' => 'eu|basque',
57
            'fa' => 'fa|farsi',
58
            'fi' => 'fi|finnish',
59
            'fo' => 'fo|faeroese',
60
            'fr' => 'fr([-_][[:alpha:]]{2})?|french',
61
            'ga' => 'ga|irish',
62
            'gl' => 'gl|galician',
63
            'he' => 'he|hebrew',
64
            'hi' => 'hi|hindi',
65
            'hr' => 'hr|croatian',
66
            'hu' => 'hu|hungarian',
67
            'id' => 'id|indonesian',
68
            'it' => 'it|italian',
69
            'ja' => 'ja|japanese',
70
            'ko' => 'ko|korean',
71
            'ka' => 'ka|georgian',
72
            'lt' => 'lt|lithuanian',
73
            'lv' => 'lv|latvian',
74
            'mk' => 'mk|macedonian',
75
            'mt' => 'mt|maltese',
76
            'ms' => 'ms|malaysian',
77
            'nl' => 'nl([-_][[:alpha:]]{2})?|dutch',
78
            'no' => 'no|norwegian',
79
            'pl' => 'pl|polish',
80
            'pt' => 'pt([-_][[:alpha:]]{2})?|portuguese',
81
            'ro' => 'ro|romanian',
82
            'ru' => 'ru|russian',
83
            'sk' => 'sk|slovak',
84
            'sq' => 'sq|albanian',
85
            'sr' => 'sr|serbian',
86
            'sv' => 'sv|swedish',
87
            'sz' => 'sz|sami',
88
            'sx' => 'sx|sutu',
89
            'th' => 'th|thai',
90
            'ts' => 'ts|tsonga',
91
            'tr' => 'tr|turkish',
92
            'tn' => 'tn|tswana',
93
            'uk' => 'uk|ukrainian',
94
            'ur' => 'ur|urdu',
95
            'vi' => 'vi|vietnamese',
96
            'tw' => 'zh[-_]tw|chinese traditional',
97
            'zh' => 'zh|chinese simplified',
98
            'ji' => 'ji|yiddish',
99
            'zu' => 'zu|zulu'
100
        ];
101
102
        if (!isset($code) || !$this->exists($code)) {
103
            if (isset($_SESSION['language'])) {
104
                $code = $_SESSION['language'];
105
            } else {
106
                $client = $this->getClientPreference();
107
108
                $code = ($client !== false) ? $client : DEFAULT_LANGUAGE;
109
            }
110
        }
111
112
        $this->set($code);
113
    }
114
115
    public function set($code)
116
    {
117
        if ($this->exists($code)) {
118
            $this->language = $code;
119
        } else {
120
            trigger_error('OSC\OM\Language::set() - The language does not exist: ' . $code);
121
        }
122
    }
123
124
    public function get($data = null, $language_code = null)
125
    {
126
        if (!isset($data)) {
127
            $data = 'code';
128
        }
129
130
        if (!isset($language_code)) {
131
            $language_code = $this->language;
132
        }
133
134
        return $this->languages[$language_code][$data];
135
    }
136
137
    public function getId($language_code = null)
138
    {
139
        return (int)$this->get('id', $language_code);
140
    }
141
142
    public function getAll()
143
    {
144
        return $this->languages;
145
    }
146
147
    public function exists($code)
148
    {
149
        return isset($this->languages[$code]);
150
    }
151
152
    public function getClientPreference()
153
    {
154
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
155
            $client = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
156
157
            foreach ($client as $c) {
158
                foreach ($this->detectors as $code => $value) {
159
                    if (preg_match('/^(' . $value . ')(;q=[0-9]\\.[0-9])?$/i', $c) && $this->exists($code)) {
160
                        return $code;
161
                    }
162
                }
163
            }
164
        }
165
166
        return false;
167
    }
168
169
    public function getDef($key, $values = null, $scope = 'global')
170
    {
171
        if (isset($this->definitions[$scope][$key])) {
172
            $def = $this->definitions[$scope][$key];
173
174
            if (is_array($values) && !empty($values)) {
175
                $def = preg_replace_callback('/\{\{([A-Za-z0-9-_]+)\}\}/', function($matches) use ($values) {
176
                    return isset($values[$matches[1]]) ? $values[$matches[1]] : $matches[1];
177
                }, $def);
178
            }
179
180
            return $def;
181
        }
182
183
        return $key;
184
    }
185
186
    public function definitionsExist($group, $language_code = null)
187
    {
188
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
189
190
        $site = OSCOM::getSite();
191
192
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
193
            $site = $matches[1];
194
            $group = $matches[2];
195
        }
196
197
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
198
199
        // legacy
200
        if (is_file($pathname . '.php')) {
201
            return true;
202
        }
203
204
        $pathname .= '.txt';
205
206
        if (is_file($pathname)) {
207
            return true;
208
        }
209
210
        if ($language_code != 'en') {
211
            return call_user_func([$this, __FUNCTION__], $group, 'en');
212
        }
213
214
        return false;
215
    }
216
217
    public function loadDefinitions($group, $language_code = null, $scope = null)
218
    {
219
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
220
221
        if (!isset($scope)) {
222
            $scope = 'global';
223
        }
224
225
        $site = OSCOM::getSite();
226
227
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
228
            $site = $matches[1];
229
            $group = $matches[2];
230
        }
231
232
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
233
234
        // legacy
235
        if (is_file($pathname . '.php')) {
236
            include($pathname . '.php');
237
            return true;
238
        }
239
240
        $pathname .= '.txt';
241
242
        if ($language_code != 'en') {
243
            call_user_func([$this, __FUNCTION__], $group, 'en', $scope);
244
        }
245
246
        $defs = $this->getDefinitions($group, $language_code, $pathname);
247
248
        $this->injectDefinitions($defs, $scope);
249
    }
250
251
    public function getDefinitions($group, $language_code, $pathname)
252
    {
253
        $defs = [];
254
255
        $group_key = str_replace(['/', '\\'], '-', $group);
256
257
        if ($this->use_cache === false) {
258
            return $this->getDefinitionsFromFile($pathname);
259
        }
260
261
        $DefCache = new Cache('languages-defs-' . $group_key . '-lang' . $this->getId($language_code));
262
263
        if ($DefCache->exists()) {
264
            $defs = $DefCache->get();
265
        } else {
266
            $Qdefs = $this->db->get('languages_definitions', [
267
                'definition_key',
268
                'definition_value'
269
            ], [
270
                'languages_id' => $this->getId($language_code),
271
                'content_group' => $group_key
272
            ]);
273
274
            while ($Qdefs->fetch()) {
275
                $defs[$Qdefs->value('definition_key')] = $Qdefs->value('definition_value');
276
            }
277
278
            if (empty($defs)) {
279
                $defs = $this->getDefinitionsFromFile($pathname);
280
281
                foreach ($defs as $key => $value) {
0 ignored issues
show
Bug introduced by
The expression $defs of type false|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
282
                    $this->db->save('languages_definitions', [
283
                        'languages_id' => $this->getId($language_code),
284
                        'content_group' => $group_key,
285
                        'definition_key' => $key,
286
                        'definition_value' => $value
287
                    ]);
288
                }
289
            }
290
291
            $DefCache->save($defs);
292
        }
293
294
        return $defs;
295
    }
296
297
    public function getDefinitionsFromFile($filename)
298
    {
299
        if (!is_file($filename)) {
300
            trigger_error('OSC\OM\Language::getDefinitionsFromFile() - Filename does not exist: ' . $filename);
301
302
            return false;
303
        }
304
305
        $defs = [];
306
307
        foreach (file($filename) as $line) {
308
            $line = trim($line);
309
310
            if (!empty($line) && (substr($line, 0, 1) != '#')) {
311
                $delimiter = strpos($line, '=');
312
313
                if (($delimiter !== false) && (preg_match('/^[A-Za-z0-9_-]/', substr($line, 0, $delimiter)) === 1) && (substr_count(substr($line, 0, $delimiter), ' ') === 1)) {
314
                    $key = trim(substr($line, 0, $delimiter));
315
                    $value = trim(substr($line, $delimiter + 1));
316
317
                    $defs[$key] = $value;
318
                } elseif (isset($key)) {
319
                    $defs[$key] .= "\n" . $line;
320
                }
321
            }
322
        }
323
324
        return $defs;
325
    }
326
327
    public function injectDefinitions($defs, $scope)
328
    {
329
        if (isset($this->definitions[$scope])) {
330
            $this->definitions[$scope] = array_merge($this->definitions[$scope], $defs);
331
        } else {
332
            $this->definitions[$scope] = $defs;
333
        }
334
    }
335
336
    public function setUseCache($flag)
337
    {
338
        $this->use_cache = ($flag === true);
339
    }
340
}
341