PageRenderTime 67ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/classes/Tools.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 3335 lines | 2441 code | 313 blank | 581 comment | 461 complexity | 9c817d8a1bc923d233ad9f4b718dda70 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * 2007-2014 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 license@prestashop.com 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 <contact@prestashop.com>
  22. * @copyright 2007-2014 PrestaShop SA
  23. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  24. * International Registered Trademark & Property of PrestaShop SA
  25. */
  26. class ToolsCore
  27. {
  28. protected static $file_exists_cache = array();
  29. protected static $_forceCompile;
  30. protected static $_caching;
  31. protected static $_user_plateform;
  32. protected static $_user_browser;
  33. public static $round_mode = null;
  34. /**
  35. * Random password generator
  36. *
  37. * @param integer $length Desired length (optional)
  38. * @param string $flag Output type (NUMERIC, ALPHANUMERIC, NO_NUMERIC)
  39. * @return string Password
  40. */
  41. public static function passwdGen($length = 8, $flag = 'ALPHANUMERIC')
  42. {
  43. switch ($flag)
  44. {
  45. case 'NUMERIC':
  46. $str = '0123456789';
  47. break;
  48. case 'NO_NUMERIC':
  49. $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  50. break;
  51. default:
  52. $str = 'abcdefghijkmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  53. break;
  54. }
  55. for ($i = 0, $passwd = ''; $i < $length; $i++)
  56. $passwd .= Tools::substr($str, mt_rand(0, Tools::strlen($str) - 1), 1);
  57. return $passwd;
  58. }
  59. public static function strReplaceFirst($search, $replace, $subject, $cur = 0)
  60. {
  61. return (strpos($subject, $search, $cur))?substr_replace($subject, $replace, (int)strpos($subject, $search, $cur), strlen($search)):$subject;
  62. }
  63. /**
  64. * Redirect user to another page
  65. *
  66. * @param string $url Desired URL
  67. * @param string $baseUri Base URI (optional)
  68. * @param Link $link
  69. * @param string|array $headers A list of headers to send before redirection
  70. */
  71. public static function redirect($url, $base_uri = __PS_BASE_URI__, Link $link = null, $headers = null)
  72. {
  73. if (!$link)
  74. $link = Context::getContext()->link;
  75. if (strpos($url, 'http://') === false && strpos($url, 'https://') === false && $link)
  76. {
  77. if (strpos($url, $base_uri) === 0)
  78. $url = substr($url, strlen($base_uri));
  79. if (strpos($url, 'index.php?controller=') !== false && strpos($url, 'index.php/') == 0)
  80. {
  81. $url = substr($url, strlen('index.php?controller='));
  82. if (Configuration::get('PS_REWRITING_SETTINGS'))
  83. $url = Tools::strReplaceFirst('&', '?', $url);
  84. }
  85. $explode = explode('?', $url);
  86. // don't use ssl if url is home page
  87. // used when logout for example
  88. $use_ssl = !empty($url);
  89. $url = $link->getPageLink($explode[0], $use_ssl);
  90. if (isset($explode[1]))
  91. $url .= '?'.$explode[1];
  92. }
  93. // Send additional headers
  94. if ($headers)
  95. {
  96. if (!is_array($headers))
  97. $headers = array($headers);
  98. foreach ($headers as $header)
  99. header($header);
  100. }
  101. header('Location: '.$url);
  102. exit;
  103. }
  104. /**
  105. * Redirect URLs already containing PS_BASE_URI
  106. *
  107. * @param string $url Desired URL
  108. */
  109. public static function redirectLink($url)
  110. {
  111. if (!preg_match('@^https?://@i', $url))
  112. {
  113. if (strpos($url, __PS_BASE_URI__) !== false && strpos($url, __PS_BASE_URI__) == 0)
  114. $url = substr($url, strlen(__PS_BASE_URI__));
  115. if (strpos($url, 'index.php?controller=') !== false && strpos($url, 'index.php/') == 0)
  116. $url = substr($url, strlen('index.php?controller='));
  117. $explode = explode('?', $url);
  118. $url = Context::getContext()->link->getPageLink($explode[0]);
  119. if (isset($explode[1]))
  120. $url .= '?'.$explode[1];
  121. }
  122. header('Location: '.$url);
  123. exit;
  124. }
  125. /**
  126. * Redirect user to another admin page
  127. *
  128. * @param string $url Desired URL
  129. */
  130. public static function redirectAdmin($url)
  131. {
  132. header('Location: '.$url);
  133. exit;
  134. }
  135. /**
  136. * getShopProtocol return the available protocol for the current shop in use
  137. * SSL if Configuration is set on and available for the server
  138. * @static
  139. * @return String
  140. */
  141. public static function getShopProtocol()
  142. {
  143. $protocol = (Configuration::get('PS_SSL_ENABLED') || (!empty($_SERVER['HTTPS'])
  144. && Tools::strtolower($_SERVER['HTTPS']) != 'off')) ? 'https://' : 'http://';
  145. return $protocol;
  146. }
  147. /**
  148. * getProtocol return the set protocol according to configuration (http[s])
  149. * @param bool $use_ssl true if require ssl
  150. * @return String (http|https)
  151. */
  152. public static function getProtocol($use_ssl = null)
  153. {
  154. return (!is_null($use_ssl) && $use_ssl ? 'https://' : 'http://');
  155. }
  156. /**
  157. * getHttpHost return the <b>current</b> host used, with the protocol (http or https) if $http is true
  158. * This function should not be used to choose http or https domain name.
  159. * Use Tools::getShopDomain() or Tools::getShopDomainSsl instead
  160. *
  161. * @param boolean $http
  162. * @param boolean $entities
  163. * @return string host
  164. */
  165. public static function getHttpHost($http = false, $entities = false, $ignore_port = false)
  166. {
  167. $host = (isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : $_SERVER['HTTP_HOST']);
  168. if ($ignore_port && $pos = strpos($host, ':'))
  169. $host = substr($host, 0, $pos);
  170. if ($entities)
  171. $host = htmlspecialchars($host, ENT_COMPAT, 'UTF-8');
  172. if ($http)
  173. $host = (Configuration::get('PS_SSL_ENABLED') ? 'https://' : 'http://').$host;
  174. return $host;
  175. }
  176. /**
  177. * getShopDomain returns domain name according to configuration and ignoring ssl
  178. *
  179. * @param boolean $http if true, return domain name with protocol
  180. * @param boolean $entities if true,
  181. * @return string domain
  182. */
  183. public static function getShopDomain($http = false, $entities = false)
  184. {
  185. if (!$domain = ShopUrl::getMainShopDomain())
  186. $domain = Tools::getHttpHost();
  187. if ($entities)
  188. $domain = htmlspecialchars($domain, ENT_COMPAT, 'UTF-8');
  189. if ($http)
  190. $domain = 'http://'.$domain;
  191. return $domain;
  192. }
  193. /**
  194. * getShopDomainSsl returns domain name according to configuration and depending on ssl activation
  195. *
  196. * @param boolean $http if true, return domain name with protocol
  197. * @param boolean $entities if true,
  198. * @return string domain
  199. */
  200. public static function getShopDomainSsl($http = false, $entities = false)
  201. {
  202. if (!$domain = ShopUrl::getMainShopDomainSSL())
  203. $domain = Tools::getHttpHost();
  204. if ($entities)
  205. $domain = htmlspecialchars($domain, ENT_COMPAT, 'UTF-8');
  206. if ($http)
  207. $domain = (Configuration::get('PS_SSL_ENABLED') ? 'https://' : 'http://').$domain;
  208. return $domain;
  209. }
  210. /**
  211. * Get the server variable SERVER_NAME
  212. *
  213. * @return string server name
  214. */
  215. public static function getServerName()
  216. {
  217. if (isset($_SERVER['HTTP_X_FORWARDED_SERVER']) && $_SERVER['HTTP_X_FORWARDED_SERVER'])
  218. return $_SERVER['HTTP_X_FORWARDED_SERVER'];
  219. return $_SERVER['SERVER_NAME'];
  220. }
  221. /**
  222. * Get the server variable REMOTE_ADDR, or the first ip of HTTP_X_FORWARDED_FOR (when using proxy)
  223. *
  224. * @return string $remote_addr ip of client
  225. */
  226. public static function getRemoteAddr()
  227. {
  228. // This condition is necessary when using CDN, don't remove it.
  229. if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] && (!isset($_SERVER['REMOTE_ADDR'])
  230. || preg_match('/^127\..*/i', trim($_SERVER['REMOTE_ADDR'])) || preg_match('/^172\.16.*/i', trim($_SERVER['REMOTE_ADDR']))
  231. || preg_match('/^192\.168\.*/i', trim($_SERVER['REMOTE_ADDR'])) || preg_match('/^10\..*/i', trim($_SERVER['REMOTE_ADDR']))))
  232. {
  233. if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ','))
  234. {
  235. $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
  236. return $ips[0];
  237. }
  238. else
  239. return $_SERVER['HTTP_X_FORWARDED_FOR'];
  240. }
  241. return $_SERVER['REMOTE_ADDR'];
  242. }
  243. /**
  244. * Check if the current page use SSL connection on not
  245. *
  246. * @return bool uses SSL
  247. */
  248. public static function usingSecureMode()
  249. {
  250. if (isset($_SERVER['HTTPS']))
  251. return in_array(Tools::strtolower($_SERVER['HTTPS']), array(1, 'on'));
  252. // $_SERVER['SSL'] exists only in some specific configuration
  253. if (isset($_SERVER['SSL']))
  254. return in_array(Tools::strtolower($_SERVER['SSL']), array(1, 'on'));
  255. // $_SERVER['REDIRECT_HTTPS'] exists only in some specific configuration
  256. if (isset($_SERVER['REDIRECT_HTTPS']))
  257. return in_array(Tools::strtolower($_SERVER['REDIRECT_HTTPS']), array(1, 'on'));
  258. if (isset($_SERVER['HTTP_SSL']))
  259. return in_array(Tools::strtolower($_SERVER['HTTP_SSL']), array(1, 'on'));
  260. if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']))
  261. return Tools::strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https';
  262. return false;
  263. }
  264. /**
  265. * Get the current url prefix protocol (https/http)
  266. *
  267. * @return string protocol
  268. */
  269. public static function getCurrentUrlProtocolPrefix()
  270. {
  271. if (Tools::usingSecureMode())
  272. return 'https://';
  273. else
  274. return 'http://';
  275. }
  276. /**
  277. * Secure an URL referrer
  278. *
  279. * @param string $referrer URL referrer
  280. * @return string secured referrer
  281. */
  282. public static function secureReferrer($referrer)
  283. {
  284. if (preg_match('/^http[s]?:\/\/'.Tools::getServerName().'(:'._PS_SSL_PORT_.')?\/.*$/Ui', $referrer))
  285. return $referrer;
  286. return __PS_BASE_URI__;
  287. }
  288. /**
  289. * Get a value from $_POST / $_GET
  290. * if unavailable, take a default value
  291. *
  292. * @param string $key Value key
  293. * @param mixed $default_value (optional)
  294. * @return mixed Value
  295. */
  296. public static function getValue($key, $default_value = false)
  297. {
  298. if (!isset($key) || empty($key) || !is_string($key))
  299. return false;
  300. $ret = (isset($_POST[$key]) ? $_POST[$key] : (isset($_GET[$key]) ? $_GET[$key] : $default_value));
  301. if (is_string($ret))
  302. return stripslashes(urldecode(preg_replace('/((\%5C0+)|(\%00+))/i', '', urlencode($ret))));
  303. return $ret;
  304. }
  305. public static function getIsset($key)
  306. {
  307. if (!isset($key) || empty($key) || !is_string($key))
  308. return false;
  309. return isset($_POST[$key]) ? true : (isset($_GET[$key]) ? true : false);
  310. }
  311. /**
  312. * Change language in cookie while clicking on a flag
  313. *
  314. * @return string iso code
  315. */
  316. public static function setCookieLanguage($cookie = null)
  317. {
  318. if (!$cookie)
  319. $cookie = Context::getContext()->cookie;
  320. /* If language does not exist or is disabled, erase it */
  321. if ($cookie->id_lang)
  322. {
  323. $lang = new Language((int)$cookie->id_lang);
  324. if (!Validate::isLoadedObject($lang) || !$lang->active || !$lang->isAssociatedToShop())
  325. $cookie->id_lang = null;
  326. }
  327. if (!Configuration::get('PS_DETECT_LANG'))
  328. unset($cookie->detect_language);
  329. /* Automatically detect language if not already defined, detect_language is set in Cookie::update */
  330. if (!Tools::getValue('isolang') && !Tools::getValue('id_lang') && (!$cookie->id_lang || isset($cookie->detect_language))
  331. && isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  332. {
  333. $array = explode(',', Tools::strtolower($_SERVER['HTTP_ACCEPT_LANGUAGE']));
  334. $string = $array[0];
  335. if (Validate::isLanguageCode($string))
  336. {
  337. $lang = Language::getLanguageByIETFCode($string);
  338. if (Validate::isLoadedObject($lang) && $lang->active && $lang->isAssociatedToShop())
  339. {
  340. Context::getContext()->language = $lang;
  341. $cookie->id_lang = (int)$lang->id;
  342. }
  343. }
  344. }
  345. if (isset($cookie->detect_language))
  346. unset($cookie->detect_language);
  347. /* If language file not present, you must use default language file */
  348. if (!$cookie->id_lang || !Validate::isUnsignedId($cookie->id_lang))
  349. $cookie->id_lang = (int)Configuration::get('PS_LANG_DEFAULT');
  350. $iso = Language::getIsoById((int)$cookie->id_lang);
  351. @include_once(_PS_THEME_DIR_.'lang/'.$iso.'.php');
  352. return $iso;
  353. }
  354. /**
  355. * Set cookie id_lang
  356. */
  357. public static function switchLanguage(Context $context = null)
  358. {
  359. if (!$context)
  360. $context = Context::getContext();
  361. // Install call the dispatcher and so the switchLanguage
  362. // Stop this method by checking the cookie
  363. if (!isset($context->cookie))
  364. return;
  365. if (($iso = Tools::getValue('isolang')) && Validate::isLanguageIsoCode($iso) && ($id_lang = (int)Language::getIdByIso($iso)))
  366. $_GET['id_lang'] = $id_lang;
  367. // update language only if new id is different from old id
  368. // or if default language changed
  369. $cookie_id_lang = $context->cookie->id_lang;
  370. $configuration_id_lang = Configuration::get('PS_LANG_DEFAULT');
  371. if ((($id_lang = (int)Tools::getValue('id_lang')) && Validate::isUnsignedId($id_lang) && $cookie_id_lang != (int)$id_lang)
  372. || (($id_lang == $configuration_id_lang) && Validate::isUnsignedId($id_lang) && $id_lang != $cookie_id_lang))
  373. {
  374. $context->cookie->id_lang = $id_lang;
  375. $language = new Language($id_lang);
  376. if (Validate::isLoadedObject($language) && $language->active)
  377. $context->language = $language;
  378. $params = $_GET;
  379. if (Configuration::get('PS_REWRITING_SETTINGS') || !Language::isMultiLanguageActivated())
  380. unset($params['id_lang']);
  381. }
  382. }
  383. public static function getCountry($address = null)
  384. {
  385. if ($id_country = Tools::getValue('id_country'));
  386. elseif (isset($address) && isset($address->id_country) && $address->id_country)
  387. $id_country = $address->id_country;
  388. elseif (Configuration::get('PS_DETECT_COUNTRY') && isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  389. {
  390. preg_match('#(?<=-)\w\w|\w\w(?!-)#', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $array);
  391. if (is_array($array) && isset($array[0]) && Validate::isLanguageIsoCode($array[0]))
  392. $id_country = Country::getByIso($array[0], true);
  393. }
  394. if (!isset($id_country) || !$id_country)
  395. $id_country = Configuration::get('PS_COUNTRY_DEFAULT');
  396. return (int)$id_country;
  397. }
  398. /**
  399. * Set cookie currency from POST or default currency
  400. *
  401. * @return Currency object
  402. */
  403. public static function setCurrency($cookie)
  404. {
  405. if (Tools::isSubmit('SubmitCurrency') && ($id_currency = Tools::getValue('id_currency')))
  406. {
  407. $currency = Currency::getCurrencyInstance((int)$id_currency);
  408. if (is_object($currency) && $currency->id && !$currency->deleted && $currency->isAssociatedToShop())
  409. $cookie->id_currency = (int)$currency->id;
  410. }
  411. $currency = null;
  412. if ((int)$cookie->id_currency)
  413. $currency = Currency::getCurrencyInstance((int)$cookie->id_currency);
  414. if (!Validate::isLoadedObject($currency) || (bool)$currency->deleted || !(bool)$currency->active)
  415. $currency = Currency::getCurrencyInstance(Configuration::get('PS_CURRENCY_DEFAULT'));
  416. $cookie->id_currency = (int)$currency->id;
  417. if ($currency->isAssociatedToShop())
  418. return $currency;
  419. else
  420. {
  421. // get currency from context
  422. $currency = Shop::getEntityIds('currency', Context::getContext()->shop->id, true, true);
  423. if (isset($currency[0]) && $currency[0]['id_currency'])
  424. {
  425. $cookie->id_currency = $currency[0]['id_currency'];
  426. return Currency::getCurrencyInstance((int)$cookie->id_currency);
  427. }
  428. }
  429. return $currency;
  430. }
  431. /**
  432. * Return price with currency sign for a given product
  433. *
  434. * @param float $price Product price
  435. * @param object|array $currency Current currency (object, id_currency, NULL => context currency)
  436. * @return string Price correctly formated (sign, decimal separator...)
  437. */
  438. public static function displayPrice($price, $currency = null, $no_utf8 = false, Context $context = null)
  439. {
  440. if (!is_numeric($price))
  441. return $price;
  442. if (!$context)
  443. $context = Context::getContext();
  444. if ($currency === null)
  445. $currency = $context->currency;
  446. // if you modified this function, don't forget to modify the Javascript function formatCurrency (in tools.js)
  447. elseif (is_int($currency))
  448. $currency = Currency::getCurrencyInstance((int)$currency);
  449. if (is_array($currency))
  450. {
  451. $c_char = $currency['sign'];
  452. $c_format = $currency['format'];
  453. $c_decimals = (int)$currency['decimals'] * _PS_PRICE_DISPLAY_PRECISION_;
  454. $c_blank = $currency['blank'];
  455. }
  456. elseif (is_object($currency))
  457. {
  458. $c_char = $currency->sign;
  459. $c_format = $currency->format;
  460. $c_decimals = (int)$currency->decimals * _PS_PRICE_DISPLAY_PRECISION_;
  461. $c_blank = $currency->blank;
  462. }
  463. else
  464. return false;
  465. $blank = ($c_blank ? ' ' : '');
  466. $ret = 0;
  467. if (($is_negative = ($price < 0)))
  468. $price *= -1;
  469. $price = Tools::ps_round($price, $c_decimals);
  470. /*
  471. * If the language is RTL and the selected currency format contains spaces as thousands separator
  472. * then the number will be printed in reverse since the space is interpreted as separating words.
  473. * To avoid this we replace the currency format containing a space with the one containing a comma (,) as thousand
  474. * separator when the language is RTL.
  475. *
  476. * TODO: This is not ideal, a currency format should probably be tied to a language, not to a currency.
  477. */
  478. if (($c_format == 2) && ($context->language->is_rtl == 1))
  479. $c_format = 4;
  480. switch ($c_format)
  481. {
  482. /* X 0,000.00 */
  483. case 1:
  484. $ret = $c_char.$blank.number_format($price, $c_decimals, '.', ',');
  485. break;
  486. /* 0 000,00 X*/
  487. case 2:
  488. $ret = number_format($price, $c_decimals, ',', ' ').$blank.$c_char;
  489. break;
  490. /* X 0.000,00 */
  491. case 3:
  492. $ret = $c_char.$blank.number_format($price, $c_decimals, ',', '.');
  493. break;
  494. /* 0,000.00 X */
  495. case 4:
  496. $ret = number_format($price, $c_decimals, '.', ',').$blank.$c_char;
  497. break;
  498. /* X 0'000.00 Added for the switzerland currency */
  499. case 5:
  500. $ret = $c_char.$blank.number_format($price, $c_decimals, '.', "'");
  501. break;
  502. }
  503. if ($is_negative)
  504. $ret = '-'.$ret;
  505. if ($no_utf8)
  506. return str_replace('€', chr(128), $ret);
  507. return $ret;
  508. }
  509. /* Just to fix a bug
  510. * Need real CLDR functions
  511. */
  512. public static function displayNumber($number, $currency)
  513. {
  514. if (is_array($currency))
  515. $format = $currency['format'];
  516. elseif (is_object($currency))
  517. $format = $currency->format;
  518. return number_format($number, 0, '.', in_array($format, array(1, 4)) ? ',': ' ');
  519. }
  520. public static function displayPriceSmarty($params, &$smarty)
  521. {
  522. if (array_key_exists('currency', $params))
  523. {
  524. $currency = Currency::getCurrencyInstance((int)$params['currency']);
  525. if (Validate::isLoadedObject($currency))
  526. return Tools::displayPrice($params['price'], $currency, false);
  527. }
  528. return Tools::displayPrice($params['price']);
  529. }
  530. /**
  531. * Return price converted
  532. *
  533. * @param float $price Product price
  534. * @param object|array $currency Current currency object
  535. * @param boolean $to_currency convert to currency or from currency to default currency
  536. * @param Context $context
  537. * @return float Price
  538. */
  539. public static function convertPrice($price, $currency = null, $to_currency = true, Context $context = null)
  540. {
  541. static $default_currency = null;
  542. if ($default_currency === null)
  543. $default_currency = (int)Configuration::get('PS_CURRENCY_DEFAULT');
  544. if (!$context)
  545. $context = Context::getContext();
  546. if ($currency === null)
  547. $currency = $context->currency;
  548. elseif (is_numeric($currency))
  549. $currency = Currency::getCurrencyInstance($currency);
  550. $c_id = (is_array($currency) ? $currency['id_currency'] : $currency->id);
  551. $c_rate = (is_array($currency) ? $currency['conversion_rate'] : $currency->conversion_rate);
  552. if ($c_id != $default_currency)
  553. {
  554. if ($to_currency)
  555. $price *= $c_rate;
  556. else
  557. $price /= $c_rate;
  558. }
  559. return $price;
  560. }
  561. /**
  562. *
  563. * Convert amount from a currency to an other currency automatically
  564. * @param float $amount
  565. * @param Currency $currency_from if null we used the default currency
  566. * @param Currency $currency_to if null we used the default currency
  567. */
  568. public static function convertPriceFull($amount, Currency $currency_from = null, Currency $currency_to = null)
  569. {
  570. if ($currency_from === $currency_to)
  571. return $amount;
  572. if ($currency_from === null)
  573. $currency_from = new Currency(Configuration::get('PS_CURRENCY_DEFAULT'));
  574. if ($currency_to === null)
  575. $currency_to = new Currency(Configuration::get('PS_CURRENCY_DEFAULT'));
  576. if ($currency_from->id == Configuration::get('PS_CURRENCY_DEFAULT'))
  577. $amount *= $currency_to->conversion_rate;
  578. else
  579. {
  580. $conversion_rate = ($currency_from->conversion_rate == 0 ? 1 : $currency_from->conversion_rate);
  581. // Convert amount to default currency (using the old currency rate)
  582. $amount = $amount / $conversion_rate;
  583. // Convert to new currency
  584. $amount *= $currency_to->conversion_rate;
  585. }
  586. return Tools::ps_round($amount, _PS_PRICE_COMPUTE_PRECISION_);
  587. }
  588. /**
  589. * Display date regarding to language preferences
  590. *
  591. * @param array $params Date, format...
  592. * @param object $smarty Smarty object for language preferences
  593. * @return string Date
  594. */
  595. public static function dateFormat($params, &$smarty)
  596. {
  597. return Tools::displayDate($params['date'], null, (isset($params['full']) ? $params['full'] : false));
  598. }
  599. /**
  600. * Display date regarding to language preferences
  601. *
  602. * @param string $date Date to display format UNIX
  603. * @param integer $id_lang Language id DEPRECATED
  604. * @param boolean $full With time or not (optional)
  605. * @param string $separator DEPRECATED
  606. * @return string Date
  607. */
  608. public static function displayDate($date, $id_lang = null, $full = false, $separator = null)
  609. {
  610. if ($id_lang !== null)
  611. Tools::displayParameterAsDeprecated('id_lang');
  612. if ($separator !== null)
  613. Tools::displayParameterAsDeprecated('separator');
  614. if (!$date || !($time = strtotime($date)))
  615. return $date;
  616. if ($date == '0000-00-00 00:00:00' || $date == '0000-00-00')
  617. return '';
  618. if (!Validate::isDate($date) || !Validate::isBool($full))
  619. throw new PrestaShopException('Invalid date');
  620. $context = Context::getContext();
  621. $date_format = ($full ? $context->language->date_format_full : $context->language->date_format_lite);
  622. return date($date_format, $time);
  623. }
  624. /**
  625. * Sanitize a string
  626. *
  627. * @param string $string String to sanitize
  628. * @param boolean $full String contains HTML or not (optional)
  629. * @return string Sanitized string
  630. */
  631. public static function safeOutput($string, $html = false)
  632. {
  633. if (!$html)
  634. $string = strip_tags($string);
  635. return @Tools::htmlentitiesUTF8($string, ENT_QUOTES);
  636. }
  637. public static function htmlentitiesUTF8($string, $type = ENT_QUOTES)
  638. {
  639. if (is_array($string))
  640. return array_map(array('Tools', 'htmlentitiesUTF8'), $string);
  641. return htmlentities((string)$string, $type, 'utf-8');
  642. }
  643. public static function htmlentitiesDecodeUTF8($string)
  644. {
  645. if (is_array($string))
  646. {
  647. $string = array_map(array('Tools', 'htmlentitiesDecodeUTF8'), $string);
  648. return (string)array_shift($string);
  649. }
  650. return html_entity_decode((string)$string, ENT_QUOTES, 'utf-8');
  651. }
  652. public static function safePostVars()
  653. {
  654. if (!isset($_POST) || !is_array($_POST))
  655. $_POST = array();
  656. else
  657. $_POST = array_map(array('Tools', 'htmlentitiesUTF8'), $_POST);
  658. }
  659. /**
  660. * Delete directory and subdirectories
  661. *
  662. * @param string $dirname Directory name
  663. */
  664. public static function deleteDirectory($dirname, $delete_self = true)
  665. {
  666. $dirname = rtrim($dirname, '/').'/';
  667. if (file_exists($dirname))
  668. if ($files = scandir($dirname))
  669. {
  670. foreach ($files as $file)
  671. if ($file != '.' && $file != '..' && $file != '.svn')
  672. {
  673. if (is_dir($dirname.$file))
  674. Tools::deleteDirectory($dirname.$file, true);
  675. elseif (file_exists($dirname.$file))
  676. {
  677. @chmod($dirname.$file, 0777); // NT ?
  678. unlink($dirname.$file);
  679. }
  680. }
  681. if ($delete_self && file_exists($dirname))
  682. if (!rmdir($dirname))
  683. {
  684. @chmod($dirname, 0777); // NT ?
  685. return false;
  686. }
  687. return true;
  688. }
  689. return false;
  690. }
  691. /**
  692. * Delete file
  693. *
  694. * @param string File path
  695. * @param array Excluded files
  696. */
  697. public static function deleteFile($file, $exclude_files = array())
  698. {
  699. if (isset($exclude_files) && !is_array($exclude_files))
  700. $exclude_files = array($exclude_files);
  701. if (file_exists($file) && is_file($file) && array_search(basename($file), $exclude_files) === FALSE)
  702. {
  703. @chmod($dirname.$file, 0777); // NT ?
  704. unlink($file);
  705. }
  706. }
  707. /**
  708. * Clear XML cache folder
  709. */
  710. public static function clearXMLCache()
  711. {
  712. $themes = array();
  713. foreach (Theme::getThemes() as $theme)
  714. $themes[] = $theme->directory;
  715. foreach (scandir(_PS_ROOT_DIR_.'/config/xml') as $file)
  716. {
  717. $path_info = pathinfo($file, PATHINFO_EXTENSION);
  718. if (($path_info == 'xml') && ($file != 'default.xml') && !in_array(basename($file, '.'.$path_info), $themes))
  719. self::deleteFile(_PS_ROOT_DIR_.'/config/xml/'.$file);
  720. }
  721. }
  722. /**
  723. * Display an error according to an error code
  724. *
  725. * @param string $string Error message
  726. * @param boolean $htmlentities By default at true for parsing error message with htmlentities
  727. */
  728. public static function displayError($string = 'Fatal error', $htmlentities = true, Context $context = null)
  729. {
  730. global $_ERRORS;
  731. if (is_null($context))
  732. $context = Context::getContext();
  733. @include_once(_PS_TRANSLATIONS_DIR_.$context->language->iso_code.'/errors.php');
  734. if (defined('_PS_MODE_DEV_') && _PS_MODE_DEV_ && $string == 'Fatal error')
  735. return ('<pre>'.print_r(debug_backtrace(), true).'</pre>');
  736. if (!is_array($_ERRORS))
  737. return $htmlentities ? Tools::htmlentitiesUTF8($string) : $string;
  738. $key = md5(str_replace('\'', '\\\'', $string));
  739. $str = (isset($_ERRORS) && is_array($_ERRORS) && array_key_exists($key, $_ERRORS)) ? $_ERRORS[$key] : $string;
  740. return $htmlentities ? Tools::htmlentitiesUTF8(stripslashes($str)) : $str;
  741. }
  742. /**
  743. * Display an error with detailed object
  744. *
  745. * @param mixed $object
  746. * @param boolean $kill
  747. * @return $object if $kill = false;
  748. */
  749. public static function dieObject($object, $kill = true)
  750. {
  751. echo '<xmp style="text-align: left;">';
  752. print_r($object);
  753. echo '</xmp><br />';
  754. if ($kill)
  755. die('END');
  756. return $object;
  757. }
  758. /**
  759. * Display a var dump in firebug console
  760. *
  761. * @param object $object Object to display
  762. */
  763. public static function fd($object, $type = 'log')
  764. {
  765. $types = array('log', 'debug', 'info', 'warn', 'error', 'assert');
  766. if (!in_array($type, $types))
  767. $type = 'log';
  768. echo '
  769. <script type="text/javascript">
  770. console.'.$type.'('.Tools::jsonEncode($object).');
  771. </script>
  772. ';
  773. }
  774. /**
  775. * ALIAS OF dieObject() - Display an error with detailed object
  776. *
  777. * @param object $object Object to display
  778. */
  779. public static function d($object, $kill = true)
  780. {
  781. return (Tools::dieObject($object, $kill));
  782. }
  783. public static function debug_backtrace($start = 0, $limit = null)
  784. {
  785. $backtrace = debug_backtrace();
  786. array_shift($backtrace);
  787. for ($i = 0; $i < $start; ++$i)
  788. array_shift($backtrace);
  789. echo '
  790. <div style="margin:10px;padding:10px;border:1px solid #666666">
  791. <ul>';
  792. $i = 0;
  793. foreach ($backtrace as $id => $trace)
  794. {
  795. if ((int)$limit && (++$i > $limit ))
  796. break;
  797. $relative_file = (isset($trace['file'])) ? 'in /'.ltrim(str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']), '/') : '';
  798. $current_line = (isset($trace['line'])) ? ':'.$trace['line'] : '';
  799. echo '<li>
  800. <b>'.((isset($trace['class'])) ? $trace['class'] : '').((isset($trace['type'])) ? $trace['type'] : '').$trace['function'].'</b>
  801. '.$relative_file.$current_line.'
  802. </li>';
  803. }
  804. echo '</ul>
  805. </div>';
  806. }
  807. /**
  808. * ALIAS OF dieObject() - Display an error with detailed object but don't stop the execution
  809. *
  810. * @param object $object Object to display
  811. */
  812. public static function p($object)
  813. {
  814. return (Tools::dieObject($object, false));
  815. }
  816. /**
  817. * Check if submit has been posted
  818. *
  819. * @param string $submit submit name
  820. */
  821. public static function isSubmit($submit)
  822. {
  823. return (
  824. isset($_POST[$submit]) || isset($_POST[$submit.'_x']) || isset($_POST[$submit.'_y'])
  825. || isset($_GET[$submit]) || isset($_GET[$submit.'_x']) || isset($_GET[$submit.'_y'])
  826. );
  827. }
  828. /**
  829. * @deprecated 1.5.0
  830. */
  831. public static function getMetaTags($id_lang, $page_name, $title = '')
  832. {
  833. Tools::displayAsDeprecated();
  834. return Meta::getMetaTags($id_lang, $page_name, $title);
  835. }
  836. /**
  837. * @deprecated 1.5.0
  838. */
  839. public static function getHomeMetaTags($id_lang, $page_name)
  840. {
  841. Tools::displayAsDeprecated();
  842. return Meta::getHomeMetas($id_lang, $page_name);
  843. }
  844. /**
  845. * @deprecated 1.5.0
  846. */
  847. public static function completeMetaTags($meta_tags, $default_value, Context $context = null)
  848. {
  849. Tools::displayAsDeprecated();
  850. return Meta::completeMetaTags($meta_tags, $default_value, $context);
  851. }
  852. /**
  853. * Encrypt password
  854. *
  855. * @param string $passwd String to encrypt
  856. */
  857. public static function encrypt($passwd)
  858. {
  859. return md5(_COOKIE_KEY_.$passwd);
  860. }
  861. /**
  862. * Encrypt data string
  863. *
  864. * @param string $data String to encrypt
  865. */
  866. public static function encryptIV($data)
  867. {
  868. return md5(_COOKIE_IV_.$data);
  869. }
  870. /**
  871. * Get token to prevent CSRF
  872. *
  873. * @param string $token token to encrypt
  874. */
  875. public static function getToken($page = true, Context $context = null)
  876. {
  877. if (!$context)
  878. $context = Context::getContext();
  879. if ($page === true)
  880. return (Tools::encrypt($context->customer->id.$context->customer->passwd.$_SERVER['SCRIPT_NAME']));
  881. else
  882. return (Tools::encrypt($context->customer->id.$context->customer->passwd.$page));
  883. }
  884. /**
  885. * Tokenize a string
  886. *
  887. * @param string $string string to encript
  888. */
  889. public static function getAdminToken($string)
  890. {
  891. return !empty($string) ? Tools::encrypt($string) : false;
  892. }
  893. public static function getAdminTokenLite($tab, Context $context = null)
  894. {
  895. if (!$context)
  896. $context = Context::getContext();
  897. return Tools::getAdminToken($tab.(int)Tab::getIdFromClassName($tab).(int)$context->employee->id);
  898. }
  899. public static function getAdminTokenLiteSmarty($params, &$smarty)
  900. {
  901. $context = Context::getContext();
  902. return Tools::getAdminToken($params['tab'].(int)Tab::getIdFromClassName($params['tab']).(int)$context->employee->id);
  903. }
  904. /**
  905. * Get a valid URL to use from BackOffice
  906. *
  907. * @param string $url An URL to use in BackOffice
  908. * @param boolean $entites Set to true to use htmlentities function on URL param
  909. */
  910. public static function getAdminUrl($url = null, $entities = false)
  911. {
  912. $link = Tools::getHttpHost(true).__PS_BASE_URI__;
  913. if (isset($url))
  914. $link .= ($entities ? Tools::htmlentitiesUTF8($url) : $url);
  915. return $link;
  916. }
  917. /**
  918. * Get a valid image URL to use from BackOffice
  919. *
  920. * @param string $image Image name
  921. * @param boolean $entites Set to true to use htmlentities function on image param
  922. */
  923. public static function getAdminImageUrl($image = null, $entities = false)
  924. {
  925. return Tools::getAdminUrl(basename(_PS_IMG_DIR_).'/'.$image, $entities);
  926. }
  927. /**
  928. * Get the user's journey
  929. *
  930. * @param integer $id_category Category ID
  931. * @param string $path Path end
  932. * @param boolean $linkOntheLastItem Put or not a link on the current category
  933. * @param string [optionnal] $categoryType defined what type of categories is used (products or cms)
  934. */
  935. public static function getPath($id_category, $path = '', $link_on_the_item = false, $category_type = 'products', Context $context = null)
  936. {
  937. if (!$context)
  938. $context = Context::getContext();
  939. $id_category = (int)$id_category;
  940. if ($id_category == 1)
  941. return '<span class="navigation_end">'.$path.'</span>';
  942. $pipe = Configuration::get('PS_NAVIGATION_PIPE');
  943. if (empty($pipe))
  944. $pipe = '>';
  945. $full_path = '';
  946. if ($category_type === 'products')
  947. {
  948. $interval = Category::getInterval($id_category);
  949. $id_root_category = $context->shop->getCategory();
  950. $interval_root = Category::getInterval($id_root_category);
  951. if ($interval)
  952. {
  953. $sql = 'SELECT c.id_category, cl.name, cl.link_rewrite
  954. FROM '._DB_PREFIX_.'category c
  955. LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category'.Shop::addSqlRestrictionOnLang('cl').')
  956. '.Shop::addSqlAssociation('category', 'c').'
  957. WHERE c.nleft <= '.$interval['nleft'].'
  958. AND c.nright >= '.$interval['nright'].'
  959. AND c.nleft >= '.$interval_root['nleft'].'
  960. AND c.nright <= '.$interval_root['nright'].'
  961. AND cl.id_lang = '.(int)$context->language->id.'
  962. AND c.active = 1
  963. AND c.level_depth > '.(int)$interval_root['level_depth'].'
  964. ORDER BY c.level_depth ASC';
  965. $categories = Db::getInstance()->executeS($sql);
  966. $n = 1;
  967. $n_categories = count($categories);
  968. foreach ($categories as $category)
  969. {
  970. $full_path .=
  971. (($n < $n_categories || $link_on_the_item) ? '<a href="'.Tools::safeOutput($context->link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'" data-gg="">' : '').
  972. htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
  973. (($n < $n_categories || $link_on_the_item) ? '</a>' : '').
  974. (($n++ != $n_categories || !empty($path)) ? '<span class="navigation-pipe">'.$pipe.'</span>' : '');
  975. }
  976. return $full_path.$path;
  977. }
  978. }
  979. elseif ($category_type === 'CMS')
  980. {
  981. $category = new CMSCategory($id_category, $context->language->id);
  982. if (!Validate::isLoadedObject($category))
  983. die(Tools::displayError());
  984. $category_link = $context->link->getCMSCategoryLink($category);
  985. if ($path != $category->name)
  986. $full_path .= '<a href="'.Tools::safeOutput($category_link).'" data-gg="">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a><span class="navigation-pipe">'.$pipe.'</span>'.$path;
  987. else
  988. $full_path = ($link_on_the_item ? '<a href="'.Tools::safeOutput($category_link).'" data-gg="">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($link_on_the_item ? '</a>' : '');
  989. return Tools::getPath($category->id_parent, $full_path, $link_on_the_item, $category_type);
  990. }
  991. }
  992. /**
  993. * @param string [optionnal] $type_cat defined what type of categories is used (products or cms)
  994. */
  995. public static function getFullPath($id_category, $end, $type_cat = 'products', Context $context = null)
  996. {
  997. if (!$context)
  998. $context = Context::getContext();
  999. $id_category = (int)$id_category;
  1000. $pipe = (Configuration::get('PS_NAVIGATION_PIPE') ? Configuration::get('PS_NAVIGATION_PIPE') : '>');
  1001. $default_category = 1;
  1002. if ($type_cat === 'products')
  1003. {
  1004. $default_category = $context->shop->getCategory();
  1005. $category = new Category($id_category, $context->language->id);
  1006. }
  1007. elseif ($type_cat === 'CMS')
  1008. $category = new CMSCategory($id_category, $context->language->id);
  1009. if (!Validate::isLoadedObject($category))
  1010. $id_category = $default_category;
  1011. if ($id_category == $default_category)
  1012. return htmlentities($end, ENT_NOQUOTES, 'UTF-8');
  1013. return Tools::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>';
  1014. }
  1015. /**
  1016. * Return the friendly url from the provided string
  1017. *
  1018. * @param string $str
  1019. * @param bool $utf8_decode (deprecated)
  1020. * @return string
  1021. */
  1022. public static function link_rewrite($str, $utf8_decode = null)
  1023. {
  1024. if ($utf8_decode !== null)
  1025. Tools::displayParameterAsDeprecated('utf8_decode');
  1026. return Tools::str2url($str);
  1027. }
  1028. /**
  1029. * Return a friendly url made from the provided string
  1030. * If the mbstring library is available, the output is the same as the js function of the same name
  1031. *
  1032. * @param string $str
  1033. * @return string
  1034. */
  1035. public static function str2url($str)
  1036. {
  1037. static $allow_accented_chars = null;
  1038. if ($allow_accented_chars === null)
  1039. $allow_accented_chars = Configuration::get('PS_ALLOW_ACCENTED_CHARS_URL');
  1040. if (!is_string($str))
  1041. return false;
  1042. $str = trim($str);
  1043. if (function_exists('mb_strtolower'))
  1044. $str = mb_strtolower($str, 'utf-8');
  1045. if (!$allow_accented_chars)
  1046. $str = Tools::replaceAccentedChars($str);
  1047. // Remove all non-whitelist chars.
  1048. if ($allow_accented_chars)
  1049. $str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]\-\pL]/u', '', $str);
  1050. else
  1051. $str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]\-]/','', $str);
  1052. $str = preg_replace('/[\s\'\:\/\[\]\-]+/', ' ', $str);
  1053. $str = str_replace(array(' ', '/'), '-', $str);
  1054. // If it was not possible to lowercase the string with mb_strtolower, we do it after the transformations.
  1055. // This way we lose fewer special chars.
  1056. if (!function_exists('mb_strtolower'))
  1057. $str = Tools::strtolower($str);
  1058. return $str;
  1059. }
  1060. /**
  1061. * Replace all accented chars by their equivalent non accented chars.
  1062. *
  1063. * @param string $str
  1064. * @return string
  1065. */
  1066. public static function replaceAccentedChars($str)
  1067. {
  1068. /* One source among others:
  1069. http://www.tachyonsoft.com/uc0000.htm
  1070. http://www.tachyonsoft.com/uc0001.htm
  1071. http://www.tachyonsoft.com/uc0004.htm
  1072. */
  1073. $patterns = array(
  1074. /* Lowercase */
  1075. /* a */ '/[\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}\x{0101}\x{0103}\x{0105}\x{0430}\x{00C0}-\x{00C3}\x{1EA0}-\x{1EB7}]/u',
  1076. /* b */ '/[\x{0431}]/u',
  1077. /* c */ '/[\x{00E7}\x{0107}\x{0109}\x{010D}\x{0446}]/u',
  1078. /* d */ '/[\x{010F}\x{0111}\x{0434}\x{0110}]/u',
  1079. /* e */ '/[\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{0113}\x{0115}\x{0117}\x{0119}\x{011B}\x{0435}\x{044D}\x{00C8}-\x{00CA}\x{1EB8}-\x{1EC7}]/u',
  1080. /* f */ '/[\x{0444}]/u',
  1081. /* g */ '/[\x{011F}\x{0121}\x{0123}\x{0433}\x{0491}]/u',
  1082. /* h */ '/[\x{0125}\x{0127}]/u',
  1083. /* i */ '/[\x{00EC}\x{00ED}\x{00EE}\x{00EF}\x{0129}\x{012B}\x{012D}\x{012F}\x{0131}\x{0438}\x{0456}\x{00CC}\x{00CD}\x{1EC8}-\x{1ECB}\x{0128}]/u',
  1084. /* j */ '/[\x{0135}\x{0439}]/u',
  1085. /* k */ '/[\x{0137}\x{0138}\x{043A}]/u',
  1086. /* l */ '/[\x{013A}\x{013C}\x{013E}\x{0140}\x{0142}\x{043B}]/u',
  1087. /* m */ '/[\x{043C}]/u',
  1088. /* n */ '/[\x{00F1}\x{0144}\x{0146}\x{0148}\x{0149}\x{014B}\x{043D}]/u',
  1089. /* o */ '/[\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F8}\x{014D}\x{014F}\x{0151}\x{043E}\x{00D2}-\x{00D5}\x{01A0}\x{01A1}\x{1ECC}-\x{1EE3}]/u',
  1090. /* p */ '/[\x{043F}]/u',
  1091. /* r */ '/[\x{0155}\x{0157}\x{0159}\x{0440}]/u',
  1092. /* s */ '/[\x{015B}\x{015D}\x{015F}\x{0161}\x{0441}]/u',
  1093. /* ss */ '/[\x{00DF}]/u',
  1094. /* t */ '/[\x{0163}\x{0165}\x{0167}\x{0442}]/u',
  1095. /* u */ '/[\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{0169}\x{016B}\x{016D}\x{016F}\x{0171}\x{0173}\x{0443}\x{00D9}-\x{00DA}\x{0168}\x{01AF}\x{01B0}\x{1EE4}-\x{1EF1}]/u',
  1096. /* v */ '/[\x{0432}]/u',
  1097. /* w */ '/[\x{0175}]/u',
  1098. /* y */ '/[\x{00FF}\x{0177}\x{00FD}\x{044B}\x{1EF2}-\x{1EF9}\x{00DD}]/u',
  1099. /* z */ '/[\x{017A}\x{017C}\x{017E}\x{0437}]/u',
  1100. /* ae */ '/[\x{00E6}]/u',
  1101. /* ch */ '/[\x{0447}]/u',
  1102. /* kh */ '/[\x{0445}]/u',
  1103. /* oe */ '/[\x{0153}]/u',
  1104. /* sh */ '/[\x{0448}]/u',
  1105. /* shh*/ '/[\x{0449}]/u',
  1106. /* ya */ '/[\x{044F}]/u',
  1107. /* ye */ '/[\x{0454}]/u',
  1108. /* yi */ '/[\x{0457}]/u',
  1109. /* yo */ '/[\x{0451}]/u',
  1110. /* yu */ '/[\x{044E}]/u',
  1111. /* zh */ '/[\x{0436}]/u',
  1112. /* Uppercase */
  1113. /* A */ '/[\x{0100}\x{0102}\x{0104}\x{00C0}\x{00C1}\x{00C2}\x{00C3}\x{00C4}\x{00C5}\x{0410}]/u',
  1114. /* B */ '/[\x{0411}]]/u',
  1115. /* C */ '/[\x{00C7}\x{0106}\x{0108}\x{010A}\x{010C}\x{0426}]/u',
  1116. /* D */ '/[\x{010E}\x{0110}\x{0414}]/u',
  1117. /* E */ '/[\x{00C8}\x{00C9}\x{00CA}\x{00CB}\x{0112}\x{0114}\x{0116}\x{0118}\x{011A}\x{0415}\x{042D}]/u',
  1118. /* F */ '/[\x{0424}]/u',
  1119. /* G */ '/[\x{011C}\x{011E}\x{0120}\x{0122}\x{0413}\x{0490}]/u',
  1120. /* H */ '/[\x{0124}\x{0126}]/u',
  1121. /* I */ '/[\x{0128}\x{012A}\x{012C}\x{012E}\x{0130}\x{0418}\x{0406}]/u',
  1122. /* J */ '/[\x{0134}\x{0419}]/u',
  1123. /* K */ '/[\x{0136}\x{041A}]/u',
  1124. /* L */ '/[\x{0139}\x{013B}\x{013D}\x{0139}\x{0141}\x{041B}]/u',
  1125. /* M */ '/[\x{041C}]/u',
  1126. /* N */ '/[\x{00D1}\x{0143}\x{0145}\x{0147}\x{014A}\x{041D}]/u',
  1127. /* O */ '/[\x{00D3}\x{014C}\x{014E}\x{0150}\x{041E}]/u',
  1128. /* P */ '/[\x{041F}]/u',
  1129. /* R */ '/[\x{0154}\x{0156}\x{0158}\x{0420}]/u',
  1130. /* S */ '/[\x{015A}\x{015C}\x{015E}\x{0160}\x{0421}]/u',
  1131. /* T */ '/[\x{0162}\x{0164}\x{0166}\x{0422}]/u',
  1132. /* U */ '/[\x{00D9}\x{00DA}\x{00DB}\x{00DC}\x{0168}\x{016A}\x{016C}\x{016E}\x{0170}\x{0172}\x{0423}]/u',
  1133. /* V */ '/[\x{0412}]/u',
  1134. /* W */ '/[\x{0174}]/u',
  1135. /* Y */ '/[\x{0176}\x{042B}]/u',
  1136. /* Z */ '/[\x{0179}\x{017B}\x{017D}\x{0417}]/u',
  1137. /* AE */ '/[\x{00C6}]/u',
  1138. /* CH */ '/[\x{0427}]/u',
  1139. /* KH */ '/[\x{0425}]/u',
  1140. /* OE */ '/[\x{0152}]/u',
  1141. /* SH */ '/[\x{0428}]/u',
  1142. /* SHH*/ '/[\x{0429}]/u',
  1143. /* YA */ '/[\x{042F}]/u',
  1144. /* YE */ '/[\x{0404}]/u',
  1145. /* YI */ '/[\x{0407}]/u',
  1146. /* YO */ '/[\x{0401}]/u',
  1147. /* YU */ '/[\x{042E}]/u',
  1148. /* ZH */ '/[\x{0416}]/u');
  1149. // ö to oe
  1150. // å to aa
  1151. // ä to ae
  1152. $replacements = array(
  1153. 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's', 'ss', 't', 'u', 'v', 'w', 'y', 'z', 'ae', 'ch', 'kh', 'oe', 'sh', 'shh', 'ya', 'ye', 'yi', 'yo', 'yu', 'zh',
  1154. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'V', 'W', 'Y', 'Z', 'AE', 'CH', 'KH', 'OE', 'SH', 'SHH', 'YA', 'YE', 'YI', 'YO', 'YU', 'ZH'
  1155. );
  1156. return preg_replace($patterns, $replacements, $str);
  1157. }
  1158. /**
  1159. * Truncate strings
  1160. *
  1161. * @param string $str
  1162. * @param integer $max_length Max length
  1163. * @param string $suffix Suffix optional
  1164. * @return string $str truncated
  1165. */
  1166. /* CAUTION : Use it only on module hookEvents.
  1167. ** For other purposes use the smarty function instead */
  1168. public static function truncate($str, $max_length, $suffix = '...')
  1169. {
  1170. if (Tools::strlen($str) <= $max_length)
  1171. return $str;
  1172. $str = utf8_decode($str);
  1173. return (utf8_encode(substr($str, 0, $max_length - Tools::strlen($suffix)).$suffix));
  1174. }
  1175. /*Copied from CakePHP String utility file*/
  1176. public static function truncateString($text, $length = 120, $options = array())
  1177. {
  1178. $default = array(
  1179. 'ellipsis' => '...', 'exact' => true, 'html' => true
  1180. );
  1181. $options = array_merge($default, $options);
  1182. extract($options);
  1183. if ($html)
  1184. {
  1185. if (Tools::strlen(preg_replace('/<.*?>/', '', $text)) <= $length)
  1186. return $text;
  1187. $totalLength = Tools::strlen(strip_tags($ellipsis));
  1188. $openTags = array();
  1189. $truncate = '';
  1190. preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
  1191. foreach ($tags as $tag)
  1192. {
  1193. if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2]))
  1194. {
  1195. if (preg_match('/<[\w]+[^>]*>/s', $tag[0]))
  1196. array_unshift($openTags, $tag[2]);
  1197. elseif (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag))
  1198. {
  1199. $pos = array_search($closeTag[1], $openTags);
  1200. if ($pos !== false)
  1201. array_splice($openTags, $pos, 1);
  1202. }
  1203. }
  1204. $truncate .= $tag[1];
  1205. $contentLength = Tools::strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
  1206. if ($contentLength + $totalLength > $length)
  1207. {
  1208. $left = $length - $totalLength;
  1209. $entitiesLength = 0;
  1210. if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE))
  1211. {
  1212. foreach ($entities[0] as $entity)
  1213. {
  1214. if ($entity[1] + 1 - $entitiesLength <= $left)
  1215. {
  1216. $left--;
  1217. $entitiesLength += Tools::strlen($entity[0]);
  1218. }
  1219. else
  1220. break;
  1221. }
  1222. }
  1223. $truncate .= Tools::substr($tag[3], 0, $left + $entitiesLength);
  1224. break;
  1225. }
  1226. else
  1227. {
  1228. $truncate .= $tag[3];
  1229. $totalLength += $contentLength;
  1230. }
  1231. if ($totalLength >= $length)
  1232. break;
  1233. }
  1234. }
  1235. else
  1236. {
  1237. if (Tools::strlen($text) <= $length)
  1238. return $text;
  1239. $truncate = Tools::substr($text, 0, $length - Tools::strlen($ellipsis));
  1240. }
  1241. if (!$exact)
  1242. {
  1243. $spacepos = Tools::strrpos($truncate, ' ');
  1244. if ($html)
  1245. {
  1246. $truncateCheck = Tools::substr($truncate, 0, $spacepos);
  1247. $lastOpenTag = Tools::strrpos($truncateCheck, '<');
  1248. $lastCloseTag = Tools::strrpos($truncateCheck, '>');
  1249. if ($lastOpenTag > $lastCloseTag)
  1250. {
  1251. preg_match_all('/<[\w]+[^>]*>/s', $truncate, $lastTagMatches);
  1252. $lastTag = array_pop($lastTagMatches[0]);
  1253. $spacepos = Tools::strrpos($truncate, $lastTag) + Tools::strlen($lastTag);
  1254. }
  1255. $bits = Tools::substr($truncate, $spacepos);
  1256. preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
  1257. if (!empty($droppedTags))
  1258. {
  1259. if (!empty($openTags))
  1260. {
  1261. foreach ($droppedTags as $closingTag)
  1262. if (!in_array($closingTag[1], $openTags))
  1263. array_unshift($openTags, $closingTag[1]);
  1264. }
  1265. else
  1266. {
  1267. foreach ($droppedTags as $closingTag)
  1268. $openTags[] = $closingTag[1];
  1269. }
  1270. }
  1271. }
  1272. $truncate = Tools::substr($truncate, 0, $spacepos);
  1273. }
  1274. $truncate .= $ellipsis;
  1275. if ($html)
  1276. foreach ($openTags as $tag)
  1277. $truncate .= '</' . $tag . '>';
  1278. return $truncate;
  1279. }
  1280. public static function normalizeDirectory($directory)
  1281. {
  1282. $last = $directory[strlen($directory) - 1];
  1283. if (in_array($last, array('/', '\\')))
  1284. {
  1285. $directory[strlen($directory) - 1] = DIRECTORY_SEPARATOR;
  1286. return $directory;
  1287. }
  1288. $directory .= DIRECTORY_SEPARATOR;
  1289. return $directory;
  1290. }
  1291. /**
  1292. * Generate date form
  1293. *
  1294. * @param integer $year Year to select
  1295. * @param integer $month Month to select
  1296. * @param integer $day Day to select
  1297. * @return array $tab html data with 3 cells :['days'], ['months'], ['years']
  1298. *
  1299. */
  1300. public static function dateYears()
  1301. {
  1302. $tab = array();
  1303. for ($i = date('Y'); $i >= 1900; $i--)
  1304. $tab[] = $i;
  1305. return $tab;
  1306. }
  1307. public static function dateDays()
  1308. {
  1309. $tab = array();
  1310. for ($i = 1; $i != 32; $i++)
  1311. $tab[] = $i;
  1312. return $tab;
  1313. }
  1314. public static function dateMonths()
  1315. {
  1316. $tab = array();
  1317. for ($i = 1; $i != 13; $i++)
  1318. $tab[$i] = date('F', mktime(0, 0, 0, $i, date('m'), date('Y')));
  1319. return $tab;
  1320. }
  1321. public static function hourGenerate($hours, $minutes, $seconds)
  1322. {
  1323. return implode(':', array($hours, $minutes, $seconds));
  1324. }
  1325. public static function dateFrom($date)
  1326. {
  1327. $tab = explode(' ', $date);
  1328. if (!isset($tab[1]))
  1329. $date .= ' '.Tools::hourGenerate(0, 0, 0);
  1330. return $date;
  1331. }
  1332. public static function dateTo($date)
  1333. {
  1334. $tab = explode(' ', $date);
  1335. if (!isset($tab[1]))
  1336. $date .= ' '.Tools::hourGenerate(23, 59, 59);
  1337. return $date;
  1338. }
  1339. public static function strtolower($str)
  1340. {
  1341. if (is_array($str))
  1342. return false;
  1343. if (function_exists('mb_strtolower'))
  1344. return mb_strtolower($str, 'utf-8');
  1345. return strtolower($str);
  1346. }
  1347. public static function strlen($str, $encoding = 'UTF-8')
  1348. {
  1349. if (is_array($str))
  1350. return false;
  1351. $str = html_entity_decode($str, ENT_COMPAT, 'UTF-8');
  1352. if (function_exists('mb_strlen'))
  1353. return mb_strlen($str, $encoding);
  1354. return strlen($str);
  1355. }
  1356. public static function stripslashes($string)
  1357. {
  1358. if (_PS_MAGIC_QUOTES_GPC_)
  1359. $string = stripslashes($string);
  1360. return $string;
  1361. }
  1362. public static function strtoupper($str)
  1363. {
  1364. if (is_array($str))
  1365. return false;
  1366. if (function_exists('mb_strtoupper'))
  1367. return mb_strtoupper($str, 'utf-8');
  1368. return strtoupper($str);
  1369. }
  1370. public static function substr($str, $start, $length = false, $encoding = 'utf-8')
  1371. {
  1372. if (is_array($str))
  1373. return false;
  1374. if (function_exists('mb_substr'))
  1375. return mb_substr($str, (int)$start, ($length === false ? Tools::strlen($str) : (int)$length), $encoding);
  1376. return substr($str, $start, ($length === false ? Tools::strlen($str) : (int)$length));
  1377. }
  1378. public static function strpos($str, $find, $offset = 0, $encoding = 'UTF-8')
  1379. {
  1380. if (function_exists('mb_strpos'))
  1381. return mb_strpos($str, $find, $offset, $encoding);
  1382. return strpos($str, $find, $offset);
  1383. }
  1384. public static function strrpos($str, $find, $offset = 0, $encoding = 'utf-8')
  1385. {
  1386. if (function_exists('mb_strrpos'))
  1387. return mb_strrpos($str, $find, $offset, $encoding);
  1388. return strrpos($str, $find, $offset);
  1389. }
  1390. public static function ucfirst($str)
  1391. {
  1392. return Tools::strtoupper(Tools::substr($str, 0, 1)).Tools::substr($str, 1);
  1393. }
  1394. public static function ucwords($str)
  1395. {
  1396. if (function_exists('mb_convert_case'))
  1397. return mb_convert_case($str, MB_CASE_TITLE);
  1398. return ucwords(Tools::strtolower($str));
  1399. }
  1400. public static function orderbyPrice(&$array, $order_way)
  1401. {
  1402. foreach ($array as &$row)
  1403. $row['price_tmp'] = Product::getPriceStatic($row['id_product'], true, ((isset($row['id_product_attribute']) && !empty($row['id_product_attribute'])) ? (int)$row['id_product_attribute'] : null), 2);
  1404. unset($row);
  1405. if (Tools::strtolower($order_way) == 'desc')
  1406. uasort($array, 'cmpPriceDesc');
  1407. else
  1408. uasort($array, 'cmpPriceAsc');
  1409. foreach ($array as &$row)
  1410. unset($row['price_tmp']);
  1411. }
  1412. public static function iconv($from, $to, $string)
  1413. {
  1414. if (function_exists('iconv'))
  1415. return iconv($from, $to.'//TRANSLIT', str_replace('¥', '&yen;', str_replace('£', '&pound;', str_replace('€', '&euro;', $string))));
  1416. return html_entity_decode(htmlentities($string, ENT_NOQUOTES, $from), ENT_NOQUOTES, $to);
  1417. }
  1418. public static function isEmpty($field)
  1419. {
  1420. return ($field === '' || $field === null);
  1421. }
  1422. /**
  1423. * returns the rounded value of $value to specified precision, according to your configuration;
  1424. *
  1425. * @note : PHP 5.3.0 introduce a 3rd parameter mode in round function
  1426. *
  1427. * @param float $value
  1428. * @param int $precision
  1429. * @return float
  1430. */
  1431. public static function ps_round($value, $precision = 0)
  1432. {
  1433. if (Tools::$round_mode == null)
  1434. Tools::$round_mode = (int)Configuration::get('PS_PRICE_ROUND_MODE');
  1435. switch (Tools::$round_mode)
  1436. {
  1437. case PS_ROUND_UP:
  1438. return Tools::ceilf($value, $precision);
  1439. case PS_ROUND_DOWN:
  1440. return Tools::floorf($value, $precision);
  1441. case PS_ROUND_HALF_DOWN:
  1442. case PS_ROUND_HALF_EVEN:
  1443. case PS_ROUND_HALF_ODD:
  1444. return Tools::math_round($value, $precision, Tools::$round_mode);
  1445. case PS_ROUND_HALF_UP:
  1446. default:
  1447. return Tools::math_round($value, $precision, PS_ROUND_HALF_UP);
  1448. }
  1449. }
  1450. public static function math_round($value, $places, $mode = PS_ROUND_HALF_UP)
  1451. {
  1452. //If PHP_ROUND_HALF_UP exist (PHP 5.3) use it and pas…

Large files files are truncated, but you can click here to view the full file