Tools::generateHtaccess()   F
last analyzed

Complexity

Conditions 24
Paths 1960

Size

Total Lines 163

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 24
nc 1960
nop 5
dl 0
loc 163
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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: 14204 $
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 Tools
29
{
30
	protected static $file_exists_cache = array();
31
	protected static $_forceCompile;
32
	protected static $_caching;
33
34
	/**
35
	* Random password generator
36
	*
37
	* @param integer $length Desired length (optional)
38
	* @return string Password
39
	*/
40
	public static function passwdGen($length = 8)
41
	{
42
		$str = 'abcdefghijkmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
43
		for ($i = 0, $passwd = ''; $i < $length; $i++)
44
			$passwd .= self::substr($str, mt_rand(0, self::strlen($str) - 1), 1);
0 ignored issues
show
Documentation introduced by
1 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...
45
		return $passwd;
46
	}
47
48
	/**
49
	* Redirect user to another page
50
	*
51
	* @param string $url Desired URL
52
	* @param string $baseUri Base URI (optional)
53
	*/
54
	public static function redirect($url, $baseUri = __PS_BASE_URI__)
0 ignored issues
show
Coding Style introduced by
redirect uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
55
	{
56
		if (strpos($url, 'http://') === FALSE && strpos($url, 'https://') === FALSE)
57
		{
58
			global $link;
59
			if (strpos($url, $baseUri) !== FALSE && strpos($url, $baseUri) == 0)
60
				$url = substr($url, strlen($baseUri));
61
			$explode = explode('?', $url, 2);
62
			// don't use ssl if url is home page
63
			// used when logout for example
64
			$useSSL = !empty($url);
65
			$url = $link->getPageLink($explode[0], $useSSL);
66
			if (isset($explode[1]))
67
				$url .= '?'.$explode[1];
68
			$baseUri = '';
69
		}
70
71
		if (isset($_SERVER['HTTP_REFERER']) AND ($url == $_SERVER['HTTP_REFERER']))
72
			header('Location: '.$_SERVER['HTTP_REFERER']);
73
		else
74
			header('Location: '.$baseUri.$url);
75
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirect() 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...
76
	}
77
78
	/**
79
	* Redirect url wich allready PS_BASE_URI
80
	*
81
	* @param string $url Desired URL
82
	*/
83
	public static function redirectLink($url)
84
	{
85
		if (!preg_match('@^https?://@i', $url))
86
		{
87
			global $link;
88
			if (strpos($url, __PS_BASE_URI__) !== FALSE && strpos($url, __PS_BASE_URI__) == 0)
89
				$url = substr($url, strlen(__PS_BASE_URI__));
90
			$explode = explode('?', $url, 2);
91
			$url = $link->getPageLink($explode[0]);
92
			if (isset($explode[1]))
93
				$url .= '?'.$explode[1];
94
		}
95
96
		header('Location: '.$url);
97
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectLink() 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...
98
	}
99
100
	/**
101
	* Redirect user to another admin page
102
	*
103
	* @param string $url Desired URL
104
	*/
105
	public static function redirectAdmin($url)
106
	{
107
		header('Location: '.$url);
108
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method redirectAdmin() 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...
109
	}
110
111
	/**
112
	 * getProtocol return the set protocol according to configuration (http[s])
113
	 * @param Boolean true if require ssl
114
	 * @return String (http|https)
115
	 */
116
	public static function getProtocol($use_ssl = null)
117
	{
118
		return (!is_null($use_ssl) && $use_ssl ? 'https://' : 'http://');
119
	}
120
121
	/**
122
	 * getHttpHost return the <b>current</b> host used, with the protocol (http or https) if $http is true
123
	 * This function should not be used to choose http or https domain name.
124
	 * Use Tools::getShopDomain() or Tools::getShopDomainSsl instead
125
	 *
126
	 * @param boolean $http
127
	 * @param boolean $entities
128
	 * @return string host
129
	 */
130
	public static function getHttpHost($http = false, $entities = false)
0 ignored issues
show
Coding Style introduced by
getHttpHost uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
131
	{
132
		$host = (isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']);
133
		if ($entities)
134
			$host = htmlspecialchars($host, ENT_COMPAT, 'UTF-8');
135
		if ($http)
136
			$host = (Configuration::get('PS_SSL_ENABLED') ? 'https://' : 'http://').$host;
137
		return $host;
138
	}
139
140
	/**
141
	 * getShopDomain returns domain name according to configuration and ignoring ssl
142
	 *
143
	 * @param boolean $http if true, return domain name with protocol
144
	 * @param boolean $entities if true,
145
	 * @return string domain
146
	 */
147
	public static function getShopDomain($http = false, $entities = false)
148
	{
149
		if (!($domain = Configuration::get('PS_SHOP_DOMAIN')))
150
			$domain = self::getHttpHost();
151
		if ($entities)
152
			$domain = htmlspecialchars($domain, ENT_COMPAT, 'UTF-8');
153
		if ($http)
154
			$domain = 'http://'.$domain;
155
		return $domain;
156
	}
157
158
	/**
159
	 * getShopDomainSsl returns domain name according to configuration and depending on ssl activation
160
	 *
161
	 * @param boolean $http if true, return domain name with protocol
162
	 * @param boolean $entities if true,
163
	 * @return string domain
164
	 */
165
	public static function getShopDomainSsl($http = false, $entities = false)
166
	{
167
		if (!($domain = Configuration::get('PS_SHOP_DOMAIN_SSL')))
168
			$domain = self::getHttpHost();
169
		if ($entities)
170
			$domain = htmlspecialchars($domain, ENT_COMPAT, 'UTF-8');
171
		if ($http)
172
			$domain = (Configuration::get('PS_SSL_ENABLED') ? 'https://' : 'http://').$domain;
173
		return $domain;
174
	}
175
176
	/**
177
	* Get the server variable SERVER_NAME
178
	*
179
	* @return string server name
180
	*/
181
	static function getServerName()
0 ignored issues
show
Coding Style introduced by
getServerName uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
182
	{
183
		if (isset($_SERVER['HTTP_X_FORWARDED_SERVER']) AND $_SERVER['HTTP_X_FORWARDED_SERVER'])
184
			return $_SERVER['HTTP_X_FORWARDED_SERVER'];
185
		return $_SERVER['SERVER_NAME'];
186
	}
187
188
	/**
189
	* Get the server variable REMOTE_ADDR, or the first ip of HTTP_X_FORWARDED_FOR (when using proxy)
190
	*
191
	* @return string $remote_addr ip of client
192
	*/
193
	static function getRemoteAddr()
0 ignored issues
show
Coding Style introduced by
getRemoteAddr uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
194
	{
195
		// This condition is necessary when using CDN, don't remove it.
196
		if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND $_SERVER['HTTP_X_FORWARDED_FOR'] AND (!isset($_SERVER['REMOTE_ADDR']) OR preg_match('/^127\..*/i', trim($_SERVER['REMOTE_ADDR'])) OR preg_match('/^172\.16.*/i', trim($_SERVER['REMOTE_ADDR'])) OR preg_match('/^192\.168\.*/i', trim($_SERVER['REMOTE_ADDR'])) OR preg_match('/^10\..*/i', trim($_SERVER['REMOTE_ADDR']))))
197
		{
198
			if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ','))
199
			{
200
				$ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
201
				return $ips[0];
202
			}
203
			else
204
				return $_SERVER['HTTP_X_FORWARDED_FOR'];
205
		}
206
		return $_SERVER['REMOTE_ADDR'];
207
	}
208
209
	/**
210
	* Check if the current page use SSL connection on not
211
	*
212
	* @return bool uses SSL
213
	*/
214
	public static function usingSecureMode()
0 ignored issues
show
Coding Style introduced by
usingSecureMode uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
215
	{
216
		if (isset($_SERVER['HTTPS']))
217
			return ($_SERVER['HTTPS'] == 1 || strtolower($_SERVER['HTTPS']) == 'on');
218
		// $_SERVER['SSL'] exists only in some specific configuration
219
		if (isset($_SERVER['SSL']))
220
			return ($_SERVER['SSL'] == 1 || strtolower($_SERVER['SSL']) == 'on');
221
222
		return false;
223
	}
224
225
	/**
226
	* Get the current url prefix protocol (https/http)
227
	*
228
	* @return string protocol
229
	*/
230
	public static function getCurrentUrlProtocolPrefix()
231
	{
232
		if (self::usingSecureMode())
233
			return 'https://';
234
		else
235
			return 'http://';
236
	}
237
238
	/**
239
	* Secure an URL referrer
240
	*
241
	* @param string $referrer URL referrer
242
	* @return secured referrer
243
	*/
244
	public static function secureReferrer($referrer)
245
	{
246
		if (preg_match('/^http[s]?:\/\/'.self::getServerName().'(:'._PS_SSL_PORT_.')?\/.*$/Ui', $referrer))
247
			return $referrer;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $referrer; (string) is incompatible with the return type documented by Tools::secureReferrer of type secured.

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...
248
		return __PS_BASE_URI__;
249
	}
250
251
	/**
252
	* Get a value from $_POST / $_GET
253
	* if unavailable, take a default value
254
	*
255
	* @param string $key Value key
256
	* @param mixed $defaultValue (optional)
257
	* @return mixed Value
258
	*/
259
	public static function getValue($key, $defaultValue = false)
0 ignored issues
show
Coding Style introduced by
getValue uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
getValue uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
260
	{
261
	 	if (!isset($key) OR empty($key) OR !is_string($key))
262
			return false;
263
		$ret = (isset($_POST[$key]) ? $_POST[$key] : (isset($_GET[$key]) ? $_GET[$key] : $defaultValue));
264
265
		if (is_string($ret) === true)
266
			$ret = stripslashes(urldecode(preg_replace('/((\%5C0+)|(\%00+))/i', '', urlencode($ret))));
267
		elseif (is_array($ret))
268
            $ret = Tools::getArrayValue($ret);
269
270
		return $ret;
271
	}
272
273
    /**
274
     * Escape values contained in an array
275
     *
276
     * @param array $array Value array
277
     * @return mixed Value
278
     */
279
    public static function getArrayValue($array)
280
    {
281
        foreach ($array as &$row)
282
        {
283
            if (is_array($row))
284
                $row = Tools::getArrayValue($row);
285
            else
286
                $row = stripslashes(urldecode(preg_replace('/((\%5C0+)|(\%00+))/i', '', urlencode($row))));
287
        }
288
289
        return $array;
290
    }
291
292
	public static function getIsset($key)
0 ignored issues
show
Coding Style introduced by
getIsset uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
getIsset uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
293
	{
294
	 	if (!isset($key) OR empty($key) OR !is_string($key))
295
			return false;
296
	 	return isset($_POST[$key]) ? true : (isset($_GET[$key]) ? true : false);
297
	}
298
299
	/**
300
	* Change language in cookie while clicking on a flag
301
	*
302
	* @return string iso code
303
	*/
304
	public static function setCookieLanguage()
