Language   F
last analyzed

Complexity

Total Complexity 177

Size/Duplication

Total Lines 684
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 684
rs 1.916
c 0
b 0
f 0
wmc 177
lcom 3
cbo 3

26 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getFields() 0 14 2
A _generateFiles() 0 13 5
C moveToIso() 0 34 12
A _getThemesList() 0 9 6
A toggleStatus() 0 15 3
A checkFiles() 0 4 1
A checkFilesWithIsoCode() 0 10 5
F getFilesList() 0 111 46
B loadUpdateSQL() 0 34 9
B recurseDeleteDir() 0 18 8
F delete() 0 71 19
B deleteSelection() 0 24 7
A getLanguages() 0 14 5
A getLanguage() 0 6 2
A getIsoById() 0 6 2
A getIdByIso() 0 7 2
A getLanguageCodeByIso() 0 7 2
A getIsoIds() 0 4 2
B copyLanguageData() 0 24 7
A loadLanguages() 0 7 2
A update() 0 18 3
C checkAndAddLanguage() 0 57 15
A _copyNoneFlag() 0 4 1
A isInstalled() 0 11 4
A countActiveLanguages() 0 6 2

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
* 2007-2012 PrestaShop
4
*
5
* NOTICE OF LICENSE
6
*
7
* This source file is subject to the Open Software License (OSL 3.0)
8
* that is bundled with this package in the file LICENSE.txt.
9
* It is also available through the world-wide-web at this URL:
10
* http://opensource.org/licenses/osl-3.0.php
11
* If you did not receive a copy of the license and are unable to
12
* obtain it through the world-wide-web, please send an email
13
* to [email protected] so we can send you a copy immediately.
14
*
15
* DISCLAIMER
16
*
17
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
18
* versions in the future. If you wish to customize PrestaShop for your
19
* needs please refer to http://www.prestashop.com for more information.
20
*
21
*  @author PrestaShop SA <[email protected]>
22
*  @copyright  2007-2012 PrestaShop SA
23
*  @version  Release: $Revision: 14001 $
24
*  @license    http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
25
*  International Registered Trademark & Property of PrestaShop SA
26
*/
27
28
class Language extends ObjectModel
29
{
30
    public      $id;
31
32
    /** @var string Name */
33
    public      $name;
34
35
    /** @var string 2-letter iso code */
36
    public      $iso_code;
37
38
    /** @var string 5-letter iso code */
39
    public      $language_code;
40
41
    /** @var string date format http://http://php.net/manual/en/function.date.php with the date only */
42
    public      $date_format_lite = 'Y-m-d';
43
    
44
    /** @var string date format http://http://php.net/manual/en/function.date.php with hours and minutes */
45
    public      $date_format_full = 'Y-m-d H:i:s';
46
47
    /** @var bool true if this language is right to left language */
48
    public      $is_rtl = false;
49
50
    /** @var boolean Status */
51
    public      $active = true;
52
53
    protected   $fieldsRequired = array('name', 'iso_code', 'date_format_lite', 'date_format_full');
54
    protected   $fieldsSize = array('name' => 32, 'iso_code' => 2, 'language_code' => 5, 'date_format_lite' => 32, 'date_format_full' => 32);
55
    protected   $fieldsValidate = array('name' => 'isGenericName', 'iso_code' => 'isLanguageIsoCode', 'language_code' => 'isLanguageCode',
56
    'active' => 'isBool', 'is_rtl' => 'isBool', 'date_format_lite' => 'isPhpDateFormat', 'date_format_full' => 'isPhpDateFormat');
57
58
    protected   $table = 'lang';
59
    protected   $identifier = 'id_lang';
60
61
    /** @var array Languages cache */
62
    protected static $_checkedLangs;
63
    protected static $_LANGUAGES;
64
    protected static $countActiveLanguages;
65
66
    protected $webserviceParameters = array(
67
        'objectNodeName' => 'language',
68
        'objectsNodeName' => 'languages',
69
    );
70
71
    protected $translationsFilesAndVars = array(
72
        'fields' => '_FIELDS',
73
        'errors' => '_ERRORS',
74
        'admin' => '_LANGADM',
75
        'pdf' => '_LANGPDF',
76
    );
77
78
    public  function __construct($id = NULL, $id_lang = NULL)
79
    {
80
        parent::__construct($id);
81
    }
82
83
    public function getFields()
84
    {
85
        parent::validateFields();
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (validateFields() instead of getFields()). Are you sure this is correct? If so, you might want to change this to $this->validateFields().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
86
        $fields['name'] = pSQL($this->name);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$fields was never initialized. Although not strictly required by PHP, it is generally a good practice to add $fields = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
87
        $fields['iso_code'] = pSQL(strtolower($this->iso_code));
88
        $fields['language_code'] = pSQL(strtolower($this->language_code));
89
        $fields['is_rtl'] = (int)$this->is_rtl;
90
        if (empty($fields['language_code']))
91
            $fields['language_code'] = $fields['iso_code'];
92
        $fields['date_format_lite'] = pSQL($this->date_format_lite);
93
        $fields['date_format_full'] = pSQL($this->date_format_full);
94
        $fields['active'] = (int)$this->active;
95
        return $fields;
96
    }
97
    
98
    /**
99
     * Generate traslations files
100
     * 
101
     */
102
    private function _generateFiles($newIso = null)
103
    {
104
        $iso_code = $newIso?$newIso:$this->iso_code;
105
    
106
        if (!file_exists(_PS_TRANSLATIONS_DIR_.$iso_code))
107
            mkdir(_PS_TRANSLATIONS_DIR_.$iso_code);
108
        foreach ($this->translationsFilesAndVars as $file => $var)
109
            if (!file_exists(_PS_TRANSLATIONS_DIR_.$iso_code.'/'.$file.'.php'))
110
                file_put_contents(_PS_TRANSLATIONS_DIR_.$iso_code.'/'.$file.'.php', '<?php
111
    global $'.$var.';
112
    $'.$var.' = array();
113
?>');
114
    }
115
    
116
    /**
117
     * Move translations files after editiing language iso code
118
     */
119
    public function moveToIso($newIso)
120
    {
121
        if($newIso == $this->iso_code)
122
            return true;
123
124
        if (file_exists(_PS_TRANSLATIONS_DIR_.$this->iso_code))
125
            rename(_PS_TRANSLATIONS_DIR_.$this->iso_code, _PS_TRANSLATIONS_DIR_.$newIso);
126
127
        if (file_exists(_PS_MAIL_DIR_.$this->iso_code))
128
            rename(_PS_MAIL_DIR_.$this->iso_code, _PS_MAIL_DIR_.$newIso);
129
130
        $modulesList = Module::getModulesDirOnDisk();
131
        foreach ($modulesList as $moduleDir)
132
        {
133
            if (file_exists(_PS_MODULE_DIR_.$moduleDir.'/mails/'.$this->iso_code))
134
                rename(_PS_MODULE_DIR_.$moduleDir.'/mails/'.$this->iso_code, _PS_MODULE_DIR_.$moduleDir.'/mails/'.$newIso);
135
            
136
            if (file_exists(_PS_MODULE_DIR_.$moduleDir.'/'.$this->iso_code.'.php'))
137
                rename(_PS_MODULE_DIR_.$moduleDir.'/'.$this->iso_code.'.php', _PS_MODULE_DIR_.$moduleDir.'/'.$newIso.'.php');
138
        }
139
140
        foreach ($this->_getThemesList() as $theme => $data)
141
        {
142
            if (file_exists(_PS_ALL_THEMES_DIR_.$theme.'/lang/'.$this->iso_code.'.php'))
143
                rename(_PS_ALL_THEMES_DIR_.$theme.'/lang/'.$this->iso_code.'.php', _PS_ALL_THEMES_DIR_.$theme.'/lang/'.$newIso.'.php');
144
145
            if (file_exists(_PS_ALL_THEMES_DIR_.$theme.'/mails/'.$this->iso_code))
146
                rename(_PS_ALL_THEMES_DIR_.$theme.'/mails/'.$this->iso_code, _PS_ALL_THEMES_DIR_.$theme.'/mails/'.$newIso);
147
148
            foreach ($modulesList as $module)
149
                if (file_exists(_PS_ALL_THEMES_DIR_.$theme.'/modules/'.$module.'/'.$this->iso_code.'.php'))
150
                    rename(_PS_ALL_THEMES_DIR_.$theme.'/modules/'.$module.'/'.$this->iso_code.'.php', _PS_ALL_THEMES_DIR_.$theme.'/modules/'.$module.'/'.$newIso.'.php');
151
        }
152
    }
153
    
154
    /**
155
      * Return an array with themes and thumbnails
156
      *
157
      * @return array
158
      */
159
    private function _getThemesList()
160
    {
161
        $dir = opendir(_PS_ALL_THEMES_DIR_);
162
        while ($folder = readdir($dir))
163
            if ($folder != '.' AND $folder != '..' AND file_exists(_PS_ALL_THEMES_DIR_.'/'.$folder.'/preview.jpg'))
164
                $themes[$folder]['name'] = $folder;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$themes was never initialized. Although not strictly required by PHP, it is generally a good practice to add $themes = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
165
        closedir($dir); 
166
        return isset($themes) ? $themes : array();
167
    }
168
    
169
    public function add($autodate = true, $nullValues = false)
170
    {
171
        if (!parent::add($autodate))
172
            return false;
173
174
        // create empty files if they not exists
175
        $this->_generateFiles();
176
177
        $resUpdateSQL = $this->loadUpdateSQL();
178
        // If url_rewrite is not enabled, we don't need to regenerate .htaccess
179
        if (!Configuration::get('PS_REWRITING_SETTINGS'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_REWRITING_SETTINGS') of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
180
            return $resUpdateSQL;
181
182
        return ($resUpdateSQL AND Tools::generateHtaccess(dirname(__FILE__).'/../.htaccess',
183
            (int)(Configuration::get('PS_REWRITING_SETTINGS')),
184
            (int)(Configuration::get('PS_HTACCESS_CACHE_CONTROL')),
185
            Configuration::get('PS_HTACCESS_SPECIFIC'),
186
            (int)Configuration::get('PS_HTACCESS_DISABLE_MULTIVIEWS')
0 ignored issues
show
Documentation introduced by
(int) \Configuration::ge...SS_DISABLE_MULTIVIEWS') is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
187
        ));
188
    }
189
190
    public function toggleStatus()
191
    {
192
        if (!parent::toggleStatus())
193
            return false;
194
195
        // If url_rewrite is not enabled, we don't need to regenerate .htaccess
196
        if (!Configuration::get('PS_REWRITING_SETTINGS'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_REWRITING_SETTINGS') of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
197
            return true;
198
        return (Tools::generateHtaccess(dirname(__FILE__).'/../.htaccess',
199
            (int)(Configuration::get('PS_REWRITING_SETTINGS')),
200
            (int)(Configuration::get('PS_HTACCESS_CACHE_CONTROL')),
201
            Configuration::get('PS_HTACCESS_SPECIFIC'),
202
            (int)Configuration::get('PS_HTACCESS_DISABLE_MULTIVIEWS')
0 ignored issues
show
Documentation introduced by
(int) \Configuration::ge...SS_DISABLE_MULTIVIEWS') is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
203
        ));
204
    }
205
206
    public function checkFiles()
207
    {
208
        return self::checkFilesWithIsoCode($this->iso_code);
209
    }
210
211
212
    /**
213
     * This functions checks if every files exists for the language $iso_code.
214
     * Concerned files are theses located in translations/$iso_code/
215
     * and translations/mails/$iso_code .
216
     *
217
     * @param mixed $iso_code
218
     * @returntrue if all files exists
219
     */
220
    public static function checkFilesWithIsoCode($iso_code)
221
    {
222
        if (isset(self::$_checkedLangs[$iso_code]) AND self::$_checkedLangs[$iso_code])
223
            return true;
224
        foreach (array_keys(self::getFilesList($iso_code, _THEME_NAME_, false, false, false, true)) as $key)
225
            if (!file_exists($key))
226
                return false;
227
        self::$_checkedLangs[$iso_code] = true;
228
        return true;
229
    }
230
231
    public static function getFilesList($iso_from, $theme_from, $iso_to = false, $theme_to = false, $select = false, $check = false, $modules = false)
232
    {
233
        if (empty($iso_from))
234
            die(Tools::displayError());
0 ignored issues
show
Coding Style Compatibility introduced by
The method getFilesList() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
235
236
        $copy = ($iso_to AND $theme_to) ? true : false;
237
238
        $lPath_from = _PS_TRANSLATIONS_DIR_.(string)$iso_from.'/';
239
        $tPath_from = _PS_ROOT_DIR_.'/themes/'.(string)$theme_from.'/';
240
        $mPath_from = _PS_MAIL_DIR_.(string)$iso_from.'/';
241
242
        if ($copy)
243
        {
244
            $lPath_to = _PS_TRANSLATIONS_DIR_.(string)$iso_to.'/';
245
            $tPath_to = _PS_ROOT_DIR_.'/themes/'.(string)$theme_to.'/';
246
            $mPath_to = _PS_MAIL_DIR_.(string)$iso_to.'/';
247
        }
248
249
        $lFiles = array('admin'.'.php', 'errors'.'.php', 'fields'.'.php', 'pdf'.'.php');
250
        $mFiles =  array('account.html', 'account.txt', 'bankwire.html', 'bankwire.txt',
251
        'cheque.html', 'cheque.txt', 'contact.html', 'contact.txt', 'contact_form.html',
252
        'contact_form.txt', 'credit_slip.html', 'credit_slip.txt', 'download_product.html',
253
        'download_product.txt', 'download-product.tpl', 'employee_password.html', 'employee_password.txt',
254
        'forward_msg.html', 'forward_msg.txt', 'guest_to_customer.html', 'guest_to_customer.txt',
255
        'in_transit.html', 'in_transit.txt', 'log_alert.html', 'log_alert.txt', 'newsletter.html', 'newsletter.txt',
256
        'order_canceled.html', 'order_canceled.txt', 'order_conf.html', 'order_conf.txt',
257
        'order_customer_comment.html', 'order_customer_comment.txt', 'order_merchant_comment.html',
258
        'order_merchant_comment.txt', 'order_return_state.html', 'order_return_state.txt',
259
        'outofstock.html', 'outofstock.txt', 'password.html', 'password.txt', 'password_query.html',
260
        'password_query.txt', 'payment.html', 'payment.txt', 'payment_error.html', 'payment_error.txt',
261
        'preparation.html', 'preparation.txt', 'refund.html', 'refund.txt', 'reply_msg.html',
262
        'reply_msg.txt', 'shipped.html', 'shipped.txt', 'test.html', 'test.txt', 'voucher.html', 'voucher.txt');
263
264
        $number = -1;
265
266
        $files = array();
267
        $files_tr = array();
268
        $files_theme = array();
269
        $files_mail = array();
270
        $files_modules = array();
271
272
        // When a copy is made from a theme in specific language
273
        // to an other theme for the same language,
274
        // it's avoid to copy Translations, Mails files
275
        // and modules files which are not override by theme.
276
        if (!$copy OR $iso_from != $iso_to)
277
        {
278
            // Translations files
279
            if (!$check OR ($check AND (string)$iso_from != 'en'))
280
                foreach ($lFiles as $file)
281
                    $files_tr[$lPath_from.$file] = ($copy ? $lPath_to.$file : ++$number);
0 ignored issues
show
Bug introduced by
The variable $lPath_to does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
282
            if ($select == 'tr')
283
                return $files_tr;
284
            $files = array_merge($files, $files_tr);
285
286
            // Mail files
287
            if (!$check OR ($check AND (string)$iso_from != 'en'))
288
                $files_mail[$mPath_from.'lang.php'] = ($copy ? $mPath_to.'lang.php' : ++$number);
0 ignored issues
show
Bug introduced by
The variable $mPath_to does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
289
            foreach ($mFiles as $file)
290
                $files_mail[$mPath_from.$file] = ($copy ? $mPath_to.$file : ++$number);
291
            if ($select == 'mail')
292
                return $files_mail;
293
            $files = array_merge($files, $files_mail);
294
295
            // Modules
296
            if ($modules)
297
            {
298
                $modList = Module::getModulesDirOnDisk();
299
                foreach ($modList as $mod)
300
                {
301
                    $modDir = _PS_MODULE_DIR_.$mod;
302
                    // Lang file
303
                    if (file_exists($modDir.'/'.(string)$iso_from.'.php'))
304
                        $files_modules[$modDir.'/'.(string)$iso_from.'.php'] = ($copy ? $modDir.'/'.(string)$iso_to.'.php' : ++$number);
305
                    // Mails files
306
                    $modMailDirFrom = $modDir.'/mails/'.(string)$iso_from;
307
                    $modMailDirTo = $modDir.'/mails/'.(string)$iso_to;
308
                    if (file_exists($modMailDirFrom))
309
                    {
310
                        $dirFiles = scandir($modMailDirFrom);
311
                        foreach ($dirFiles as $file)
312
                            if (file_exists($modMailDirFrom.'/'.$file) AND $file != '.' AND $file != '..' AND $file != '.svn')
313
                                $files_modules[$modMailDirFrom.'/'.$file] = ($copy ? $modMailDirTo.'/'.$file : ++$number);
314
                    }
315
                }
316
                if ($select == 'modules')
317
                    return $files_modules;
318
                $files = array_merge($files, $files_modules);
319
            }
320
        }
321
        elseif ($select == 'mail' OR $select == 'tr')
322
        {
323
            return $files;
324
        }
325
326
        // Theme files
327
        if (!$check OR ($check AND (string)$iso_from != 'en'))
328
        {
329
            $files_theme[$tPath_from.'lang/'.(string)$iso_from.'.php'] = ($copy ? $tPath_to.'lang/'.(string)$iso_to.'.php' : ++$number);
0 ignored issues
show
Bug introduced by
The variable $tPath_to does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
330
            $module_theme_files = (file_exists($tPath_from.'modules/') ? scandir($tPath_from.'modules/') : array());
331
            foreach ($module_theme_files as $module)
332
                if ($module !== '.' AND $module != '..' AND $module !== '.svn' AND file_exists($tPath_from.'modules/'.$module.'/'.(string)$iso_from.'.php'))
333
                    $files_theme[$tPath_from.'modules/'.$module.'/'.(string)$iso_from.'.php'] = ($copy ? $tPath_to.'modules/'.$module.'/'.(string)$iso_to.'.php' : ++$number);
334
        }
335
        if ($select == 'theme')
336
            return $files_theme;
337
        $files = array_merge($files, $files_theme);
338
339
        // Return
340
        return $files;
341
    }
342
343
    /**
344
     * loadUpdateSQL will create default lang values when you create a new lang, based on default id lang
345
     * 
346
     * @return boolean true if succeed
347
     */
348
    public function loadUpdateSQL()
349
    {
350
        $tables = Db::getInstance()->ExecuteS('SHOW TABLES LIKE \''._DB_PREFIX_.'%_lang\' ');
351
        $langTables = array();
352
353
        foreach($tables as $table)
354
            foreach($table as $t)
355
                if ($t != _DB_PREFIX_.'configuration_lang')
356
                    $langTables[] = $t;
357
358
        Db::getInstance()->Execute('SET @id_lang_default = (SELECT c.`value` FROM `'._DB_PREFIX_.'configuration` c WHERE c.`name` = \'PS_LANG_DEFAULT\' LIMIT 1)');
359
        $return = true;
360
        foreach($langTables as $name)
361
        {
362
            $fields = '';
363
            $columns = Db::getInstance()->ExecuteS('SHOW COLUMNS FROM `'.$name.'`');
364
            foreach($columns as $column)
365
                $fields .= $column['Field'].', ';
366
            $fields = rtrim($fields, ', ');
367
            $identifier = 'id_'.str_replace('_lang', '', str_replace(_DB_PREFIX_, '', $name));
368
369
            $sql = 'INSERT IGNORE INTO `'.$name.'` ('.$fields.') (SELECT ';
370
            foreach($columns as $column) {
371
                if ($identifier != $column['Field'] and $column['Field'] != 'id_lang')
372
                    $sql .= '(SELECT `'.$column['Field'].'` FROM `'.$name.'` tl WHERE tl.`id_lang` = @id_lang_default AND tl.`'.$identifier.'` = `'.str_replace('_lang', '', $name).'`.`'.$identifier.'`), ';
373
                else
374
                    $sql.= '`'.$column['Field'].'`, ';
375
            }
376
            $sql = rtrim($sql, ', ');
377
            $sql .= ' FROM `'._DB_PREFIX_.'lang` CROSS JOIN `'.str_replace('_lang', '', $name).'`) ;';
378
            $return &= Db::getInstance()->Execute(pSQL($sql));
379
        }
380
        return $return;
381
    }
382
383
    public static function recurseDeleteDir($dir)
384
    {
385
        if (!is_dir($dir))
386
            return false;
387
        if ($handle = @opendir($dir))
388
        {
389
            while (false !== ($file = readdir($handle)))
390
                if ($file != '.' && $file != '..')
391
                {
392
                    if (is_dir($dir.'/'.$file))
393
                        self::recurseDeleteDir($dir.'/'.$file);
394
                    elseif (file_exists($dir.'/'.$file))
395
                        @unlink($dir.'/'.$file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
396
                }
397
            closedir($handle);
398
        }
399
        rmdir($dir);
400
    }
401
402
    public function delete()
403
    {
404
        if (empty($this->iso_code))
405
            $this->iso_code = self::getIsoById($this->id);
406
407
        // Database translations deletion
408
        $result = Db::getInstance()->ExecuteS('SHOW TABLES FROM `'._DB_NAME_.'`');
409
        foreach ($result AS $row)
410
            if (preg_match('/_lang/', $row['Tables_in_'._DB_NAME_]))
411
                if (!Db::getInstance()->Execute('DELETE FROM `'.$row['Tables_in_'._DB_NAME_].'` WHERE `id_lang` = '.(int)($this->id)))
412
                    return false;
413
414
        // Delete tags
415
        Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'tag WHERE id_lang = '.(int)($this->id));
416
417
        // Delete search words
418
        Db::getInstance()->Execute('DELETE FROM '._DB_PREFIX_.'search_word WHERE id_lang = '.(int)($this->id));
419
420
        // Files deletion
421
        foreach (self::getFilesList($this->iso_code, _THEME_NAME_, false, false, false, true, true) as $key => $file)
0 ignored issues
show
Bug introduced by
The expression self::getFilesList($this...lse, false, true, true) of type null|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...
422
            if (file_exists($key))
423
                unlink($key);
424
        $modList = scandir(_PS_MODULE_DIR_);
425
        foreach ($modList as $mod)
426
        {
427
            self::recurseDeleteDir(_PS_MODULE_DIR_.$mod.'/mails/'.$this->iso_code);
428
            $files = @scandir(_PS_MODULE_DIR_.$mod.'/mails/');
429
            if (count($files) <= 2)
430
                self::recurseDeleteDir(_PS_MODULE_DIR_.$mod.'/mails/');
431
432
            if (file_exists(_PS_MODULE_DIR_.$mod.'/'.$this->iso_code.'.php'))
433
            {
434
                unlink(_PS_MODULE_DIR_.$mod.'/'.$this->iso_code.'.php');
435
                $files = @scandir(_PS_MODULE_DIR_.$mod);
436
                if (count($files) <= 2)
437
                    self::recurseDeleteDir(_PS_MODULE_DIR_.$mod);
438
            }
439
        }
440
441
        if (file_exists(_PS_MAIL_DIR_.$this->iso_code))
442
            self::recurseDeleteDir(_PS_MAIL_DIR_.$this->iso_code);
443
        if (file_exists(_PS_TRANSLATIONS_DIR_.$this->iso_code))
444
            self::recurseDeleteDir(_PS_TRANSLATIONS_DIR_.$this->iso_code);
445
        if (!parent::delete())
446
            return false;
447
448
        // delete images
449
        $files_copy = array('/en.jpg', '/en-default-thickbox.jpg', '/en-default-home.jpg', '/en-default-large.jpg', '/en-default-medium.jpg', '/en-default-small.jpg', '/en-default-large_scene.jpg');
450
        $tos = array(_PS_CAT_IMG_DIR_, _PS_MANU_IMG_DIR_, _PS_PROD_IMG_DIR_, _PS_SUPP_IMG_DIR_);
451
        foreach($tos AS $to)
452
            foreach($files_copy AS $file)
453
            {
454
                $name = str_replace('/en', ''.$this->iso_code, $file);
455
456
                if (file_exists($to.$name))
457
                    unlink($to.$name);
458
                if (file_exists(dirname(__FILE__).'/../img/l/'.$this->id.'.jpg'))
459
                    unlink(dirname(__FILE__).'/../img/l/'.$this->id.'.jpg');
460
            }
461
462
        // If url_rewrite is not enabled, we don't need to regenerate .htaccess
463
        if (!Configuration::get('PS_REWRITING_SETTINGS'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_REWRITING_SETTINGS') of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
464
            return true;
465
466
        return Tools::generateHtaccess(dirname(__FILE__).'/../.htaccess',
467
                                    (int)(Configuration::get('PS_REWRITING_SETTINGS')),
468
                                    (int)(Configuration::get('PS_HTACCESS_CACHE_CONTROL')),
469
                                    Configuration::get('PS_HTACCESS_SPECIFIC'),
470
                                    (int)Configuration::get('PS_HTACCESS_DISABLE_MULTIVIEWS')
0 ignored issues
show
Documentation introduced by
(int) \Configuration::ge...SS_DISABLE_MULTIVIEWS') is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
471
                                );
472
    }
473
474
475
    public function deleteSelection($selection)
476
    {
477
        if (!is_array($selection) OR !Validate::isTableOrIdentifier($this->identifier) OR !Validate::isTableOrIdentifier($this->table))
478
            die(Tools::displayError());
0 ignored issues
show
Coding Style Compatibility introduced by
The method deleteSelection() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
479
        $result = true;
480
        foreach ($selection AS $id)
481
        {
482
            $this->id = (int)($id);
483
            $result = $result AND $this->delete();
484
        }
485
486
        // If url_rewrite is not enabled, we don't need to regenerate .htaccess
487
        if (!Configuration::get('PS_REWRITING_SETTINGS'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_REWRITING_SETTINGS') of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
488
            return true;
489
490
        Tools::generateHtaccess(dirname(__FILE__).'/../.htaccess',
491
                                (int)(Configuration::get('PS_REWRITING_SETTINGS')),
492
                                (int)(Configuration::get('PS_HTACCESS_CACHE_CONTROL')),
493
                                Configuration::get('PS_HTACCESS_SPECIFIC'),
494
                                (int)Configuration::get('PS_HTACCESS_DISABLE_MULTIVIEWS')
0 ignored issues
show
Documentation introduced by
(int) \Configuration::ge...SS_DISABLE_MULTIVIEWS') is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
495
                            );
496
497
        return $result;
498
    }
499
500
    /**
501
      * Return available languages
502
      *
503
      * @param boolean $active Select only active languages
504
      * @return array Languages
505
      */
506
    public static function getLanguages($active = true)
507
    {
508
        if (!self::$_LANGUAGES)
509
            self::loadLanguages();
510
511
        $languages = array();
512
        foreach (self::$_LANGUAGES AS $language)
513
        {
514
            if ($active AND !$language['active'])
515
                continue;
516
            $languages[] = $language;
517
        }
518
        return $languages;
519
    }
520
521
    public static function getLanguage($id_lang)
522
    {
523
        if (!array_key_exists((int)($id_lang), self::$_LANGUAGES))
524
            return false;
525
        return self::$_LANGUAGES[(int)($id_lang)];
526
    }
527
528
    /**
529
      * Return iso code from id
530
      *
531
      * @param integer $id_lang Language ID
532
      * @return string Iso code
533
      */
534
    public static function getIsoById($id_lang)
535
    {
536
        if (isset(self::$_LANGUAGES[(int)$id_lang]['iso_code']))
537
            return self::$_LANGUAGES[(int)$id_lang]['iso_code'];
538
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Language::getIsoById of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
539
    }
540
541
    /**
542
      * Return id from iso code
543
      *
544
      * @param string $iso_code Iso code
545
      * @return integer Language ID
546
      */
547
    public static function getIdByIso($iso_code)
548
    {
549
        if (!Validate::isLanguageIsoCode($iso_code))
550
            die(Tools::displayError('Fatal error: ISO code is not correct').' '.$iso_code);
0 ignored issues
show
Coding Style Compatibility introduced by
The method getIdByIso() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
551
552
        return Db::getInstance()->getValue('SELECT `id_lang` FROM `'._DB_PREFIX_.'lang` WHERE `iso_code` = \''.pSQL(strtolower($iso_code)).'\'');
553
    }
554
555
    public static function getLanguageCodeByIso($iso_code)
556
    {
557
        if (!Validate::isLanguageIsoCode($iso_code))
558
            die(Tools::displayError('Fatal error: ISO code is not correct').' '.$iso_code);
0 ignored issues
show
Coding Style Compatibility introduced by
The method getLanguageCodeByIso() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
559
560
        return Db::getInstance()->getValue('SELECT `language_code` FROM `'._DB_PREFIX_.'lang` WHERE `iso_code` = \''.pSQL(strtolower($iso_code)).'\'');
561
    }
562
563
    /**
564
      * Return array (id_lang, iso_code)
565
      *
566
      * @param string $iso_code Iso code
0 ignored issues
show
Bug introduced by
There is no parameter named $iso_code. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
567
      * @return array  Language (id_lang, iso_code)
568
      */
569
    public static function getIsoIds($active = true)
570
    {
571
        return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS('SELECT `id_lang`, `iso_code` FROM `'._DB_PREFIX_.'lang` '.($active ? 'WHERE active = 1' : ''));
572
    }
573
574
    public static function copyLanguageData($from, $to)
575
    {
576
        $result = Db::getInstance()->ExecuteS('SHOW TABLES FROM `'._DB_NAME_.'`');
577
        foreach ($result AS $row)
578
            if (preg_match('/_lang/', $row['Tables_in_'._DB_NAME_]) AND $row['Tables_in_'._DB_NAME_] != _DB_PREFIX_.'lang')
579
            {
580
                $result2 = Db::getInstance()->ExecuteS('SELECT * FROM `'.$row['Tables_in_'._DB_NAME_].'` WHERE `id_lang` = '.(int)($from));
581
                if (!sizeof($result2))
582
                    continue;
583
                Db::getInstance()->Execute('DELETE FROM `'.$row['Tables_in_'._DB_NAME_].'` WHERE `id_lang` = '.(int)($to));
584
                $query = 'INSERT INTO `'.$row['Tables_in_'._DB_NAME_].'` VALUES ';
585
                foreach ($result2 AS $row2)
586
                {
587
                    $query .= '(';
588
                    $row2['id_lang'] = $to;
589
                    foreach ($row2 AS $field)
590
                        $query .= '\''.pSQL($field, true).'\',';
591
                    $query = rtrim($query, ',').'),';
592
                }
593
                $query = rtrim($query, ',');
594
                Db::getInstance()->Execute($query);
595
            }
596
        return true;
597
    }
598
599
    /**
600
      * Load all languages in memory for caching
601
      */
602
    public static function loadLanguages()
603
    {
604
        self::$_LANGUAGES = array();
605
        $result = Db::getInstance()->ExecuteS('SELECT * FROM `'._DB_PREFIX_.'lang`');
606
        foreach ($result AS $row)
607
            self::$_LANGUAGES[(int)$row['id_lang']] = $row;
608
    }
609
610
    public function update($nullValues = false)
611
    {
612
613
        
614
        if (!parent::update($nullValues))
615
            return false;
616
617
        // If url_rewrite is not enabled, we don't need to regenerate .htaccess
618
        if (!Configuration::get('PS_REWRITING_SETTINGS'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_REWRITING_SETTINGS') of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
619
            return true;
620
621
        return Tools::generateHtaccess(dirname(__FILE__).'/../.htaccess',
622
                            (int)(Configuration::get('PS_REWRITING_SETTINGS')),
623
                            (int)(Configuration::get('PS_HTACCESS_CACHE_CONTROL')),
624
                            Configuration::get('PS_HTACCESS_SPECIFIC'),
625
                            (int)Configuration::get('PS_HTACCESS_DISABLE_MULTIVIEWS')
0 ignored issues
show
Documentation introduced by
(int) \Configuration::ge...SS_DISABLE_MULTIVIEWS') is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
626
                            );
627
    }
628
629
    public static function checkAndAddLanguage($iso_code)
630
    {
631
        if (Language::getIdByIso($iso_code))
632
            return true;
633
        else
634
        {
635
            if (@fsockopen('www.prestashop.com', 80))
636
            {
637
                $lang = new Language();
638
                $lang->iso_code = $iso_code;
639
                $lang->active = true;
640
641
                if ($lang_pack = Tools::jsonDecode(Tools::file_get_contents('http://www.prestashop.com/download/lang_packs/get_language_pack.php?version='._PS_VERSION_.'&iso_lang='.$iso_code)))
642
                {
643
                    if (isset($lang_pack->name)
644
                    && isset($lang_pack->version)
645
                    && isset($lang_pack->iso_code))
646
                        $lang->name = $lang_pack->name;
647
                }
648
                if (!$lang->name OR !$lang->add())
649
                    return false;
650
                $insert_id = (int)($lang->id);
651
652
                if ($lang_pack)
0 ignored issues
show
Bug Best Practice introduced by
The expression $lang_pack of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
653
                {
654
                    $flag = Tools::file_get_contents('http://www.prestashop.com/download/lang_packs/flags/jpeg/'.$iso_code.'.jpg');
655
                    if ($flag != NULL && !preg_match('/<body>/', $flag))
656
                    {
657
                        $file = fopen(dirname(__FILE__).'/../img/l/'.$insert_id.'.jpg', 'w');
658
                        if ($file)
659
                        {
660
                            fwrite($file, $flag);
661
                            fclose($file);
662
                        }
663
                        else
664
                            self::_copyNoneFlag($insert_id);
665
                    }
666
                    else
667
                        self::_copyNoneFlag($insert_id);
668
                }
669
                else
670
                    self::_copyNoneFlag($insert_id);
671
672
                $files_copy = array('/en.jpg', '/en-default-thickbox.jpg', '/en-default-home.jpg', '/en-default-large.jpg', '/en-default-medium.jpg', '/en-default-small.jpg', '/en-default-large_scene.jpg');
673
                $tos = array(_PS_CAT_IMG_DIR_, _PS_MANU_IMG_DIR_, _PS_PROD_IMG_DIR_, _PS_SUPP_IMG_DIR_);
674
                foreach($tos AS $to)
675
                    foreach($files_copy AS $file)
676
                    {
677
                        $name = str_replace('/en', '/'.$iso_code, $file);
678
                        copy(dirname(__FILE__).'/../img/l'.$file, $to.$name);
679
                    }
680
                return true;
681
            }
682
            else
683
                return false;
684
        }
685
    }
686
687
    protected static function _copyNoneFlag($id)
688
    {
689
        return copy(dirname(__FILE__).'/../img/l/none.jpg', dirname(__FILE__).'/../img/l/'.$id.'.jpg');
690
    }
691
692
    private static $_cache_language_installation = null;
693
    public static function isInstalled($iso_code)
694
    {
695
        if (self::$_cache_language_installation === null)
696
        {
697
            self::$_cache_language_installation = array();
698
            $result = Db::getInstance()->ExecuteS('SELECT `id_lang`, `iso_code` FROM `'._DB_PREFIX_.'lang`');
699
            foreach ($result as $row)
700
                self::$_cache_language_installation[$row['iso_code']] = $row['id_lang'];
701
        }
702
        return (isset(self::$_cache_language_installation[$iso_code]) ? self::$_cache_language_installation[$iso_code] : false);
703
    }
704
705
    public static function countActiveLanguages()
706
    {
707
        if (!self::$countActiveLanguages)
708
            self::$countActiveLanguages = Db::getInstance()->getValue('SELECT COUNT(*) FROM `'._DB_PREFIX_.'lang` WHERE `active` = 1');
709
        return self::$countActiveLanguages;
710
    }
711
}
712
713