Language   C
last analyzed

Complexity

Total Complexity 70

Size/Duplication

Total Lines 344
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 344
rs 5.6163
c 1
b 0
f 0
wmc 70
lcom 1
cbo 4

16 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 90 6
A set() 0 8 2
A get() 0 12 3
A getId() 0 4 1
A getAll() 0 4 1
A exists() 0 4 1
B getImage() 0 12 5
B getClientPreference() 0 16 7
A parseDefinition() 0 10 4
D definitionsExist() 0 30 9
D loadDefinitions() 0 33 9
B getDefinitions() 0 45 6
D getDefinitionsFromFile() 0 25 9
A injectDefinitions() 0 8 2
A setUseCache() 0 4 1
A getDef() 0 14 4

How to fix   Complexity   

Complex Class

Complex classes like Language often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Language, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
  * osCommerce Online Merchant
4
  *
5
  * @copyright (c) 2016 osCommerce; https://www.oscommerce.com
6
  * @license MIT; https://www.oscommerce.com/license/mit.txt
7
  */
8
9
namespace OSC\OM;
10
11
use OSC\OM\Cache;
12
use OSC\OM\HTML;
13
use OSC\OM\OSCOM;
14
use OSC\OM\Registry;
15
16
class Language
17
{
18
    protected $language;
19
    protected $languages = [];
20
    protected $definitions = [];
21
    protected $detectors = [];
22
    protected $use_cache = false;
23
    protected $db;
24
25
    public function __construct($code = null)
26
    {
27
        $this->db = Registry::get('Db');
28
29
        $Qlanguages = $this->db->prepare('select languages_id, name, code, image, directory from :table_languages order by sort_order');
30
        $Qlanguages->setCache('languages-system');
31
        $Qlanguages->execute();
32
33
        while ($Qlanguages->fetch()) {
34
            $this->languages[$Qlanguages->value('code')] = [
35
                'id' => $Qlanguages->valueInt('languages_id'),
36
                'code' => $Qlanguages->value('code'),
37
                'name' => $Qlanguages->value('name'),
38
                'image' => $Qlanguages->value('image'),
39
                'directory' => $Qlanguages->value('directory')
40
            ];
41
        }
42
43
        $this->detectors = [
44
            'af' => 'af|afrikaans',
45
            'ar' => 'ar([-_][[:alpha:]]{2})?|arabic',
46
            'be' => 'be|belarusian',
47
            'bg' => 'bg|bulgarian',
48
            'br' => 'pt[-_]br|brazilian portuguese',
49
            'ca' => 'ca|catalan',
50
            'cs' => 'cs|czech',
51
            'da' => 'da|danish',
52
            'de' => 'de([-_][[:alpha:]]{2})?|german',
53
            'el' => 'el|greek',
54
            'en' => 'en([-_][[:alpha:]]{2})?|english',
55
            'es' => 'es([-_][[:alpha:]]{2})?|spanish',
56
            'et' => 'et|estonian',
57
            'eu' => 'eu|basque',
58
            'fa' => 'fa|farsi',
59
            'fi' => 'fi|finnish',
60
            'fo' => 'fo|faeroese',
61
            'fr' => 'fr([-_][[:alpha:]]{2})?|french',
62
            'ga' => 'ga|irish',
63
            'gl' => 'gl|galician',
64
            'he' => 'he|hebrew',
65
            'hi' => 'hi|hindi',
66
            'hr' => 'hr|croatian',
67
            'hu' => 'hu|hungarian',
68
            'id' => 'id|indonesian',
69
            'it' => 'it|italian',
70
            'ja' => 'ja|japanese',
71
            'ko' => 'ko|korean',
72
            'ka' => 'ka|georgian',
73
            'lt' => 'lt|lithuanian',
74
            'lv' => 'lv|latvian',
75
            'mk' => 'mk|macedonian',
76
            'mt' => 'mt|maltese',
77
            'ms' => 'ms|malaysian',
78
            'nl' => 'nl([-_][[:alpha:]]{2})?|dutch',
79
            'no' => 'no|norwegian',
80
            'pl' => 'pl|polish',
81
            'pt' => 'pt([-_][[:alpha:]]{2})?|portuguese',
82
            'ro' => 'ro|romanian',
83
            'ru' => 'ru|russian',
84
            'sk' => 'sk|slovak',
85
            'sq' => 'sq|albanian',
86
            'sr' => 'sr|serbian',
87
            'sv' => 'sv|swedish',
88
            'sz' => 'sz|sami',
89
            'sx' => 'sx|sutu',
90
            'th' => 'th|thai',
91
            'ts' => 'ts|tsonga',
92
            'tr' => 'tr|turkish',
93
            'tn' => 'tn|tswana',
94
            'uk' => 'uk|ukrainian',
95
            'ur' => 'ur|urdu',
96
            'vi' => 'vi|vietnamese',
97
            'tw' => 'zh[-_]tw|chinese traditional',
98
            'zh' => 'zh|chinese simplified',
99
            'ji' => 'ji|yiddish',
100
            'zu' => 'zu|zulu'
101
        ];
102
103
        if (!isset($code) || !$this->exists($code)) {
104
            if (isset($_SESSION['language'])) {
105
                $code = $_SESSION['language'];
106
            } else {
107
                $client = $this->getClientPreference();
108
109
                $code = ($client !== false) ? $client : DEFAULT_LANGUAGE;
110
            }
111
        }
112
113
        $this->set($code);
114
    }
115
116
    public function set($code)
117
    {
118
        if ($this->exists($code)) {
119
            $this->language = $code;
120
        } else {
121
            trigger_error('OSC\OM\Language::set() - The language does not exist: ' . $code);
122
        }
123
    }
124
125
    public function get($data = null, $language_code = null)
126
    {
127
        if (!isset($data)) {
128
            $data = 'code';
129
        }
130
131
        if (!isset($language_code)) {
132
            $language_code = $this->language;
133
        }
134
135
        return $this->languages[$language_code][$data];
136
    }
137
138
    public function getId($language_code = null)
139
    {
140
        return (int)$this->get('id', $language_code);
141
    }
142
143
    public function getAll()
144
    {
145
        return $this->languages;
146
    }
147
148
    public function exists($code)
149
    {
150
        return isset($this->languages[$code]);
151
    }
152
153
    public function getImage($language_code, $width = null, $height = null)
154
    {
155
        if (!isset($width) || !is_int($width)) {
156
            $width = 16;
157
        }
158
159
        if (!isset($height) || !is_int($height)) {
160
            $height = 12;
161
        }
162
163
        return HTML::image(OSCOM::link('Shop/public/third_party/flag-icon-css/flags/4x3/' . $this->get('image', $language_code) . '.svg', null, false), $this->get('name', $language_code), $width, $height);
164
    }
165
166
    public function getClientPreference()
167
    {
168
        if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) && !empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
169
            $client = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
170
171
            foreach ($client as $c) {
172
                foreach ($this->detectors as $code => $value) {
173
                    if (preg_match('/^(' . $value . ')(;q=[0-9]\\.[0-9])?$/i', $c) && $this->exists($code)) {
174
                        return $code;
175
                    }
176
                }
177
            }
178
        }