0 ignored issues
show
Coding Style introduced by
setCookieLanguage uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
305
	{
306
		global $cookie;
307
308
		/* If language does not exist or is disabled, erase it */
309
		if ($cookie->id_lang)
310
		{
311
			$lang = new Language((int)$cookie->id_lang);
312
			if (!Validate::isLoadedObject($lang) OR !$lang->active)
313
				$cookie->id_lang = NULL;
314
		}
315
316
		/* Automatically detect language if not already defined */
317
		if (!$cookie->id_lang AND isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
318
		{
319
			$array = explode(',', self::strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
320
			if (self::strlen($array[0]) > 2)
321
			{
322
				$tab = explode('-', $array[0]);
323
				$string = $tab[0];
324
			}
325
			else
326
				$string = $array[0];
327
			if (Validate::isLanguageIsoCode($string))
328
			{
329
				$lang = new Language((int)(Language::getIdByIso($string)));
330
				if (Validate::isLoadedObject($lang) AND $lang->active)
331
					$cookie->id_lang = (int)($lang->id);
332
			}
333
		}
334
335
		/* If language file not present, you must use default language file */
336
		if (!$cookie->id_lang OR !Validate::isUnsignedId($cookie->id_lang))
337
			$cookie->id_lang = (int)(Configuration::get('PS_LANG_DEFAULT'));
338
339
		$iso = Language::getIsoById((int)$cookie->id_lang);
340
		@include_once(_PS_THEME_DIR_.'lang/'.$iso.'.php');
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...
341
342
		return $iso;
343
	}
344
345
	/**
346
	 * Set cookie id_lang
347
	 */
348
	public static function switchLanguage()
349
	{
350
		global $cookie;
351
352
		if ($id_lang = (int)(self::getValue('id_lang')) AND Validate::isUnsignedId($id_lang))
353
			$cookie->id_lang = $id_lang;
354
	}
355
356
	/**
357
	 * Set cookie currency from POST or default currency
358
	 *
359
	 * @return Currency object
360
	 */
361
	public static function setCurrency()
0 ignored issues
show
Coding Style introduced by
setCurrency uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
362
	{
363
		global $cookie;
364
365
		if (self::isSubmit('SubmitCurrency'))
366
			if (isset($_POST['id_currency']) AND is_numeric($_POST['id_currency']))
367
			{
368
				$currency = Currency::getCurrencyInstance((int)($_POST['id_currency']));
369
				if (is_object($currency) AND $currency->id AND !$currency->deleted)
370
					$cookie->id_currency = (int)($currency->id);
371
			}
372
373
		if ((int)$cookie->id_currency)
374
		{
375
			$currency = Currency::getCurrencyInstance((int)$cookie->id_currency);
376
			if (is_object($currency) AND (int)$currency->id AND (int)$currency->deleted != 1 AND $currency->active)
377
				return $currency;
378
		}
379
		$currency = Currency::getCurrencyInstance((int)(Configuration::get('PS_CURRENCY_DEFAULT')));
380
		if (is_object($currency) AND $currency->id)
381
			$cookie->id_currency = (int)($currency->id);
382
		return $currency;
383
	}
384
385
	/**
386
	* Return price with currency sign for a given product
387
	*
388
	* @param float $price Product price
389
	* @param object $currency Current currency (object, id_currency, NULL => getCurrent())
390
	* @return string Price correctly formated (sign, decimal separator...)
391
	*/
392
	public static function displayPrice($price, $currency = NULL, $no_utf8 = false)
393
	{
394
		if ($currency === NULL)
395
			$currency = Currency::getCurrent();
396
		/* if you modified this function, don't forget to modify the Javascript function formatCurrency (in tools.js) */
397
		if (is_int($currency))
398
			$currency = Currency::getCurrencyInstance((int)$currency);
399
		
400
		if (is_array($currency))
401
		{
402
			$c_char = $currency['sign'];
403
			$c_format = $currency['format'];
404
			$c_decimals = (int)$currency['decimals'] * _PS_PRICE_DISPLAY_PRECISION_;
405
			$c_blank = $currency['blank'];
406
		}
407
		elseif (is_object($currency))
408
		{
409
			$c_char = $currency->sign;
410
			$c_format = $currency->format;
411
			$c_decimals = (int)$currency->decimals * _PS_PRICE_DISPLAY_PRECISION_;
412
			$c_blank = $currency->blank;
413
		}
414
		else
415
			return false;
416
		
417
		$blank = ($c_blank ? ' ' : '');
418
		$ret = 0;
419
		if (($isNegative = ($price < 0)))
420
			$price *= -1;
421
		$price = self::ps_round($price, $c_decimals);
422
		switch ($c_format)
423
	 	{
424
	 	 	/* X 0,000.00 */
425
	 	 	case 1:
426
				$ret = $c_char.$blank.number_format($price, $c_decimals, '.', ',');
427
				break;
428
			/* 0 000,00 X*/
429
			case 2:
430
				$ret = number_format($price, $c_decimals, ',', ' ').$blank.$c_char;
431
				break;
432
			/* X 0.000,00 */
433
			case 3:
434
				$ret = $c_char.$blank.number_format($price, $c_decimals, ',', '.');
435
				break;
436
			/* 0,000.00 X */
437
			case 4:
438
				$ret = number_format($price, $c_decimals, '.', ',').$blank.$c_char;
439
				break;
440
		}
441
		if ($isNegative)
442
			$ret = '-'.$ret;
443
		if ($no_utf8)
444
			return str_replace('€', chr(128), $ret);
445
		return $ret;
446
	}
447
448
	public static function displayPriceSmarty($params, &$smarty)
0 ignored issues
show
Unused Code introduced by
The parameter $smarty is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
449
	{
450
		if (array_key_exists('currency', $params))
451
		{
452
			$currency = Currency::getCurrencyInstance((int)($params['currency']));
453
			if (Validate::isLoadedObject($currency))
454
				return self::displayPrice($params['price'], $currency, false);
455
		}
456
		return self::displayPrice($params['price']);
457
	}
458
459
	/**
460
	* Return price converted
461
	*
462
	* @param float $price Product price
463
	* @param object $currency Current currency object
464
	* @param boolean $to_currency convert to currency or from currency to default currency
465
	*/
466
	public static function convertPrice($price, $currency = NULL, $to_currency = true)
467
	{
468
		if ($currency === NULL)
469
			$currency = Currency::getCurrent();
470
		elseif (is_numeric($currency))
471
			$currency = Currency::getCurrencyInstance($currency);
472
473
		$c_id = (is_array($currency) ? $currency['id_currency'] : $currency->id);
474
		$c_rate = (is_array($currency) ? $currency['conversion_rate'] : $currency->conversion_rate);
475
476
		if ($c_id != (int)(Configuration::get('PS_CURRENCY_DEFAULT')))
477
		{
478
			if ($to_currency)
479
				$price *= $c_rate;
480
			else
481
				$price /= $c_rate;
482
		}
483
484
		return $price;
485
	}
486
487
488
489
	/**
490
	* Display date regarding to language preferences
491
	*
492
	* @param array $params Date, format...
493
	* @param object $smarty Smarty object for language preferences
494
	* @return string Date
495
	*/
496
	public static function dateFormat($params, &$smarty)
497
	{
498
		return self::displayDate($params['date'], $smarty->ps_language->id, (isset($params['full']) ? $params['full'] : false));
499
	}
500
501
	/**
502
	* Display date regarding to language preferences
503
	*
504
	* @param string $date Date to display format UNIX
505
	* @param integer $id_lang Language id
506
	* @param boolean $full With time or not (optional)
507
	* @return string Date
508
	*/
509
	public static function displayDate($date, $id_lang, $full = false, $separator = '-')
0 ignored issues
show
Unused Code introduced by
The parameter $separator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
510
	{
511
	 	if (!$date OR !($time = strtotime($date)))
512
	 		return $date;
513
		if (!Validate::isDate($date) OR !Validate::isBool($full))
514
			die (self::displayError('Invalid date'));
0 ignored issues
show
Coding Style Compatibility introduced by
The method displayDate() 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...
515
516
		$language = Language::getLanguage((int)$id_lang);
517
		return date($full ? $language['date_format_full'] : $language['date_format_lite'], $time);
518
	}
519
520
	/**
521
	* Sanitize a string
522
	*
523
	* @param string $string String to sanitize
524
	* @param boolean $full String contains HTML or not (optional)
0 ignored issues
show
Bug introduced by
There is no parameter named $full. 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...
525
	* @return string Sanitized string
526
	*/
527
	public static function safeOutput($string, $html = false)
528
	{
529
	 	if (!$html)
530
			$string = strip_tags($string);
531
		return @Tools::htmlentitiesUTF8($string, ENT_QUOTES);
532
	}
533
534
	public static function htmlentitiesUTF8($string, $type = ENT_QUOTES)
535
	{
536
		if (is_array($string))
537
			return array_map(array('Tools', 'htmlentitiesUTF8'), $string);
538
		return htmlentities($string, $type, 'utf-8');
539
	}
540
541
	public static function htmlentitiesDecodeUTF8($string)
542
	{
543
		if (is_array($string))
544
			return array_map(array('Tools', 'htmlentitiesDecodeUTF8'), $string);
545
		return html_entity_decode($string, ENT_QUOTES, 'utf-8');
546
	}
547
548
	public static function safePostVars()
0 ignored issues
show
Coding Style introduced by
safePostVars uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
549
	{
550
		$_POST = array_map(array('Tools', 'htmlentitiesUTF8'), $_POST);
551
	}
552
553
	/**
554
	* Delete directory and subdirectories
555
	*
556
	* @param string $dirname Directory name
557
	*/
558
	public static function deleteDirectory($dirname, $delete_self = true)
559
	{
560
		$dirname = rtrim($dirname, '/').'/';
561
		$files = scandir($dirname);
562
		foreach ($files as $file)
563
			if ($file != '.' AND $file != '..')
564
			{
565
				if (is_dir($dirname.$file))
566
					self::deleteDirectory($dirname.$file, true);
567
				elseif (file_exists($dirname.$file))
568
					unlink($dirname.$file);
569
			}
570
		if ($delete_self)
571
			rmdir($dirname);
572
	}
573
574
	/**
575
	* Display an error according to an error code
576
	*
577
	* @param string $string Error message
578
	* @param boolean $htmlentities By default at true for parsing error message with htmlentities
579
	*/
580
	public static function displayError($string = 'Fatal error', $htmlentities = true)
581
	{
582
		global $_ERRORS, $cookie;
583
584
		$iso = strtolower(Language::getIsoById((is_object($cookie) AND $cookie->id_lang) ? (int)$cookie->id_lang : (int)Configuration::get('PS_LANG_DEFAULT')));
585
		@include_once(_PS_TRANSLATIONS_DIR_.$iso.'/errors.php');
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...
586
587
		if (defined('_PS_MODE_DEV_') AND _PS_MODE_DEV_ AND $string == 'Fatal error')
588
			return ('<pre>'.print_r(debug_backtrace(), true).'</pre>');
589
		if (!is_array($_ERRORS))
590
			return str_replace('"', '&quot;', $string);
591
		$key = md5(str_replace('\'', '\\\'', $string));
592
		$str = (isset($_ERRORS) AND is_array($_ERRORS) AND key_exists($key, $_ERRORS)) ? ($htmlentities ? htmlentities($_ERRORS[$key], ENT_COMPAT, 'UTF-8') : $_ERRORS[$key]) : $string;
593
		return str_replace('"', '&quot;', stripslashes($str));
594
	}
595
596
	/**
597
	 * Display an error with detailed object
598
	 *
599
	 * @param mixed $object
600
	 * @param boolean $kill
601
	 * @return $object if $kill = false;
0 ignored issues
show
Documentation introduced by
The doc-type $object could not be parsed: Unknown type name "$object" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
602
	 */
603
	public static function dieObject($object, $kill = true)
604
	{
605
		echo '<pre style="text-align: left;">';
606
		print_r($object);
607
		echo '</pre><br />';
608
		if ($kill)
609
			die('END');
0 ignored issues
show
Coding Style Compatibility introduced by
The method dieObject() 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...
610
		return $object;
611
	}
612
613
	/**
614
	* ALIAS OF dieObject() - Display an error with detailed object
615
	*
616
	* @param object $object Object to display
617
	*/
618
	public static function d($object, $kill = true)
619
	{
620
		return (self::dieObject($object, $kill));
621
	}
622
623
	/**
624
	* ALIAS OF dieObject() - Display an error with detailed object but don't stop the execution
625
	*
626
	* @param object $object Object to display
627
	*/
628
	public static function p($object)
629
	{
630
		return (self::dieObject($object, false));
631
	}
632
633
	/**
634
	* Check if submit has been posted
635
	*
636
	* @param string $submit submit name
637
	*/
638
	public static function isSubmit($submit)
0 ignored issues
show
Coding Style introduced by
isSubmit uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
isSubmit uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
639
	{
640
		return (
641
			isset($_POST[$submit]) OR isset($_POST[$submit.'_x']) OR isset($_POST[$submit.'_y'])
642
			OR isset($_GET[$submit]) OR isset($_GET[$submit.'_x']) OR isset($_GET[$submit.'_y'])
643
		);
644
	}
645
646
	/**
647
	* Get meta tages for a given page
648
	*
649
	* @param integer $id_lang Language id
650
	* @return array Meta tags
651
	*/
652
	public static function getMetaTags($id_lang, $page_name, $title = '')
653
	{
654
		global $maintenance;
655
656
		if (!(isset($maintenance) AND (!in_array(self::getRemoteAddr(), explode(',', Configuration::get('PS_MAINTENANCE_IP'))))))
657
		{
658
		 	/* Products specifics meta tags */
659
			if ($id_product = self::getValue('id_product'))
660
			{
661
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
662
				SELECT `name`, `meta_title`, `meta_description`, `meta_keywords`, `description_short`
663
				FROM `'._DB_PREFIX_.'product` p
664
				LEFT JOIN `'._DB_PREFIX_.'product_lang` pl ON (pl.`id_product` = p.`id_product`)
665
				WHERE pl.id_lang = '.(int)($id_lang).' AND pl.id_product = '.(int)($id_product).' AND p.active = 1');
666
				if ($row)
667
				{
668
					if (empty($row['meta_description']))
669
						$row['meta_description'] = strip_tags($row['description_short']);
670
					return self::completeMetaTags($row, $row['name']);
671
				}
672
			}
673
674
			/* Categories specifics meta tags */
675
			elseif ($id_category = self::getValue('id_category'))
676
			{
677
				if (!empty($title))
678
					$title = ' - '.$title;
679
				$page_number = (int)self::getValue('p');
680
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
681
				SELECT `name`, `meta_title`, `meta_description`, `meta_keywords`, `description`
682
				FROM `'._DB_PREFIX_.'category_lang`
683
				WHERE id_lang = '.(int)($id_lang).' AND id_category = '.(int)($id_category));
684
				if ($row)
685
				{
686
					if (empty($row['meta_description']))
687
						$row['meta_description'] = strip_tags($row['description']);
688
689
					// Paginate title
690
					if (!empty($row['meta_title']))
691
						$row['meta_title'] = $title.$row['meta_title'].(!empty($page_number) ? ' ('.$page_number.')' : '').' - '.Configuration::get('PS_SHOP_NAME');
692
					else
693
						$row['meta_title'] = $row['name'].(!empty($page_number) ? ' ('.$page_number.')' : '').' - '.Configuration::get('PS_SHOP_NAME');
694
					
695
					if (!empty($title))
696
						$row['meta_title'] = $title.(!empty($page_number) ? ' ('.$page_number.')' : '').' - '.Configuration::get('PS_SHOP_NAME');
697
						
698
					return self::completeMetaTags($row, $row['name']);
699
				}
700
			}
701
702
			/* Manufacturers specifics meta tags */
703
			elseif ($id_manufacturer = self::getValue('id_manufacturer'))
704
			{
705
				$page_number = (int)self::getValue('p');
706
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
707
				SELECT `name`, `meta_title`, `meta_description`, `meta_keywords`
708
				FROM `'._DB_PREFIX_.'manufacturer_lang` ml
709
				LEFT JOIN `'._DB_PREFIX_.'manufacturer` m ON (ml.`id_manufacturer` = m.`id_manufacturer`)
710
				WHERE ml.id_lang = '.(int)($id_lang).' AND ml.id_manufacturer = '.(int)($id_manufacturer));
711
				if ($row)
712
				{
713
					if (empty($row['meta_description']))
714
						$row['meta_description'] = strip_tags($row['meta_description']);
715
					$row['meta_title'] .= $row['name'] . (!empty($page_number) ? ' ('.$page_number.')' : '');
716
					$row['meta_title'] .= ' - '.Configuration::get('PS_SHOP_NAME');
717
					return self::completeMetaTags($row, $row['meta_title']);
718
				}
719
			}
720
721
			/* Suppliers specifics meta tags */
722
			elseif ($id_supplier = self::getValue('id_supplier'))
723
			{
724
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
725
				SELECT `name`, `meta_title`, `meta_description`, `meta_keywords`
726
				FROM `'._DB_PREFIX_.'supplier_lang` sl
727
				LEFT JOIN `'._DB_PREFIX_.'supplier` s ON (sl.`id_supplier` = s.`id_supplier`)
728
				WHERE sl.id_lang = '.(int)($id_lang).' AND sl.id_supplier = '.(int)($id_supplier));
729
730
				if ($row)
731
				{
732
					if (empty($row['meta_description']))
733
						$row['meta_description'] = strip_tags($row['meta_description']);
734
					if (!empty($row['meta_title']))
735
						$row['meta_title'] = $row['meta_title'].' - '.Configuration::get('PS_SHOP_NAME');
736
					return self::completeMetaTags($row, $row['name']);
737
				}
738
			}
739
740
			/* CMS specifics meta tags */
741
			elseif ($id_cms = self::getValue('id_cms'))
742
			{
743
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
744
				SELECT `meta_title`, `meta_description`, `meta_keywords`
745
				FROM `'._DB_PREFIX_.'cms_lang`
746
				WHERE id_lang = '.(int)($id_lang).' AND id_cms = '.(int)($id_cms));
747
				if ($row)
748
				{
749
					$row['meta_title'] = $row['meta_title'].' - '.Configuration::get('PS_SHOP_NAME');
750
					return self::completeMetaTags($row, $row['meta_title']);
751
				}
752
			}
753
754
			/* CMS category specifics meta tags */
755
			elseif ($id_cms = self::getValue('id_cms_category'))
756
			{
757
				$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('
758
				SELECT `meta_title`, `meta_description`, `meta_keywords`
759
				FROM `'._DB_PREFIX_.'cms_category_lang`
760
				WHERE id_lang = '.(int)($id_lang).' AND id_cms_category = '.(int)($id_cms));
761
				if ($row)
762
				{
763
					$row['meta_title'] = $row['meta_title'].' - '.Configuration::get('PS_SHOP_NAME');
764
					return self::completeMetaTags($row, $row['meta_title']);
765
				}
766
			}
767
		}
768
769
		/* Default meta tags */
770
		return self::getHomeMetaTags($id_lang, $page_name);
771
	}
772
773
	/**
774
	* Get meta tags for a given page
775
	*
776
	* @param integer $id_lang Language id
777
	* @return array Meta tags
778
	*/
779
	public static function getHomeMetaTags($id_lang, $page_name)
780
	{
781
		/* Metas-tags */
782
		$metas = Meta::getMetaByPage($page_name, $id_lang);
783
		$ret['meta_title'] = (isset($metas['title']) AND $metas['title']) ? $metas['title'].' - '.Configuration::get('PS_SHOP_NAME') : Configuration::get('PS_SHOP_NAME');
0 ignored issues
show
Coding Style Comprehensibility introduced by
$ret was never initialized. Although not strictly required by PHP, it is generally a good practice to add $ret = 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...
784
		$ret['meta_description'] = (isset($metas['description']) AND $metas['description']) ? $metas['description'] : '';
785
		$ret['meta_keywords'] = (isset($metas['keywords']) AND $metas['keywords']) ? $metas['keywords'] :  '';
786
		return $ret;
787
	}
788
789
790
	public static function completeMetaTags($metaTags, $defaultValue)
791
	{
792
		global $cookie;
793
794
		if (empty($metaTags['meta_title']))
795
			$metaTags['meta_title'] = $defaultValue.' - '.Configuration::get('PS_SHOP_NAME');
796
		if (empty($metaTags['meta_description']))
797
			$metaTags['meta_description'] = Configuration::get('PS_META_DESCRIPTION', (int)($cookie->id_lang)) ? Configuration::get('PS_META_DESCRIPTION', (int)($cookie->id_lang)) : '';
798
		if (empty($metaTags['meta_keywords']))
799
			$metaTags['meta_keywords'] = Configuration::get('PS_META_KEYWORDS', (int)($cookie->id_lang)) ? Configuration::get('PS_META_KEYWORDS', (int)($cookie->id_lang)) : '';
800
		return $metaTags;
801
	}
802
803
	/**
804
	* Encrypt password
805
	*
806
	* @param object $object Object to display
0 ignored issues
show
Bug introduced by
There is no parameter named $object. 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...
807
	*/
808
	public static function encrypt($passwd)
809
	{
810
		return md5(pSQL(_COOKIE_KEY_.$passwd));
811
	}
812
813
	/**
814
	* Get token to prevent CSRF
815
	*
816
	* @param string $token token to encrypt
0 ignored issues
show
Bug introduced by
There is no parameter named $token. 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...
817
	*/
818
	public static function getToken($page = true)
0 ignored issues
show
Coding Style introduced by
getToken uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
819
	{
820
		global $cookie;
821
		if ($page === true)
822
			return (self::encrypt($cookie->id_customer.$cookie->passwd.$_SERVER['SCRIPT_NAME']));
823
		else
824
			return (self::encrypt($cookie->id_customer.$cookie->passwd.$page));
825
	}
826
827
	/**
828
	* Encrypt password
829
	*
830
	* @param object $object Object to display
0 ignored issues
show
Bug introduced by
There is no parameter named $object. 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...
831
	*/
832
	public static function getAdminToken($string)
833
	{
834
		return !empty($string) ? self::encrypt($string) : false;
835
	}
836
	public static function getAdminTokenLite($tab)
837
	{
838
		global $cookie;
839
		return self::getAdminToken($tab.(int)Tab::getIdFromClassName($tab).(int)$cookie->id_employee);
840
	}
841
842
	/**
843
	* Get the user's journey
844
	*
845
	* @param integer $id_category Category ID
846
	* @param string $path Path end
847
	* @param boolean $linkOntheLastItem Put or not a link on the current category
848
	* @param string [optionnal] $categoryType defined what type of categories is used (products or cms)
849
	*/
850
	public static function getPath($id_category, $path = '', $linkOntheLastItem = false, $categoryType = 'products')
851
	{
852
		global $link, $cookie;
853
854
		if ($id_category == 1)
855
			return '<span class="navigation_end">'.$path.'</span>';
856
857
		$pipe = Configuration::get('PS_NAVIGATION_PIPE');
858
		if (empty($pipe))
859
			$pipe = '>';
860
861
		$fullPath = '';
862
863
		if ($categoryType === 'products')
864
		{
865
			$category = Db::getInstance()->getRow('
866
			SELECT id_category, level_depth, nleft, nright
867
			FROM '._DB_PREFIX_.'category
868
			WHERE id_category = '.(int)$id_category);
869
870
			if (isset($category['id_category']))
871
			{
872
				$categories = Db::getInstance()->ExecuteS('
873
				SELECT c.id_category, cl.name, cl.link_rewrite
874
				FROM '._DB_PREFIX_.'category c
875
				LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category)
876
				WHERE c.nleft <= '.(int)$category['nleft'].' AND c.nright >= '.(int)$category['nright'].' AND cl.id_lang = '.(int)($cookie->id_lang).' AND c.id_category != 1
877
				ORDER BY c.level_depth ASC
878
				LIMIT '.(int)$category['level_depth']);
879
880
				$n = 1;
881
				$nCategories = (int)sizeof($categories);
882
				foreach ($categories AS $category)
883
				{
884
					$fullPath .=
885
					(($n < $nCategories OR $linkOntheLastItem) ? '<a href="'.self::safeOutput($link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'">' : '').
886
					htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
887
					(($n < $nCategories OR $linkOntheLastItem) ? '</a>' : '').
888
					(($n++ != $nCategories OR !empty($path)) ? '<span class="navigation-pipe">'.$pipe.'</span>' : '');
889
				}
890
891
				return $fullPath.$path;
892
			}
893
		}
894
		elseif ($categoryType === 'CMS')
895
		{
896
			$category = new CMSCategory((int)($id_category), (int)($cookie->id_lang));
897
			if (!Validate::isLoadedObject($category))
898
				die(self::displayError());
0 ignored issues
show
Coding Style Compatibility introduced by
The method getPath() 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...
899
			$categoryLink = $link->getCMSCategoryLink($category);
900
901
			if ($path != $category->name)
902
				$fullPath .= '<a href="'.self::safeOutput($categoryLink).'">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a><span class="navigation-pipe">'.$pipe.'</span>'.$path;
903
			else
904
				$fullPath = ($linkOntheLastItem ? '<a href="'.self::safeOutput($categoryLink).'">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($linkOntheLastItem ? '</a>' : '');
905
906
			return self::getPath((int)($category->id_parent), $fullPath, $linkOntheLastItem, $categoryType);
907
		}
908
	}
909
910
	/**
911
	* @param string [optionnal] $type_cat defined what type of categories is used (products or cms)
912
	*/
913
	public static function getFullPath($id_category, $end, $type_cat = 'products')
914
	{
915
		global $cookie;
916
917
		$pipe = (Configuration::get('PS_NAVIGATION_PIPE') ? Configuration::get('PS_NAVIGATION_PIPE') : '>');
918
919
		if ($type_cat === 'products')
920
			$category = new Category((int)($id_category), (int)($cookie->id_lang));
921
		elseif ($type_cat === 'CMS')
922
			$category = new CMSCategory((int)($id_category), (int)($cookie->id_lang));
923
924
		if (!Validate::isLoadedObject($category))
0 ignored issues
show
Bug introduced by
The variable $category 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...
925
			$id_category = 1;
926
		if ($id_category == 1)
927
			return htmlentities($end, ENT_NOQUOTES, 'UTF-8');
928
929
		return self::getPath($id_category, $category->name, true, $type_cat).'<span class="navigation-pipe">'.$pipe.'</span> <span class="navigation_product">'.htmlentities($end, ENT_NOQUOTES, 'UTF-8').'</span>';
930
	}
931
932
	/**
933
	 * @deprecated
934
	 */
935
	public static function getCategoriesTotal()
936
	{
937
		Tools::displayAsDeprecated();
938
		$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT COUNT(`id_category`) AS total FROM `'._DB_PREFIX_.'category`');
939
		return (int)($row['total']);
940
	}
941
942
	/**
943
	 * @deprecated
944
	 */
945
	public static function getProductsTotal()
946
	{
947
		Tools::displayAsDeprecated();
948
		$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT COUNT(`id_product`) AS total FROM `'._DB_PREFIX_.'product`');
949
		return (int)($row['total']);
950
	}
951
952
	/**
953
	 * @deprecated
954
	 */
955
	public static function getCustomersTotal()
956
	{
957
		Tools::displayAsDeprecated();
958
		$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT COUNT(`id_customer`) AS total FROM `'._DB_PREFIX_.'customer`');
959
		return (int)($row['total']);
960
	}
961
962
	/**
963
	 * @deprecated
964
	 */
965
	public static function getOrdersTotal()
966
	{
967
		Tools::displayAsDeprecated();
968
		$row = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT COUNT(`id_order`) AS total FROM `'._DB_PREFIX_.'orders`');
969
		return (int)($row['total']);
970
	}
971
972
	/*
973
	** Historyc translation function kept for compatibility
974
	** Removing soon
975
	*/
976
	public static function historyc_l($key, $translations)
977
	{
978
		global $cookie;
979
		if (!$translations OR !is_array($translations))
980
			die(self::displayError());
0 ignored issues
show
Coding Style Compatibility introduced by
The method historyc_l() 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...
981
		$iso = strtoupper(Language::getIsoById($cookie->id_lang));
982
		$lang = key_exists($iso, $translations) ? $translations[$iso] : false;
983
		return (($lang AND is_array($lang) AND key_exists($key, $lang)) ? stripslashes($lang[$key]) : $key);
984
	}
985
986
987
	/**
988
	 * Return the friendly url from the provided string
989
	 *
990
	 * @param string $str
991
	 * @param bool $utf8_decode => needs to be marked as deprecated
992
	 * @return string
993
	 */
994
	public static function link_rewrite($str, $utf8_decode = false)
0 ignored issues
show
Unused Code introduced by
The parameter $utf8_decode is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
995
	{
996
		return self::str2url($str);
997
	}
998
999
	/**
1000
	 * Return a friendly url made from the provided string
1001
	 * If the mbstring library is available, the output is the same as the js function of the same name
1002
	 *
1003
	 * @param string $str
1004
	 * @return string
1005
	 */
1006
	public static function str2url($str)
1007
	{
1008
		if (function_exists('mb_strtolower'))
1009
			$str = mb_strtolower($str, 'utf-8');
1010
1011
		$str = trim($str);
1012
		$str = self::replaceAccentedChars($str);
1013
1014
		// Remove all non-whitelist chars.
1015
		$str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]-]/','', $str);
1016
		$str = preg_replace('/[\s\'\:\/\[\]-]+/',' ', $str);
1017
		$str = preg_replace('/[ ]/','-', $str);
1018
		$str = preg_replace('/[\/]/','-', $str);
1019
1020
		// If it was not possible to lowercase the string with mb_strtolower, we do it after the transformations.
1021
		// This way we lose fewer special chars.
1022
		$str = strtolower($str);
1023
1024
		return $str;
1025
	}
1026
1027
	/**
1028
	 * Replace all accented chars by their equivalent non accented chars.
1029
	 *
1030
	 * @param string $str
1031
	 * @return string
1032
	 */
1033
	public static function replaceAccentedChars($str)
1034
	{
1035
		$str = preg_replace('/[\x{0105}\x{0104}\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}]/u','a', $str);
1036
		$str = preg_replace('/[\x{00E7}\x{010D}\x{0107}\x{0106}]/u','c', $str);
1037
		$str = preg_replace('/[\x{010F}]/u','d', $str);
1038
		$str = preg_replace('/[\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{011B}\x{0119}\x{0118}]/u','e', $str);
1039
		$str = preg_replace('/[\x{00EC}\x{00ED}\x{00EE}\x{00EF}]/u','i', $str);
1040
		$str = preg_replace('/[\x{0142}\x{0141}\x{013E}\x{013A}]/u','l', $str);
1041
		$str = preg_replace('/[\x{00F1}\x{0148}]/u','n', $str);
1042
		$str = preg_replace('/[\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F8}\x{00D3}]/u','o', $str);
1043
		$str = preg_replace('/[\x{0159}\x{0155}]/u','r', $str);
1044
		$str = preg_replace('/[\x{015B}\x{015A}\x{0161}]/u','s', $str);
1045
		$str = preg_replace('/[\x{00DF}]/u','ss', $str);
1046
		$str = preg_replace('/[\x{0165}]/u','t', $str);
1047
		$str = preg_replace('/[\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{016F}]/u','u', $str);
1048
		$str = preg_replace('/[\x{00FD}\x{00FF}]/u','y', $str);
1049
		$str = preg_replace('/[\x{017C}\x{017A}\x{017B}\x{0179}\x{017E}]/u','z', $str);
1050
		$str = preg_replace('/[\x{00E6}]/u','ae', $str);
1051
		$str = preg_replace('/[\x{0153}]/u','oe', $str);
1052
		return $str;
1053
	}
1054
1055
	/**
1056
	* Truncate strings
1057
	*
1058
	* @param string $str
1059
	* @param integer $maxLen Max length
1060
	* @param string $suffix Suffix optional
1061
	* @return string $str truncated
1062
	*/
1063
	/* CAUTION : Use it only on module hookEvents.
1064
	** For other purposes use the smarty function instead */
1065
	public static function truncate($str, $maxLen, $suffix = '...')
1066
	{
1067
	 	if (self::strlen($str) <= $maxLen)
1068
	 		return $str;
1069
	 	$str = utf8_decode($str);
1070
	 	return (utf8_encode(substr($str, 0, $maxLen - self::strlen($suffix)).$suffix));
1071
	}
1072
1073
	/**
1074
	* Generate date form
1075
	*
1076
	* @param integer $year Year to select
0 ignored issues
show
Bug introduced by
There is no parameter named $year. 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...
1077
	* @param integer $month Month to select
0 ignored issues
show
Bug introduced by
There is no parameter named $month. 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...
1078
	* @param integer $day Day to select
0 ignored issues
show
Bug introduced by
There is no parameter named $day. 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...
1079
	* @return array $tab html data with 3 cells :['days'], ['months'], ['years']
1080
	*
1081
	*/
1082
	public static function dateYears()
1083
	{
1084
		for ($i = date('Y') - 10; $i >= 1900; $i--)
1085
			$tab[] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tab was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tab = 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...
1086
		return $tab;
0 ignored issues
show
Bug introduced by
The variable $tab 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...
1087
	}
1088
1089
	public static function dateDays()
1090
	{
1091
		for ($i = 1; $i != 32; $i++)
1092
			$tab[] = $i;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tab was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tab = 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...
1093
		return $tab;
0 ignored issues
show
Bug introduced by
The variable $tab 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...
1094
	}
1095
1096
	public static function dateMonths()
1097
	{
1098
		for ($i = 1; $i != 13; $i++)
1099
			$tab[$i] = date('F', mktime(0, 0, 0, $i, date('m'), date('Y')));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tab was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tab = 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...
1100
		return $tab;
0 ignored issues
show
Bug introduced by
The variable $tab 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...
1101
	}
1102
1103
	public static function hourGenerate($hours, $minutes, $seconds)
1104
	{
1105
		return implode(':', array($hours, $minutes, $seconds));
1106
	}
1107
1108
	public static function dateFrom($date)
1109
	{
1110
		$tab = explode(' ', $date);
1111
		if (!isset($tab[1]))
1112
			$date .= ' ' . self::hourGenerate(0, 0, 0);
1113
		return $date;
1114
	}
1115
1116
	public static function dateTo($date)
1117
	{
1118
		$tab = explode(' ', $date);
1119
		if (!isset($tab[1]))
1120
			$date .= ' ' . self::hourGenerate(23, 59, 59);
1121
		return $date;
1122
	}
1123
1124
	/**
1125
	 * @deprecated
1126
	 */
1127
	public static function getExactTime()
1128
	{
1129
		Tools::displayAsDeprecated();
1130
		return time()+microtime();
1131
	}
1132
1133
	static function strtolower($str)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1134
	{
1135
		if (is_array($str))
1136
			return false;
1137
		if (function_exists('mb_strtolower'))
1138
			return mb_strtolower($str, 'utf-8');
1139
		return strtolower($str);
1140
	}
1141
1142
	static function strlen($str, $encoding = 'UTF-8')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1143
	{
1144
		if (is_array($str))
1145
			return false;
1146
		$str = html_entity_decode($str, ENT_COMPAT, 'UTF-8');
1147
		if (function_exists('mb_strlen'))
1148
			return mb_strlen($str, $encoding);
1149
		return strlen($str);
1150
	}
1151
1152
	static function stripslashes($string)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1153
	{
1154
		if (_PS_MAGIC_QUOTES_GPC_)
1155
			$string = stripslashes($string);
1156
		return $string;
1157
	}
1158
1159
	static function strtoupper($str)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1160
	{
1161
		if (is_array($str))
1162
			return false;
1163
		if (function_exists('mb_strtoupper'))
1164
			return mb_strtoupper($str, 'utf-8');
1165
		return strtoupper($str);
1166
	}
1167
1168
	static function substr($str, $start, $length = false, $encoding = 'utf-8')
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1169
	{
1170
		if (is_array($str))
1171
			return false;
1172
		if (function_exists('mb_substr'))
1173
			return mb_substr($str, (int)($start), ($length === false ? self::strlen($str) : (int)($length)), $encoding);
1174
		return substr($str, $start, ($length === false ? self::strlen($str) : (int)($length)));
1175
	}
1176
1177
	static function ucfirst($str)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1178
	{
1179
		return self::strtoupper(self::substr($str, 0, 1)).self::substr($str, 1);
0 ignored issues
show
Documentation introduced by
1 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...
1180
	}
1181
1182
	public static function orderbyPrice(&$array, $orderWay)
1183
	{
1184
		foreach ($array as &$row)
1185
			$row['price_tmp'] =  Product::getPriceStatic($row['id_product'], true, ((isset($row['id_product_attribute']) AND !empty($row['id_product_attribute'])) ? (int)($row['id_product_attribute']) : NULL), 2);
1186
		if (strtolower($orderWay) == 'desc')
1187
			uasort($array, 'cmpPriceDesc');
1188
		else
1189
			uasort($array, 'cmpPriceAsc');
1190
		foreach ($array as &$row)
1191
			unset($row['price_tmp']);
1192
	}
1193
1194
	public static function iconv($from, $to, $string)
1195
	{
1196
		if (function_exists('iconv'))
1197
			return iconv($from, $to.'//TRANSLIT', str_replace('¥', '&yen;', str_replace('£', '&pound;', str_replace('€', '&euro;', $string))));
1198
		return html_entity_decode(htmlentities($string, ENT_NOQUOTES, $from), ENT_NOQUOTES, $to);
1199
	}
1200
1201
	public static function isEmpty($field)
1202
	{
1203
		return ($field === '' OR $field === NULL);
1204
	}
1205
1206
	/**
1207
	* @deprecated
1208
	**/
1209
	public static function getTimezones($select = false)
1210
	{
1211
		Tools::displayAsDeprecated();
1212
1213
		static $_cache = 0;
1214
1215
		// One select
1216
		if ($select)
1217
		{
1218
			// No cache
1219
			if (!$_cache)
1220
			{
1221
				$tmz = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow('SELECT `name` FROM '._DB_PREFIX_.'timezone WHERE id_timezone = '.(int)($select));
1222
				$_cache = $tmz['name'];
1223
			}
1224
			return $_cache;
1225
		}
1226
1227
		// Multiple select
1228
		$tmz = Db::getInstance(_PS_USE_SQL_SLAVE_)->s('SELECT * FROM '._DB_PREFIX_.'timezone');
1229
		$tab = array();
1230
		foreach ($tmz as $timezone)
1231
			$tab[$timezone['id_timezone']] = str_replace('_', ' ', $timezone['name']);
1232
		return $tab;
1233
	}
1234
1235
	/**
1236
	* @deprecated
1237
	**/
1238
	public static function ps_set_magic_quotes_runtime($var)
1239
	{
1240
		Tools::displayAsDeprecated();
1241
1242
		if (function_exists('set_magic_quotes_runtime'))
1243
			set_magic_quotes_runtime($var);
0 ignored issues
show
Deprecated Code introduced by
The function set_magic_quotes_runtime() has been deprecated with message: Deprecated as of PHP 5.3.0. Relying on this feature is highly discouraged.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1244
	}
1245
1246
	public static function ps_round($value, $precision = 0)
1247
	{
1248
		$method = (int)(Configuration::get('PS_PRICE_ROUND_MODE'));
1249
		if ($method == PS_ROUND_UP)
1250
			return self::ceilf($value, $precision);
1251
		elseif ($method == PS_ROUND_DOWN)
1252
			return self::floorf($value, $precision);
1253
		return round($value, $precision);
1254
	}
1255
1256
	public static function ceilf($value, $precision = 0)
1257
	{
1258
		$precisionFactor = $precision == 0 ? 1 : pow(10, $precision);
1259
		$tmp = $value * $precisionFactor;
1260
		$tmp2 = (string)$tmp;
1261
		// If the current value has already the desired precision
1262
		if (strpos($tmp2, '.') === false)
1263
			return ($value);
1264
		if ($tmp2[strlen($tmp2) - 1] == 0)
1265
			return $value;
1266
		return ceil($tmp) / $precisionFactor;
1267
	}
1268
1269
	public static function floorf($value, $precision = 0)
1270
	{
1271
		$precisionFactor = $precision == 0 ? 1 : pow(10, $precision);
1272
		$tmp = $value * $precisionFactor;
1273
		$tmp2 = (string)$tmp;
1274
		// If the current value has already the desired precision
1275
		if (strpos($tmp2, '.') === false)
1276
			return ($value);
1277
		if ($tmp2[strlen($tmp2) - 1] == 0)
1278
			return $value;
1279
		return floor($tmp) / $precisionFactor;
1280
	}
1281
1282
	/**
1283
	 * file_exists() wrapper with cache to speedup performance
1284
	 *
1285
	 * @param string $filename File name
1286
	 * @return boolean Cached result of file_exists($filename)
1287
	 */
1288
	public static function file_exists_cache($filename)
1289
	{
1290
		if (!isset(self::$file_exists_cache[$filename]))
1291
			self::$file_exists_cache[$filename] = file_exists($filename);
1292
		return self::$file_exists_cache[$filename];
1293
	}
1294
1295
	public static function file_get_contents($url, $useIncludePath = false, $streamContext = NULL, $curlTimeOut = 5)
1296
	{
1297
		if ($streamContext == NULL)
1298
			$streamContext = @stream_context_create(array('http' => array('timeout' => 5)));
1299
1300
		if (in_array(ini_get('allow_url_fopen'), array('On', 'on', '1')))
1301
			return @file_get_contents($url, $useIncludePath, $streamContext);
1302
		elseif (function_exists('curl_init') && in_array(ini_get('allow_url_fopen'), array('On', 'on', '1')))
1303
		{
1304
			$curl = curl_init();
1305
			curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1306
			curl_setopt($curl, CURLOPT_URL, $url);
1307
			curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, $curlTimeOut);
1308
			curl_setopt($curl, CURLOPT_TIMEOUT, $curlTimeOut);
1309
			$content = curl_exec($curl);
1310
			curl_close($curl);
1311
			return $content;
1312
		}
1313
		else
1314
			return false;
1315
	}
1316
1317
	public static function simplexml_load_file($url, $class_name = null)
1318
	{
1319
		if (in_array(ini_get('allow_url_fopen'), array('On', 'on', '1')))
1320
			return simplexml_load_string(Tools::file_get_contents($url), $class_name);
1321
		else
1322
			return false;
1323
	}
1324
1325
	public static $a = 0;
1326
	public static function minifyHTML($html_content)
1327
	{
1328
		if (strlen($html_content) > 0)
1329
		{
1330
			//set an alphabetical order for args
1331
			$html_content = preg_replace_callback(
1332
				'/(<[a-zA-Z0-9]+)((\s?[a-zA-Z0-9]+=[\"\\\'][^\"\\\']*[\"\\\']\s?)*)>/'
1333
				,array('Tools', 'minifyHTMLpregCallback')
1334
				,$html_content);
1335
1336
			require_once(_PS_TOOL_DIR_.'minify_html/minify_html.class.php');
1337
			$html_content = str_replace(chr(194) . chr(160), '&nbsp;', $html_content);
1338
			$html_content = Minify_HTML::minify($html_content, array('xhtml', 'cssMinifier', 'jsMinifier'));
1339
1340
			if (Configuration::get('PS_HIGH_HTML_THEME_COMPRESSION'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_...TML_THEME_COMPRESSION') of type string|null is loosely compared to true; 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...
1341
			{
1342
				//$html_content = preg_replace('/"([^\>\s"]*)"/i', '$1', $html_content);//FIXME create a js bug
1343
				$html_content = preg_replace('/<!DOCTYPE \w[^\>]*dtd\">/is', '', $html_content);
1344
				$html_content = preg_replace('/\s\>/is', '>', $html_content);
1345
				$html_content = str_replace('</li>', '', $html_content);
1346
				$html_content = str_replace('</dt>', '', $html_content);
1347
				$html_content = str_replace('</dd>', '', $html_content);
1348
				$html_content = str_replace('</head>', '', $html_content);
1349
				$html_content = str_replace('<head>', '', $html_content);
1350
				$html_content = str_replace('</html>', '', $html_content);
1351
				$html_content = str_replace('</body>', '', $html_content);
1352
				//$html_content = str_replace('</p>', '', $html_content);//FIXME doesnt work...
1353
				$html_content = str_replace("</option>\n", '', $html_content);//TODO with bellow
1354
				$html_content = str_replace('</option>', '', $html_content);
1355
				$html_content = str_replace('<script type=text/javascript>', '<script>', $html_content);//Do a better expreg
1356
				$html_content = str_replace("<script>\n", '<script>', $html_content);//Do a better expreg
1357
			}
1358
1359
			return $html_content;
1360
		}
1361
		return false;
1362
	}
1363
1364
	/**
1365
	* Translates a string with underscores into camel case (e.g. first_name -> firstName)
1366
	* @prototype string public static function toCamelCase(string $str[, bool $capitaliseFirstChar = false])
1367
	*/
1368
	public static function toCamelCase($str, $capitaliseFirstChar = false)
1369
	{
1370
		$str = strtolower($str);
1371
		if ($capitaliseFirstChar)
1372
			$str = ucfirst($str);
1373
		return preg_replace_callback('/_([a-z])/', create_function('$c', 'return strtoupper($c[1]);'), $str);
1374
	}
1375
1376
	public static function getBrightness($hex)
1377
	{
1378
		$hex = str_replace('#', '', $hex);
1379
		$r = hexdec(substr($hex, 0, 2));
1380
		$g = hexdec(substr($hex, 2, 2));
1381
		$b = hexdec(substr($hex, 4, 2));
1382
		return (($r * 299) + ($g * 587) + ($b * 114)) / 1000;
1383
	}
1384
	public static function minifyHTMLpregCallback($preg_matches)
1385
	{
1386
		$args = array();
1387
		preg_match_all('/[a-zA-Z0-9]+=[\"\\\'][^\"\\\']*[\"\\\']/is', $preg_matches[2], $args);
1388
		$args = $args[0];
1389
		sort($args);
1390
		// if there is no args in the balise, we don't write a space (avoid previous : <title >, now : <title>)
1391
		if (empty($args))
1392
			$output = $preg_matches[1].'>';
1393
		else
1394
			$output = $preg_matches[1].' '.implode(' ', $args).'>';
1395
		return $output;
1396
	}
1397
1398
	public static function packJSinHTML($html_content)
1399
	{
1400
		if (strlen($html_content) > 0)
1401
		{
1402
			$htmlContentCopy = $html_content;
1403
			$html_content = preg_replace_callback(
1404
				'/\\s*(<script\\b[^>]*?>)([\\s\\S]*?)(<\\/script>)\\s*/i'
1405
				,array('Tools', 'packJSinHTMLpregCallback')
1406
				,$html_content);
1407
1408
			// If the string is too big preg_replace return null: http://php.net/manual/en/function.preg-replace-callback.php
1409
			// In this case, we don't compress the content
1410
			if ($html_content === null)
1411
			{
1412
				error_log('Error occured in function packJSinHTML');
1413
				return $htmlContentCopy;
1414
			}
1415
			return $html_content;
1416
		}
1417
		return false;
1418
	}
1419
1420
	public static function packJSinHTMLpregCallback($preg_matches)
1421
	{
1422
		$preg_matches[1] = $preg_matches[1].'/* <![CDATA[ */';
1423
		$preg_matches[2] = self::packJS($preg_matches[2]);
1424
		$preg_matches[count($preg_matches)-1] = '/* ]]> */'.$preg_matches[count($preg_matches)-1];
1425
		unset($preg_matches[0]);
1426
		$output = implode('', $preg_matches);
1427
		return $output;
1428
	}
1429
1430
1431
	public static function packJS($js_content)
1432
	{
1433
		if (strlen($js_content) > 0)
1434
		{
1435
			require_once(_PS_TOOL_DIR_.'js_minify/jsmin.php');
1436
			return JSMin::minify($js_content);
1437
		}
1438
		return false;
1439
	}
1440
1441
	public static function minifyCSS($css_content, $fileuri = false)
1442
	{
1443
		global $current_css_file;
1444
1445
		$current_css_file = $fileuri;
1446
		if (strlen($css_content) > 0)
1447
		{
1448
			$css_content = preg_replace('#/\*.*?\*/#s', '', $css_content);
1449
			$css_content = preg_replace_callback('#url\((?:\'|")?([^\)\'"]*)(?:\'|")?\)#s',array('Tools', 'replaceByAbsoluteURL'), $css_content);
1450
1451
			$css_content = preg_replace('#\s+#',' ',$css_content);
1452
			$css_content = str_replace("\t", '', $css_content);
1453
			$css_content = str_replace("\n", '', $css_content);
1454
			//$css_content = str_replace('}', "}\n", $css_content);
1455
1456
			$css_content = str_replace('; ', ';', $css_content);
1457
			$css_content = str_replace(': ', ':', $css_content);
1458
			$css_content = str_replace(' {', '{', $css_content);
1459
			$css_content = str_replace('{ ', '{', $css_content);
1460
			$css_content = str_replace(', ', ',', $css_content);
1461
			$css_content = str_replace('} ', '}', $css_content);
1462
			$css_content = str_replace(' }', '}', $css_content);
1463
			$css_content = str_replace(';}', '}', $css_content);
1464
			$css_content = str_replace(':0px', ':0', $css_content);
1465
			$css_content = str_replace(' 0px', ' 0', $css_content);
1466
			$css_content = str_replace(':0em', ':0', $css_content);
1467
			$css_content = str_replace(' 0em', ' 0', $css_content);
1468
			$css_content = str_replace(':0pt', ':0', $css_content);
1469
			$css_content = str_replace(' 0pt', ' 0', $css_content);
1470
			$css_content = str_replace(':0%', ':0', $css_content);
1471
			$css_content = str_replace(' 0%', ' 0', $css_content);
1472
1473
			return trim($css_content);
1474
		}
1475
		return false;
1476
	}
1477
1478
	public static function replaceByAbsoluteURL($matches)
1479
	{
1480
		global $current_css_file;
1481
1482
		$protocol_link = self::getCurrentUrlProtocolPrefix();
1483
1484
		if (array_key_exists(1, $matches))
1485
		{
1486
			$tmp = dirname($current_css_file).'/'.$matches[1];
1487
			return 'url(\''.$protocol_link.self::getMediaServer($tmp).$tmp.'\')';
1488
		}
1489
		return false;
1490
	}
1491
1492
	/**
1493
	 * addJS load a javascript file in the header
1494
	 *
1495
	 * @param mixed $js_uri
1496
	 * @return void
1497
	 */
1498
	public static function addJS($js_uri)
1499
	{
1500
		global $js_files;
1501
		if (!isset($js_files))
1502
			$js_files = array();
1503
		// avoid useless operation...
1504
		if (in_array($js_uri, $js_files))
1505
			return true;
1506
1507
		// detect mass add
1508
		if (!is_array($js_uri) && !in_array($js_uri, $js_files))
1509
			$js_uri = array($js_uri);
1510
		else
1511
			foreach ($js_uri as $key => $js)
0 ignored issues
show
Bug introduced by
The expression $js_uri of type object|integer|double|string|null|boolean|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...
1512
				if (in_array($js, $js_files))
1513
					unset($js_uri[$key]);
1514
1515
		//overriding of modules js files
1516
		foreach ($js_uri AS $key => &$file)
0 ignored issues
show
Bug introduced by
The expression $js_uri of type object|integer|double|string|null|boolean|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...
1517
		{
1518
			if (!preg_match('/^http(s?):\/\//i', $file))
1519
			{
1520
				$different = 0;
1521
				$override_path = str_replace(__PS_BASE_URI__.'modules/', _PS_ROOT_DIR_.'/themes/'._THEME_NAME_.'/js/modules/', $file, $different);
1522
				if ($different && file_exists($override_path))
0 ignored issues
show
Bug Best Practice introduced by
The expression $different of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1523
					$file = str_replace(__PS_BASE_URI__.'modules/', __PS_BASE_URI__.'themes/'._THEME_NAME_.'/js/modules/', $file, $different);
1524
				else
1525
				{
1526
					// remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
1527
					$url_data = parse_url($file);
1528
					$file_uri = _PS_ROOT_DIR_.self::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
1529
					// check if js files exists
1530
					if (!file_exists($file_uri))
1531
						unset($js_uri[$key]);
1532
				}
1533
			}
1534
		}
1535
1536
		// adding file to the big array...
1537
		$js_files = array_merge($js_files, $js_uri);
1538
1539
		return true;
1540
	}
1541
1542
	/**
1543
	 * addCSS allows you to add stylesheet at any time.
1544
	 *
1545
	 * @param mixed $css_uri
1546
	 * @param string $css_media_type
1547
	 * @return true
1548
	 */
1549
	public static function addCSS($css_uri, $css_media_type = 'all')
1550
	{
1551
		global $css_files;
1552
1553
		if (is_array($css_uri))
1554
		{
1555
			foreach ($css_uri as $file => $media_type)
1556
				self::addCSS($file, $media_type);
1557
			return true;
1558
		}
1559
1560
		//overriding of modules css files
1561
		$different = 0;
1562
		$override_path = str_replace(__PS_BASE_URI__.'modules/', _PS_ROOT_DIR_.'/themes/'._THEME_NAME_.'/css/modules/', $css_uri, $different);
1563
		if ($different && file_exists($override_path))
0 ignored issues
show
Bug Best Practice introduced by
The expression $different of type integer|null is loosely compared to true; this is ambiguous if the integer can be zero. 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 integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
1564
			$css_uri = str_replace(__PS_BASE_URI__.'modules/', __PS_BASE_URI__.'themes/'._THEME_NAME_.'/css/modules/', $css_uri, $different);
1565
		else
1566
		{
1567
			// remove PS_BASE_URI on _PS_ROOT_DIR_ for the following
1568
			$url_data = parse_url($css_uri);
1569
			$file_uri = _PS_ROOT_DIR_.self::str_replace_once(__PS_BASE_URI__, DIRECTORY_SEPARATOR, $url_data['path']);
1570
			// check if css files exists
1571
			if (!file_exists($file_uri))
1572
				return true;
1573
		}
1574
1575
		// detect mass add
1576
		$css_uri = array($css_uri => $css_media_type);
1577
1578
		// adding file to the big array...
1579
		if (is_array($css_files))
1580
			$css_files = array_merge($css_files, $css_uri);
1581
		else
1582
			$css_files = $css_uri;
1583
1584
		return true;
1585
	}
1586
1587
1588
	/**
1589
	* Combine Compress and Cache CSS (ccc) calls
1590
	*
1591
	*/
1592
	public static function cccCss() {
1593
		global $css_files;
1594
		//inits
1595
		$css_files_by_media = array();
1596
		$compressed_css_files = array();
1597
		$compressed_css_files_not_found = array();
1598
		$compressed_css_files_infos = array();
1599
		$protocolLink = self::getCurrentUrlProtocolPrefix();
1600
1601
		// group css files by media
1602
		foreach ($css_files as $filename => $media)
1603
		{
1604
			if (!array_key_exists($media, $css_files_by_media))
1605
				$css_files_by_media[$media] = array();
1606
1607
			$infos = array();
1608
			$infos['uri'] = $filename;
1609
			$url_data = parse_url($filename);
1610
			$infos['path'] = _PS_ROOT_DIR_.self::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
1611
			$css_files_by_media[$media]['files'][] = $infos;
1612
			if (!array_key_exists('date', $css_files_by_media[$media]))
1613
				$css_files_by_media[$media]['date'] = 0;
1614
			$css_files_by_media[$media]['date'] = max(
1615
				file_exists($infos['path']) ? filemtime($infos['path']) : 0,
1616
				$css_files_by_media[$media]['date']
1617
			);
1618
1619
			if (!array_key_exists($media, $compressed_css_files_infos))
1620
				$compressed_css_files_infos[$media] = array('key' => '');
1621
			$compressed_css_files_infos[$media]['key'] .= $filename;
1622
		}
1623
1624
		// get compressed css file infos
1625
		foreach ($compressed_css_files_infos as $media => &$info)
1626
		{
1627
			$key = md5($info['key'].$protocolLink);
1628
			$filename = _PS_THEME_DIR_.'cache/'.$key.'_'.$media.'.css';
1629
			$info = array(
1630
				'key' => $key,
1631
				'date' => file_exists($filename) ? filemtime($filename) : 0
1632
			);
1633
		}
1634
		// aggregate and compress css files content, write new caches files
1635
		foreach ($css_files_by_media as $media => $media_infos)
1636
		{
1637
			$cache_filename = _PS_THEME_DIR_.'cache/'.$compressed_css_files_infos[$media]['key'].'_'.$media.'.css';
1638
			if ($media_infos['date'] > $compressed_css_files_infos[$media]['date'])
1639
			{
1640
				$compressed_css_files[$media] = '';
1641
				foreach ($media_infos['files'] as $file_infos)
1642
				{
1643
					if (file_exists($file_infos['path']))
1644
						$compressed_css_files[$media] .= self::minifyCSS(file_get_contents($file_infos['path']), $file_infos['uri']);
1645
					else
1646
						$compressed_css_files_not_found[] = $file_infos['path'];
1647
				}
1648
				if (!empty($compressed_css_files_not_found))
1649
					$content = '/* WARNING ! file(s) not found : "'.
1650
						implode(',', $compressed_css_files_not_found).
1651
						'" */'."\n".$compressed_css_files[$media];
1652
				else
1653
					$content = $compressed_css_files[$media];
1654
				file_put_contents($cache_filename, $content);
1655
				chmod($cache_filename, 0777);
1656
			}
1657
			$compressed_css_files[$media] = $cache_filename;
1658
		}
1659
1660
		// rebuild the original css_files array
1661
		$css_files = array();
1662
		foreach ($compressed_css_files as $media => $filename)
1663
		{
1664
			$url = str_replace(_PS_THEME_DIR_, _THEMES_DIR_._THEME_NAME_.'/', $filename);
1665
			$css_files[$protocolLink.self::getMediaServer($url).$url] = $media;
1666
		}
1667
	}
1668
1669
1670
	/**
1671
	* Combine Compress and Cache (ccc) JS calls
1672
	*/
1673
	public static function cccJS() {
1674
		global $js_files;
1675
		//inits
1676
		$compressed_js_files_not_found = array();
1677
		$js_files_infos = array();
1678
		$js_files_date = 0;
1679
		$compressed_js_file_date = 0;
0 ignored issues
show
Unused Code introduced by
$compressed_js_file_date is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1680
		$compressed_js_filename = '';
1681
		$js_external_files = array();
1682
		$protocolLink = self::getCurrentUrlProtocolPrefix();
1683
1684
		// get js files infos
1685
		foreach ($js_files as $filename)
1686
		{
1687
			$expr = explode(':', $filename);
1688
1689
			if ($expr[0] == 'http')
1690
				$js_external_files[] = $filename;
1691
			else
1692
			{
1693
				$infos = array();
1694
				$infos['uri'] = $filename;
1695
				$url_data = parse_url($filename);
1696
				$infos['path'] =_PS_ROOT_DIR_.self::str_replace_once(__PS_BASE_URI__, '/', $url_data['path']);
1697
				$js_files_infos[] = $infos;
1698
1699
				$js_files_date = max(
1700
					file_exists($infos['path']) ? filemtime($infos['path']) : 0,
1701
					$js_files_date
1702
				);
1703
				$compressed_js_filename .= $filename;
1704
			}
1705
		}
1706
1707
		// get compressed js file infos
1708
		$compressed_js_filename = md5($compressed_js_filename);
1709
1710
		$compressed_js_path = _PS_THEME_DIR_.'cache/'.$compressed_js_filename.'.js';
1711
		$compressed_js_file_date = file_exists($compressed_js_path) ? filemtime($compressed_js_path) : 0;
1712
1713
		// aggregate and compress js files content, write new caches files
1714
		if ($js_files_date > $compressed_js_file_date)
1715
		{
1716
			$content = '';
1717
			foreach ($js_files_infos as $file_infos)
1718
			{
1719
				if (file_exists($file_infos['path']))
1720
					$content .= file_get_contents($file_infos['path']).';';
1721
				else
1722
					$compressed_js_files_not_found[] = $file_infos['path'];
1723
			}
1724
			$content = self::packJS($content);
1725
1726
			if (!empty($compressed_js_files_not_found))
1727
				$content = '/* WARNING ! file(s) not found : "'.
1728
					implode(',', $compressed_js_files_not_found).
1729
					'" */'."\n".$content;
1730
1731
			file_put_contents($compressed_js_path, $content);
1732
			chmod($compressed_js_path, 0777);
1733
		}
1734
1735
		// rebuild the original js_files array
1736
		$url = str_replace(_PS_ROOT_DIR_.'/', __PS_BASE_URI__, $compressed_js_path);
1737
		$js_files = array_merge(array($protocolLink.self::getMediaServer($url).$url), $js_external_files);
1738
1739
	}
1740
1741
	private static $_cache_nb_media_servers = null;
1742
	public static function getMediaServer($filename)
1743
	{
1744
		if (self::$_cache_nb_media_servers === null)
1745
		{
1746
			if (_MEDIA_SERVER_1_ == '')
1747
				self::$_cache_nb_media_servers = 0;
1748
			elseif (_MEDIA_SERVER_2_ == '')
1749
				self::$_cache_nb_media_servers = 1;
1750
			elseif (_MEDIA_SERVER_3_ == '')
1751
				self::$_cache_nb_media_servers = 2;
1752
			else
1753
				self::$_cache_nb_media_servers = 3;
1754
		}
1755
1756
		if (self::$_cache_nb_media_servers AND ($id_media_server = (abs(crc32($filename)) % self::$_cache_nb_media_servers + 1)))
1757
			return constant('_MEDIA_SERVER_'.$id_media_server.'_');
1758
		return self::getHttpHost();
1759
	}
1760
1761
	public static function generateHtaccess($path, $rewrite_settings, $cache_control, $specific = '', $disableMuliviews = false)
1762
	{
1763
		$tab = array('ErrorDocument' => array(), 'RewriteEngine' => array(), 'RewriteRule' => array());
1764
		$multilang = (Language::countActiveLanguages() > 1);
1765
1766
		// ErrorDocument
1767
		$tab['ErrorDocument']['comment'] = '# Catch 404 errors';
1768
		$tab['ErrorDocument']['content'] = '404 '.__PS_BASE_URI__.'404.php';
1769
1770
		// RewriteEngine
1771
		$tab['RewriteEngine']['comment'] = '# URL rewriting module activation';
1772
1773
		// RewriteRules
1774
		$tab['RewriteRule']['comment'] = '# URL rewriting rules';
1775
1776
		// Compatibility with the old image filesystem
1777
		if (Configuration::get('PS_LEGACY_IMAGES'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_LEGACY_IMAGES') of type string|null is loosely compared to true; 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...
1778
		{
1779
			$tab['RewriteRule']['content']['^([a-z0-9]+)\-([a-z0-9]+)(\-[_a-zA-Z0-9-]*)/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1-$2$3.jpg [L]';
1780
			$tab['RewriteRule']['content']['^([0-9]+)\-([0-9]+)/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1-$2.jpg [L]';
1781
		}
1782
1783
		// Rewriting for product image id < 100 millions
1784
		$tab['RewriteRule']['content']['^([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$1$2.jpg [L]';
1785
		$tab['RewriteRule']['content']['^([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$1$2$3.jpg [L]';
1786
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$1$2$3$4.jpg [L]';
1787
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$4/$1$2$3$4$5.jpg [L]';
1788
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$4/$5/$1$2$3$4$5$6.jpg [L]';
1789
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$4/$5/$6/$1$2$3$4$5$6$7.jpg [L]';
1790
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$4/$5/$6/$7/$1$2$3$4$5$6$7$8.jpg [L]';
1791
		$tab['RewriteRule']['content']['^([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])([0-9])(\-[_a-zA-Z0-9-]*)?/[_a-zA-Z0-9-]*\.jpg$'] = _PS_PROD_IMG_.'$1/$2/$3/$4/$5/$6/$7/$8/$1$2$3$4$5$6$7$8$9.jpg [L]';
1792
1793
		$tab['RewriteRule']['content']['^c/([0-9]+)(\-[_a-zA-Z0-9-]*)/[_a-zA-Z0-9-]*\.jpg$'] = 'img/c/$1$2.jpg [L]';
1794
		$tab['RewriteRule']['content']['^c/([a-zA-Z-]+)/[a-zA-Z0-9-]+\.jpg$'] = 'img/c/$1.jpg [L]';
1795
1796
		if ($multilang)
1797
		{
1798
			$tab['RewriteRule']['content']['^([a-z]{2})/[a-zA-Z0-9-]*/([0-9]+)\-[a-zA-Z0-9-]*\.html'] = 'product.php?id_product=$2&isolang=$1 [QSA,L]';
1799
			$tab['RewriteRule']['content']['^([a-z]{2})/([0-9]+)\-[a-zA-Z0-9-]*\.html'] = 'product.php?id_product=$2&isolang=$1 [QSA,L]';
1800
			$tab['RewriteRule']['content']['^([a-z]{2})/([0-9]+)\-[a-zA-Z0-9-]*(/[a-zA-Z0-9-]*)+'] = 'category.php?id_category=$2&isolang=$1&noredirect=1 [QSA,L]';
1801
			$tab['RewriteRule']['content']['^([a-z]{2})/([0-9]+)\-[a-zA-Z0-9-]*'] = 'category.php?id_category=$2&isolang=$1 [QSA,L]';
1802
			$tab['RewriteRule']['content']['^([a-z]{2})/content/([0-9]+)\-[a-zA-Z0-9-]*'] = 'cms.php?isolang=$1&id_cms=$2 [QSA,L]';
1803
			$tab['RewriteRule']['content']['^([a-z]{2})/content/category/([0-9]+)\-[a-zA-Z0-9-]*'] = 'cms.php?isolang=$1&id_cms_category=$2 [QSA,L]';
1804
			$tab['RewriteRule']['content']['^([a-z]{2})/([0-9]+)__[a-zA-Z0-9-]*'] = 'supplier.php?isolang=$1&id_supplier=$2 [QSA,L]';
1805
			$tab['RewriteRule']['content']['^([a-z]{2})/([0-9]+)_[a-zA-Z0-9-]*'] = 'manufacturer.php?isolang=$1&id_manufacturer=$2 [QSA,L]';
1806
		}
1807
1808
		// PS BASE URI automaticaly prepend the string, do not use PS defines for the image directories
1809
		$tab['RewriteRule']['content']['^([0-9]+)(\-[_a-zA-Z0-9-]*)/[_a-zA-Z0-9-]*\.jpg$'] = 'img/c/$1$2.jpg [L]';
1810
1811
		$tab['RewriteRule']['content']['^([0-9]+)\-[a-zA-Z0-9-]*\.html'] = 'product.php?id_product=$1 [QSA,L]';
1812
		$tab['RewriteRule']['content']['^[a-zA-Z0-9-]*/([0-9]+)\-[a-zA-Z0-9-]*\.html'] = 'product.php?id_product=$1 [QSA,L]';
1813
		// Notice : the id_category rule has to be after product rules.
1814
		// If not, category with number in their name will result a bug
1815
		$tab['RewriteRule']['content']['^([0-9]+)\-[a-zA-Z0-9-]*(/[a-zA-Z0-9-]*)+'] = 'category.php?id_category=$1&noredirect=1 [QSA,L]';
1816
		$tab['RewriteRule']['content']['^([0-9]+)\-[a-zA-Z0-9-]*'] = 'category.php?id_category=$1 [QSA,L]';
1817
		$tab['RewriteRule']['content']['^([0-9]+)__([a-zA-Z0-9-]*)'] = 'supplier.php?id_supplier=$1 [QSA,L]';
1818
		$tab['RewriteRule']['content']['^([0-9]+)_([a-zA-Z0-9-]*)'] = 'manufacturer.php?id_manufacturer=$1 [QSA,L]';
1819
		$tab['RewriteRule']['content']['^content/([0-9]+)\-([a-zA-Z0-9-]*)'] = 'cms.php?id_cms=$1 [QSA,L]';
1820
		$tab['RewriteRule']['content']['^content/category/([0-9]+)\-([a-zA-Z0-9-]*)'] = 'cms.php?id_cms_category=$1 [QSA,L]';
1821
1822
		// Compatibility with the old URLs
1823
		if (!Configuration::get('PS_INSTALL_VERSION') OR version_compare(Configuration::get('PS_INSTALL_VERSION'), '1.4.0.7') == -1)
1824
		{
1825
			// This is a nasty copy/paste of the previous links, but with "lang-en" instead of "en"
1826
			// Do not update it when you add something in the one at the top, it's only for the old links
1827
			$tab['RewriteRule']['content']['^lang-([a-z]{2})/([a-zA-Z0-9-]*)/([0-9]+)\-([a-zA-Z0-9-]*)\.html'] = 'product.php?id_product=$3&isolang=$1 [QSA,L]';
1828
			$tab['RewriteRule']['content']['^lang-([a-z]{2})/([0-9]+)\-([a-zA-Z0-9-]*)\.html'] = 'product.php?id_product=$2&isolang=$1 [QSA,L]';
1829
			$tab['RewriteRule']['content']['^lang-([a-z]{2})/([0-9]+)\-([a-zA-Z0-9-]*)'] = 'category.php?id_category=$2&isolang=$1 [QSA,L]';
1830
			$tab['RewriteRule']['content']['^content/([0-9]+)\-([a-zA-Z0-9-]*)'] = 'cms.php?id_cms=$1 [QSA,L]';
1831
			$tab['RewriteRule']['content']['^content/category/([0-9]+)\-([a-zA-Z0-9-]*)'] = 'cms.php?id_cms_category=$1 [QSA,L]';
1832
		}
1833
1834
		Language::loadLanguages();
1835
		$default_meta = Meta::getMetasByIdLang((int)Configuration::get('PS_LANG_DEFAULT'));
1836
1837
		if ($multilang)
1838
			foreach (Language::getLanguages() as $language)
1839
			{
1840
				foreach (Meta::getMetasByIdLang($language['id_lang']) as $key => $meta)
1841
					if (!empty($meta['url_rewrite']) AND Validate::isLinkRewrite($meta['url_rewrite']))
1842
						$tab['RewriteRule']['content']['^'.$language['iso_code'].'/'.$meta['url_rewrite'].'$'] = $meta['page'].'.php?isolang='.$language['iso_code'].' [QSA,L]';
1843
					elseif (array_key_exists($key, $default_meta) && $default_meta[$key]['url_rewrite'] != '')
1844
						$tab['RewriteRule']['content']['^'.$language['iso_code'].'/'.$default_meta[$key]['url_rewrite'].'$'] = $default_meta[$key]['page'].'.php?isolang='.$language['iso_code'].' [QSA,L]';
1845
				$tab['RewriteRule']['content']['^'.$language['iso_code'].'$'] = $language['iso_code'].'/ [QSA,L]';
1846
				$tab['RewriteRule']['content']['^'.$language['iso_code'].'/([^?&]*)$'] = '$1?isolang='.$language['iso_code'].' [QSA,L]';
1847
			}
1848
		else
1849
			foreach ($default_meta as $key => $meta)
1850
				if (!empty($meta['url_rewrite']))
1851
					$tab['RewriteRule']['content']['^'.$meta['url_rewrite'].'$'] = $meta['page'].'.php [QSA,L]';
1852
				elseif (array_key_exists($key, $default_meta) && $default_meta[$key]['url_rewrite'] != '')
1853
					$tab['RewriteRule']['content']['^'.$default_meta[$key]['url_rewrite'].'$'] = $default_meta[$key]['page'].'.php [QSA,L]';
1854
1855
		if (!$writeFd = @fopen($path, 'w'))
1856
			return false;
1857
1858
		// PS Comments
1859
		fwrite($writeFd, "# .htaccess automaticaly generated by PrestaShop e-commerce open-source solution\n");
1860
		fwrite($writeFd, "# WARNING: PLEASE DO NOT MODIFY THIS FILE MANUALLY. IF NECESSARY, ADD YOUR SPECIFIC CONFIGURATION WITH THE HTACCESS GENERATOR IN BACK OFFICE\n");
1861
		fwrite($writeFd, "# http://www.prestashop.com - http://www.prestashop.com/forums\n\n");
1862
		if (!empty($specific))
1863
			fwrite($writeFd, $specific);
1864
1865
		// RewriteEngine
1866
		fwrite($writeFd, "\n<IfModule mod_rewrite.c>\n");
1867
1868
		if ($disableMuliviews)
1869
			fwrite($writeFd, "\n# Disable Multiviews\nOptions -Multiviews\n\n");
1870
1871
		fwrite($writeFd, $tab['RewriteEngine']['comment']."\nRewriteEngine on\n\n");
1872
		fwrite($writeFd, $tab['RewriteRule']['comment']."\n");
1873
		// Webservice
1874
		if (Configuration::get('PS_WEBSERVICE'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_WEBSERVICE') of type string|null is loosely compared to true; 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...
1875
		{
1876
			fwrite($writeFd, 'RewriteRule ^api/?(.*)$ '.__PS_BASE_URI__."webservice/dispatcher.php?url=$1 [QSA,L]\n");
1877
			if (Configuration::get('PS_WEBSERVICE_CGI_HOST'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_WEBSERVICE_CGI_HOST') of type string|null is loosely compared to true; 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...
1878
				fwrite($writeFd, 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]'."\n");
1879
		}
1880
1881
		// Classic URL rewriting
1882
		if ($rewrite_settings)
1883
			foreach ($tab['RewriteRule']['content'] as $rule => $url)
1884
				fwrite($writeFd, 'RewriteRule '.$rule.' '.__PS_BASE_URI__.$url."\n");
1885
1886
		fwrite($writeFd, "</IfModule>\n\n");
1887
1888
		// ErrorDocument
1889
		fwrite($writeFd, $tab['ErrorDocument']['comment']."\nErrorDocument ".$tab['ErrorDocument']['content']."\n");
1890
1891
		// Cache control
1892
		if ($cache_control)
1893
		{
1894
			$cacheControl = "
1895
<IfModule mod_expires.c>
1896
	ExpiresActive On
1897
	ExpiresByType image/gif \"access plus 1 month\"
1898
	ExpiresByType image/jpeg \"access plus 1 month\"
1899
	ExpiresByType image/png \"access plus 1 month\"
1900
	ExpiresByType text/css \"access plus 1 week\"
1901
	ExpiresByType text/javascript \"access plus 1 week\"
1902
	ExpiresByType application/javascript \"access plus 1 week\"
1903
	ExpiresByType application/x-javascript \"access plus 1 week\"
1904
	ExpiresByType image/x-icon \"access plus 1 year\"
1905
</IfModule>
1906
1907
FileETag INode MTime Size
1908
<IfModule mod_deflate.c>
1909
	AddOutputFilterByType DEFLATE text/html
1910
	AddOutputFilterByType DEFLATE text/css
1911
	AddOutputFilterByType DEFLATE text/javascript
1912
	AddOutputFilterByType DEFLATE application/javascript
1913
	AddOutputFilterByType DEFLATE application/x-javascript
1914
</IfModule>
1915
				";
1916
			fwrite($writeFd, $cacheControl);
1917
		}
1918
		fclose($writeFd);
1919
1920
		Module::hookExec('afterCreateHtaccess');
1921
1922
		return true;
1923
	}
1924
1925
	/**
1926
	 * jsonDecode convert json string to php array / object
1927
	 *
1928
	 * @param string $json
1929
	 * @param boolean $assoc  (since 1.4.2.4) if true, convert to associativ array
1930
	 * @return array
1931
	 */
1932
	public static function jsonDecode($json, $assoc = false)
1933
	{
1934
		if (function_exists('json_decode'))
1935
			return json_decode($json, $assoc);
1936
		else
1937
		{
1938
			include_once(_PS_TOOL_DIR_.'json/json.php');
1939
			$pearJson = new Services_JSON(($assoc) ? SERVICES_JSON_LOOSE_TYPE : 0);
1940
			return $pearJson->decode($json);
1941
		}
1942
	}
1943
1944
	/**
1945
	 * Convert an array to json string
1946
	 *
1947
	 * @param array $data
1948
	 * @return string json
1949
	 */
1950
	public static function jsonEncode($data)
1951
	{
1952
		if (function_exists('json_encode'))
1953
			return json_encode($data);
1954
		else
1955
		{
1956
			include_once(_PS_TOOL_DIR_.'json/json.php');
1957
			$pearJson = new Services_JSON();
1958
			return $pearJson->encode($data);
1959
		}
1960
	}
1961
1962
	/**
1963
	 * Display a warning message indicating that the method is deprecated
1964
	 */
1965
	public static function displayAsDeprecated()
1966
	{
1967
		if (_PS_DISPLAY_COMPATIBILITY_WARNING_)
1968
		{
1969
			$backtrace = debug_backtrace();
1970
			$callee = next($backtrace);
1971
			trigger_error('Function <strong>'.$callee['function'].'()</strong> is deprecated in <strong>'.$callee['file'].'</strong> on line <strong>'.$callee['line'].'</strong><br />', E_USER_WARNING);
1972
1973
			$message = self::displayError('The function').' '.$callee['function'].' ('.self::displayError('Line').' '.$callee['line'].') '.self::displayError('is deprecated and will be removed in the next major version.');
1974
1975
			Logger::addLog($message, 3, $callee['class']);
1976
		}
1977
	}
1978
1979
	/**
1980
	 * Display a warning message indicating that the parameter is deprecated
1981
	 */
1982
	public static function displayParameterAsDeprecated($parameter)
1983
	{
1984
		if (_PS_DISPLAY_COMPATIBILITY_WARNING_)
1985
		{
1986
			$backtrace = debug_backtrace();
1987
			$callee = next($backtrace);
1988
			trigger_error('Parameter <strong>'.$parameter.'</strong> in function <strong>'.$callee['function'].'()</strong> is deprecated in <strong>'.$callee['file'].'</strong> on line <strong>'.$callee['Line'].'</strong><br />', E_USER_WARNING);
1989
1990
			$message = self::displayError('The parameter').' '.$parameter.' '.self::displayError(' in function ').' '.$callee['function'].' ('.self::displayError('Line').' '.$callee['Line'].') '.self::displayError('is deprecated and will be removed in the next major version.');
1991
			Logger::addLog($message, 3, $callee['class']);
1992
		}
1993
	}
1994
1995
	public static function enableCache($level = 1)
1996
	{
1997
		global $smarty;
1998
1999
		if (!Configuration::get('PS_SMARTY_CACHE'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_SMARTY_CACHE') 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...
2000
			return;
2001
		if ($smarty->force_compile == 0 AND $smarty->caching == $level)
2002
			return ;
2003
		self::$_forceCompile = (int)($smarty->force_compile);
2004
		self::$_caching = (int)($smarty->caching);
2005
		$smarty->force_compile = 0;
2006
		$smarty->caching = (int)($level);
2007
	}
2008
2009
	public static function restoreCacheSettings()
2010
	{
2011
		global $smarty;
2012
2013
		if (isset(self::$_forceCompile))
2014
			$smarty->force_compile = (int)(self::$_forceCompile);
2015
		if (isset(self::$_caching))
2016
			$smarty->caching = (int)(self::$_caching);
2017
	}
2018
2019
	public static function isCallable($function)
2020
	{
2021
		$disabled = explode(',', ini_get('disable_functions'));
2022
		return (!in_array($function, $disabled) AND is_callable($function));
2023
	}
2024
2025
	public static function pRegexp($s, $delim)
2026
	{
2027
		$s = str_replace($delim, '\\'.$delim, $s);
2028
		foreach (array('?', '[', ']', '(', ')', '{', '}', '-', '.', '+', '*', '^', '$') as $char)
2029
			$s = str_replace($char, '\\'.$char, $s);
2030
		return $s;
2031
	}
2032
2033
	public static function str_replace_once($needle , $replace, $haystack)
2034
	{
2035
		$pos = strpos($haystack, $needle);
2036
		if ($pos === false)
2037
			return $haystack;
2038
		return substr_replace($haystack, $replace, $pos, strlen($needle));
2039
	}
2040
2041
2042
	/**
2043
	 * Function property_exists does not exist in PHP < 5.1
2044
	 *
2045
	 * @param object or class $class
2046
	 * @param string $property
2047
	 * @return boolean
2048
	 */
2049
	public static function property_exists($class, $property)
2050
	{
2051
		if (function_exists('property_exists'))
2052
			return property_exists($class, $property);
2053
2054
		if (is_object($class))
2055
			$vars = get_object_vars($class);
2056
		else
2057
			$vars = get_class_vars($class);
2058
2059
		return array_key_exists($property, $vars);
2060
	}
2061
2062
	/**
2063
	 * @desc identify the version of php
2064
	 * @return string
2065
	 */
2066
	public static function checkPhpVersion()
2067
	{
2068
		$version = null;
0 ignored issues
show
Unused Code introduced by
$version is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
2069
2070
		if (defined('PHP_VERSION'))
2071
			$version = PHP_VERSION;
2072
		else
2073
			$version  = phpversion('');
2074
2075
		//Case management system of ubuntu, php version return 5.2.4-2ubuntu5.2
2076
		if (strpos($version, '-') !== false )
2077
			$version  = substr($version, 0, strpos($version, '-'));
2078
2079
		return $version;
2080
	}
2081
2082
	/**
2083
	 * @desc selection of Smarty depending on the version of php
2084
	 *
2085
	 */
2086
	public static function selectionVersionSmarty()
2087
	{
2088
		//Smarty 3 requirements PHP 5.2 +
2089
		if (strnatcmp(self::checkPhpVersion(),'5.2.0') >= 0)
2090
			Configuration::updateValue('PS_FORCE_SMARTY_2', 0);
0 ignored issues
show
Bug introduced by
The method updateValue() does not exist on Configuration. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2091
		else
2092
			Configuration::updateValue('PS_FORCE_SMARTY_2',1);
0 ignored issues
show
Bug introduced by
The method updateValue() does not exist on Configuration. Did you maybe mean update()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
2093
	}
2094
2095
	/**
2096
	 * @desc try to open a zip file in order to check if it's valid
2097
	 * @return bool success
2098
	 */
2099
	public static function ZipTest($fromFile)
2100
	{
2101
		if (class_exists('ZipArchive', false))
2102
		{
2103
			$zip = new ZipArchive();
2104
			return ($zip->open($fromFile, ZIPARCHIVE::CHECKCONS) === true);
2105
		}
2106
		else
2107
		{
2108
			require_once(dirname(__FILE__).'/../tools/pclzip/pclzip.lib.php');
2109
			$zip = new PclZip($fromFile);
2110
			return ($zip->privCheckFormat() === true);
2111
		}
2112
	}
2113
2114
	/**
2115
	 * @desc extract a zip file to the given directory
2116
	 * @return bool success
2117
	 */
2118
	public static function ZipExtract($fromFile, $toDir)
2119
	{
2120
		if (!file_exists($toDir))
2121
			mkdir($toDir, 0777);
2122
		if (class_exists('ZipArchive', false))
2123
		{
2124
			$zip = new ZipArchive();
2125
			if ($zip->open($fromFile) === true AND $zip->extractTo($toDir) AND $zip->close())
2126
				return true;
2127
			return false;
2128
		}
2129
		else
2130
		{
2131
			require_once(dirname(__FILE__).'/../tools/pclzip/pclzip.lib.php');
2132
			$zip = new PclZip($fromFile);
2133
			$list = $zip->extract(PCLZIP_OPT_PATH, $toDir);
2134
			foreach ($list as $extractedFile)
2135
				if ($extractedFile['status'] != 'ok')
2136
					return false;
2137
			return true;
2138
		}
2139
	}
2140
2141
	/**
2142
	 * Get products order field name for queries.
2143
	 *
2144
	 * @param string $type by|way
2145
	 * @param string $value If no index given, use default order from admin -> pref -> products
2146
	 */
2147
	public static function getProductsOrder($type, $value = null, $prefix = false)
2148
	{
2149
		switch ($type)
2150
		{
2151
			case 'by' :
2152
				$list = array(0 => 'name', 1 => 'price', 2 => 'date_add', 3 => 'date_upd', 4 => 'position', 5 => 'manufacturer_name', 6 => 'quantity');
2153
				$value = (is_null($value) || $value === false || $value === '') ? (int)Configuration::get('PS_PRODUCTS_ORDER_BY') : $value;
2154
				$value = (isset($list[$value])) ? $list[$value] : ((in_array($value, $list)) ? $value : 'position');
2155
				$orderByPrefix = '';
2156
				if ($prefix)
2157
				{
2158
					if ($value == 'id_product' || $value == 'date_add' || $value == 'date_upd' || $value == 'price')
2159
						$orderByPrefix = 'p.';
2160
					elseif ($value == 'name')
2161
						$orderByPrefix = 'pl.';
2162
					elseif ($value == 'manufacturer_name' && $prefix)
2163
					{
2164
						$orderByPrefix = 'm.';
2165
						$value = 'name';
2166
					}
2167
					elseif ($value == 'position' || empty($value))
2168
						$orderByPrefix = 'cp.';
2169
				}
2170
2171
				return $orderByPrefix.$value;
2172
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2173
2174
			case 'way' :
2175
				$value = (is_null($value) || $value === false || $value === '') ? (int)Configuration::get('PS_PRODUCTS_ORDER_WAY') : $value;
2176
				$list = array(0 => 'asc', 1 => 'desc');
2177
				return ((isset($list[$value])) ? $list[$value] : ((in_array($value, $list)) ? $value : 'asc'));
2178
			break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
2179
		}
2180
	}
2181
2182
	/**
2183
	 * Convert a shorthand byte value from a PHP configuration directive to an integer value
2184
	 * @param string $value value to convert
2185
	 * @return int
2186
	 */
2187
	public static function convertBytes($value)
2188
	{
2189
		if (is_numeric($value))
2190
		{
2191
			return $value;
2192
		}
2193
		else
2194
		{
2195
			$value_length = strlen($value);
2196
			$qty = substr($value, 0, $value_length - 1 );
2197
			$unit = strtolower(substr($value, $value_length - 1));
2198
			switch ($unit)
2199
			{
2200
				case 'k':
2201
					$qty *= 1024;
2202
					break;
2203
				case 'm':
2204
					$qty *= 1048576;
2205
					break;
2206
				case 'g':
2207
					$qty *= 1073741824;
2208
					break;
2209
			}
2210
			return $qty;
2211
		}
2212
	}
2213
2214
	public static function display404Error()
2215
	{
2216
		header('HTTP/1.1 404 Not Found');
2217
		header('Status: 404 Not Found');
2218
		include(dirname(__FILE__).'/../404.php');
2219
		die;
0 ignored issues
show
Coding Style Compatibility introduced by
The method display404Error() 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...
2220
	}
2221
2222
	/**
2223
	 * Display error and dies or silently log the error.
2224
	 *
2225
	 * @param string $msg
2226
	 * @param bool $die
2227
	 * @return success of logging
2228
	 */
2229
	public static function dieOrLog($msg, $die = true)
2230
	{
2231
		if ($die || (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_))
2232
			die($msg);
0 ignored issues
show
Coding Style Compatibility introduced by
The method dieOrLog() 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...
2233
		return Logger::addLog($msg);
2234
	}
2235
2236
	/**
2237
	 * Clear cache for Smarty
2238
	 *
2239
	 * @param objet $smarty
2240
	 */
2241
	public static function clearCache($smarty)
2242
	{
2243
		if (!Configuration::get('PS_FORCE_SMARTY_2'))
0 ignored issues
show
Bug Best Practice introduced by
The expression \Configuration::get('PS_FORCE_SMARTY_2') 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...
2244
			$smarty->clearAllCache();
2245
		else
2246
			$smarty->clear_all_cache();
2247
	}
2248
2249
	/**
2250
	 * getMemoryLimit allow to get the memory limit in octet
2251
	 *
2252
	 * @since 1.4.5.0
2253
	 * @return int the memory limit value in octet
2254
	 */
2255
	public static function getMemoryLimit()
2256
	{
2257
		$memory_limit = @ini_get('memory_limit');
2258
2259
		if (preg_match('/[0-9]+k/i', $memory_limit))
2260
			return 1024 * (int)$memory_limit;
2261
2262
		if (preg_match('/[0-9]+m/i', $memory_limit))
2263
			return 1024 * 1024 * (int)$memory_limit;
2264
2265
		if (preg_match('/[0-9]+g/i', $memory_limit))
2266
			return 1024 * 1024 * 1024 * (int)$memory_limit;
2267
2268
		return $memory_limit;
2269
	}
2270
2271
	public static function isX86_64arch()
2272
	{
2273
		return (PHP_INT_MAX == '9223372036854775807');
2274
	}
2275
2276
	/**
2277
	 * apacheModExists return true if the apache module $name is loaded
2278
	 * @TODO move this method in class Information (when it will exist)
2279
	 *
2280
	 * @param string $name module name
2281
	 * @return boolean true if exists
2282
	 * @since 1.4.5.0
2283
	 */
2284
	public static function apacheModExists($name)
2285
	{
2286
		if(function_exists('apache_get_modules'))
2287
		{
2288
			static $apacheModuleList = null;
2289
2290
			if (!is_array($apacheModuleList))
2291
				$apacheModuleList = apache_get_modules();
2292
2293
			// we need strpos (example, evasive can be evasive20)
2294
			foreach($apacheModuleList as $module)
2295
			{
2296
				if (strpos($module, $name) !== false)
2297
					return true;
2298
			}
2299
		}
2300
		else{
2301
			// If apache_get_modules does not exists,
2302
			// one solution should be parsing httpd.conf,
2303
			// but we could simple parse phpinfo(INFO_MODULES) return string
2304
			ob_start();
2305
			phpinfo(INFO_MODULES);
2306
			$phpinfo = ob_get_contents();
2307
			ob_end_clean();
2308
			if (strpos($phpinfo, $name) !== false)
2309
				return true;
2310
		}
2311
2312
		return false;
2313
	}
2314
}
2315
2316
/**
2317
* Compare 2 prices to sort products
2318
*
2319
* @param float $a
2320
* @param float $b
2321
* @return integer
2322
*/
2323
/* Externalized because of a bug in PHP 5.1.6 when inside an object */
2324
function cmpPriceAsc($a,$b)
2325
{
2326
	if ((float)($a['price_tmp']) < (float)($b['price_tmp']))
2327
		return (-1);
2328
	elseif ((float)($a['price_tmp']) > (float)($b['price_tmp']))
2329
		return (1);
2330
	return (0);
2331
}
2332
2333
function cmpPriceDesc($a,$b)
2334
{
2335
	if ((float)($a['price_tmp']) < (float)($b['price_tmp']))
2336
		return (1);
2337
	elseif ((float)($a['price_tmp']) > (float)($b['price_tmp']))
2338
		return (-1);
2339
	return (0);
2340
}
2341
2342