Completed
Push — master ( 7ad5ba...b4652c )
by Harald
03:30
created

Language::injectDefinitions()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
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 = $this->parseDefinition($def, $values);
176
            }
177
178
            return $def;
179
        }
180
181
        return $key;
182
    }
183
184
    public static function parseDefinition($string, $values)
185
    {
186
        if (is_array($values) && !empty($values)) {
187
            $string = preg_replace_callback('/\{\{([A-Za-z0-9-_]+)\}\}/', function($matches) use ($values) {
188
                return isset($values[$matches[1]]) ? $values[$matches[1]] : $matches[1];
189
            }, $string);
190
        }
191
192
        return $string;
193
    }
194
195
    public function definitionsExist($group, $language_code = null)
196
    {
197
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
198
199
        $site = OSCOM::getSite();
200
201
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
202
            $site = $matches[1];
203
            $group = $matches[2];
204
        }
205
206
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
207
208
        // legacy
209
        if (is_file($pathname . '.php')) {
210
            return true;
211
        }
212
213
        $pathname .= '.txt';
214
215
        if (is_file($pathname)) {
216
            return true;
217
        }
218
219
        if ($language_code != 'en') {
220
            return call_user_func([$this, __FUNCTION__], $group, 'en');
221
        }
222
223
        return false;
224
    }
225
226
    public function loadDefinitions($group, $language_code = null, $scope = null)
227
    {
228
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
229
230
        if (!isset($scope)) {
231
            $scope = 'global';
232
        }
233
234
        $site = OSCOM::getSite();
235
236
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
237
            $site = $matches[1];
238
            $group = $matches[2];
239
        }
240
241
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
242
243
        // legacy
244
        if (is_file($pathname . '.php')) {
245
            include($pathname . '.php');
246
            return true;
247
        }
248
249
        $pathname .= '.txt';
250
251
        if ($language_code != 'en') {
252
            call_user_func([$this, __FUNCTION__], $group, 'en', $scope);
253
        }
254
255
        $defs = $this->getDefinitions($group, $language_code, $pathname);
256
257
        $this->injectDefinitions($defs, $scope);
258
    }
259
260
    public function getDefinitions($group, $language_code, $pathname)
261
    {
262
        $defs = [];
263
264
        $group_key = str_replace(['/', '\\'], '-', $group);
265
266
        if ($this->use_cache === false) {
267
            return $this->getDefinitionsFromFile($pathname);
268
        }
269
270
        $DefCache = new Cache('languages-defs-' . $group_key . '-lang' . $this->getId($language_code));
271
272
        if ($DefCache->exists()) {
273
            $defs = $DefCache->get();
274
        } else {
275
            $Qdefs = $this->db->get('languages_definitions', [
276
                'definition_key',
277
                'definition_value'
278
            ], [
279
                'languages_id' => $this->getId($language_code),
280
                'content_group' => $group_key
281
            ]);
282
283
            while ($Qdefs->fetch()) {
284
                $defs[$Qdefs->value('definition_key')] = $Qdefs->value('definition_value');
285
            }
286
287
            if (empty($defs)) {
288
                $defs = $this->getDefinitionsFromFile($pathname);
289
290
                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...
291
                    $this->db->save('languages_definitions', [
292
                        'languages_id' => $this->getId($language_code),
293
                        'content_group' => $group_key,
294
                        'definition_key' => $key,
295
                        'definition_value' => $value
296
                    ]);
297
                }
298
            }
299
300
            $DefCache->save($defs);
301
        }
302
303
        return $defs;
304
    }
305
306
    public function getDefinitionsFromFile($filename)
307
    {
308
        if (!is_file($filename)) {
309
            trigger_error('OSC\OM\Language::getDefinitionsFromFile() - Filename does not exist: ' . $filename);
310
311
            return false;
312
        }
313
314
        $defs = [];
315
316
        foreach (file($filename) as $line) {
317
            $line = trim($line);
318
319
            if (!empty($line) && (substr($line, 0, 1) != '#')) {
320
                $delimiter = strpos($line, '=');
321
322
                if (($delimiter !== false) && (preg_match('/^[A-Za-z0-9_-]/', substr($line, 0, $delimiter)) === 1) && (substr_count(substr($line, 0, $delimiter), ' ') === 1)) {
323
                    $key = trim(substr($line, 0, $delimiter));
324
                    $value = trim(substr($line, $delimiter + 1));
325
326
                    $defs[$key] = $value;
327
                } elseif (isset($key)) {
328
                    $defs[$key] .= "\n" . $line;
329
                }
330
            }
331
        }
332
333
        return $defs;
334
    }
335
336
    public function injectDefinitions($defs, $scope)
337
    {
338
        if (isset($this->definitions[$scope])) {
339
            $this->definitions[$scope] = array_merge($this->definitions[$scope], $defs);
340
        } else {
341
            $this->definitions[$scope] = $defs;
342
        }
343
    }
344
345
    public function setUseCache($flag)
346
    {
347
        $this->use_cache = ($flag === true);
348
    }
349
}
350