179
180
        return false;
181
    }
182
183
    public function getDef($key, $values = null, $scope = 'global')
184
    {
185
        if (isset($this->definitions[$scope][$key])) {
186
            $def = $this->definitions[$scope][$key];
187
188
            if (is_array($values) && !empty($values)) {
189
                $def = $this->parseDefinition($def, $values);
190
            }
191
192
            return $def;
193
        }
194
195
        return $key;
196
    }
197
198
    public static function parseDefinition($string, $values)
199
    {
200
        if (is_array($values) && !empty($values)) {
201
            $string = preg_replace_callback('/\{\{([A-Za-z0-9-_]+)\}\}/', function($matches) use ($values) {
202
                return isset($values[$matches[1]]) ? $values[$matches[1]] : $matches[1];
203
            }, $string);
204
        }
205
206
        return $string;
207
    }
208
209
    public function definitionsExist($group, $language_code = null)
210
    {
211
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
212
213
        $site = OSCOM::getSite();
214
215
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
216
            $site = $matches[1];
217
            $group = $matches[2];
218
        }
219
220
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
221
222
        // legacy
223
        if (is_file($pathname . '.php')) {
224
            return true;
225
        }
226
227
        $pathname .= '.txt';
228
229
        if (is_file($pathname)) {
230
            return true;
231
        }
232
233
        if ($language_code != 'en') {
234
            return call_user_func([$this, __FUNCTION__], $group, 'en');
235
        }
236
237
        return false;
238
    }
239
240
    public function loadDefinitions($group, $language_code = null, $scope = null)
241
    {
242
        $language_code = isset($language_code) && $this->exists($language_code) ? $language_code : $this->get('code');
243
244
        if (!isset($scope)) {
245
            $scope = 'global';
246
        }
247
248
        $site = OSCOM::getSite();
249
250
        if ((strpos($group, '/') !== false) && (preg_match('/^([A-Z][A-Za-z0-9-_]*)\/(.*)$/', $group, $matches) === 1) && OSCOM::siteExists($matches[1])) {
251
            $site = $matches[1];
252
            $group = $matches[2];
253
        }
254
255
        $pathname = OSCOM::getConfig('dir_root', $site) . 'includes/languages/' . $this->get('directory', $language_code) . '/' . $group;
256
257
        // legacy
258
        if (is_file($pathname . '.php')) {
259
            include($pathname . '.php');
260
            return true;
261
        }
262
263
        $pathname .= '.txt';
264
265
        if ($language_code != 'en') {
266
            call_user_func([$this, __FUNCTION__], $group, 'en', $scope);
267
        }
268
269
        $defs = $this->getDefinitions($group, $language_code, $pathname);
270
271
        $this->injectDefinitions($defs, $scope);
272
    }
273
274
    public function getDefinitions($group, $language_code, $pathname)
275
    {
276
        $defs = [];
277
278
        $group_key = str_replace(['/', '\\'], '-', $group);
279
280
        if ($this->use_cache === false) {
281
            return $this->getDefinitionsFromFile($pathname);
282
        }
283
284
        $DefCache = new Cache('languages-defs-' . $group_key . '-lang' . $this->getId($language_code));
285
286
        if ($DefCache->exists()) {
287
            $defs = $DefCache->get();
288
        } else {
289
            $Qdefs = $this->db->get('languages_definitions', [
290
                'definition_key',
291
                'definition_value'
292
            ], [
293
                'languages_id' => $this->getId($language_code),
294
                'content_group' => $group_key
295
            ]);
296
297
            while ($Qdefs->fetch()) {
298
                $defs[$Qdefs->value('definition_key')] = $Qdefs->value('definition_value');
299
            }
300
301
            if (empty($defs)) {
302
                $defs = $this->getDefinitionsFromFile($pathname);
303
304
                foreach ($defs as $key => $value) {
305
                    $this->db->save('languages_definitions', [
306
                        'languages_id' => $this->getId($language_code),
307
                        'content_group' => $group_key,
308
                        'definition_key' => $key,
309
                        'definition_value' => $value
310
                    ]);
311
                }
312
            }
313
314
            $DefCache->save($defs);
315
        }
316
317
        return $defs;
318
    }
319
320
    public function getDefinitionsFromFile($filename)
321
    {
322
        $defs = [];
323
324
        if (is_file($filename)) {
325
            foreach (file($filename) as $line) {
326
                $line = trim($line);
327
328
                if (!empty($line) && (substr($line, 0, 1) != '#')) {
329
                    $delimiter = strpos($line, '=');
330
331
                    if (($delimiter !== false) && (preg_match('/^[A-Za-z0-9_-]/', substr($line, 0, $delimiter)) === 1) && (substr_count(substr($line, 0, $delimiter), ' ') === 1)) {
332
                        $key = trim(substr($line, 0, $delimiter));
333
                        $value = trim(substr($line, $delimiter + 1));
334
335
                        $defs[$key] = $value;
336
                    } elseif (isset($key)) {
337
                        $defs[$key] .= "\n" . $line;
338
                    }
339
                }
340
            }
341
        }
342
343
        return $defs;
344
    }
345
346
    public function injectDefinitions($defs, $scope)
347
    {
348
        if (isset($this->definitions[$scope])) {
349
            $this->definitions[$scope] = array_merge($this->definitions[$scope], $defs);
350
        } else {
351
            $this->definitions[$scope] = $defs;
352
        }
353
    }
354
355
    public function setUseCache($flag)
356
    {
357
        $this->use_cache = ($flag === true);
358
    }
359
}
360