PageRenderTime 48ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/profiling/Controller.php

https://gitlab.com/mtellezgalindo/PrestaShop
PHP | 596 lines | 525 code | 43 blank | 28 comment | 46 complexity | 8183cb7d4a7914656d6ff818f0ad700d MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0
  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. abstract class Controller extends ControllerCore
  27. {
  28. public $_memory = array();
  29. public $_time = array();
  30. private static $_footer = true;
  31. public static function disableParentCalls()
  32. {
  33. self::$_footer = false;
  34. }
  35. private function displayMemoryColor($n)
  36. {
  37. $n /= 1048576;
  38. if ($n > 3)
  39. return '<span style="color:red">'.sprintf('%0.2f', $n).'</span>';
  40. elseif ($n > 1)
  41. return '<span style="color:orange">'.sprintf('%0.2f', $n).'</span>';
  42. elseif (round($n, 2) > 0)
  43. return '<span style="color:green">'.sprintf('%0.2f', $n).'</span>';
  44. return '<span style="color:green">-</span>';
  45. }
  46. private function displayPeakMemoryColor($n)
  47. {
  48. $n /= 1048576;
  49. if ($n > 16)
  50. return '<span style="color:red">'.sprintf('%0.1f', $n).'</span>';
  51. if ($n > 12)
  52. return '<span style="color:orange">'.sprintf('%0.1f', $n).'</span>';
  53. return '<span style="color:green">'.sprintf('%0.1f', $n).'</span>';
  54. }
  55. private function displaySQLQueries($n)
  56. {
  57. if ($n > 150)
  58. return '<span style="color:red">'.$n.' queries</span>';
  59. if ($n > 100)
  60. return '<span style="color:orange">'.$n.' queries</span>';
  61. return '<span style="color:green">'.$n.' quer'.($n == 1 ? 'y' : 'ies').'</span>';
  62. }
  63. private function displayRowsBrowsed($n)
  64. {
  65. if ($n > 400)
  66. return '<span style="color:red">'.$n.' rows browsed</span>';
  67. if ($n > 100)
  68. return '<span style="color:orange">'.$n.' rows browsed</span>';
  69. return '<span style="color:green">'.$n.' row'.($n == 1 ? '' : 's').' browsed</span>';
  70. }
  71. private function displayLoadTimeColor($n, $kikoo = false)
  72. {
  73. if ($n > 1)
  74. return '<span style="color:red">'.round($n * 1000).'</span>'.($kikoo ? ' ms<br />You\'d better run your shop on a toaster' : '');
  75. elseif ($n > 0.5)
  76. return '<span style="color:orange">'.round($n * 1000).'</span>'.($kikoo ? ' ms<br />I hope it is a shared hosting' : '');
  77. elseif ($n > 0)
  78. return '<span style="color:green">'.round($n * 1000).'</span>'.($kikoo ? ' ms<br />Good boy! That\'s what I call a webserver!' : '');
  79. return '<span style="color:green">-</span>'.($kikoo ? ' ms<br />Faster than light' : '');
  80. }
  81. private function getTimeColor($n)
  82. {
  83. if ($n > 4)
  84. return 'style="color:red"';
  85. if ($n > 2)
  86. return 'style="color:orange"';
  87. return 'style="color:green"';
  88. }
  89. private function getQueryColor($n)
  90. {
  91. if ($n > 5)
  92. return 'style="color:red"';
  93. if ($n > 2)
  94. return 'style="color:orange"';
  95. return 'style="color:green"';
  96. }
  97. private function getTableColor($n)
  98. {
  99. if ($n > 30)
  100. return 'style="color:red"';
  101. if ($n > 20)
  102. return 'style="color:orange"';
  103. return 'style="color:green"';
  104. }
  105. private function getObjectModelColor($n)
  106. {
  107. if ($n > 50)
  108. return 'style="color:red"';
  109. if ($n > 10)
  110. return 'style="color:orange"';
  111. return 'style="color:green"';
  112. }
  113. public function __construct()
  114. {
  115. if (!self::$_footer)
  116. return;
  117. $this->_memory['config'] = memory_get_usage();
  118. $this->_mempeak['config'] = memory_get_peak_usage();
  119. $this->_time['config'] = microtime(true);
  120. parent::__construct();
  121. $this->_memory['constructor'] = memory_get_usage();
  122. $this->_mempeak['constructor'] = memory_get_peak_usage();
  123. $this->_time['constructor'] = microtime(true);
  124. }
  125. public function run()
  126. {
  127. $this->init();
  128. $this->_memory['init'] = memory_get_usage();
  129. $this->_mempeak['init'] = memory_get_peak_usage();
  130. $this->_time['init'] = microtime(true);
  131. if ($this->checkAccess())
  132. {
  133. $this->_memory['checkAccess'] = memory_get_usage();
  134. $this->_mempeak['checkAccess'] = memory_get_peak_usage();
  135. $this->_time['checkAccess'] = microtime(true);
  136. if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className)))
  137. $this->setMedia();
  138. $this->_memory['setMedia'] = memory_get_usage();
  139. $this->_mempeak['setMedia'] = memory_get_peak_usage();
  140. $this->_time['setMedia'] = microtime(true);
  141. // postProcess handles ajaxProcess
  142. $this->postProcess();
  143. $this->_memory['postProcess'] = memory_get_usage();
  144. $this->_mempeak['postProcess'] = memory_get_peak_usage();
  145. $this->_time['postProcess'] = microtime(true);
  146. if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className)))
  147. $this->initHeader();
  148. $this->_memory['initHeader'] = memory_get_usage();
  149. $this->_mempeak['initHeader'] = memory_get_peak_usage();
  150. $this->_time['initHeader'] = microtime(true);
  151. $this->initContent();
  152. $this->_memory['initContent'] = memory_get_usage();
  153. $this->_mempeak['initContent'] = memory_get_peak_usage();
  154. $this->_time['initContent'] = microtime(true);
  155. if (!$this->content_only && ($this->display_footer || (isset($this->className) && $this->className)))
  156. $this->initFooter();
  157. $this->_memory['initFooter'] = memory_get_usage();
  158. $this->_mempeak['initFooter'] = memory_get_peak_usage();
  159. $this->_time['initFooter'] = microtime(true);
  160. // default behavior for ajax process is to use $_POST[action] or $_GET[action]
  161. // then using displayAjax[action]
  162. if ($this->ajax)
  163. {
  164. $action = Tools::toCamelCase(Tools::getValue('action'), true);
  165. if (!empty($action) && method_exists($this, 'displayAjax'.$action))
  166. $this->{'displayAjax'.$action}();
  167. elseif (method_exists($this, 'displayAjax'))
  168. $this->displayAjax();
  169. }
  170. else
  171. $this->displayDebug();
  172. }
  173. else
  174. {
  175. $this->initCursedPage();
  176. $this->displayDebug();
  177. }
  178. }
  179. private function sizeofvar($var)
  180. {
  181. $start_memory = memory_get_usage();
  182. try {
  183. $tmp = Tools::unSerialize(serialize($var));
  184. } catch (Exception $e) {
  185. $tmp = $this->getVarData($var);
  186. }
  187. $size = memory_get_usage() - $start_memory;
  188. return $size;
  189. }
  190. private function getVarData($var)
  191. {
  192. if (is_object($var))
  193. return $var;
  194. return (string)$var;
  195. }
  196. public function displayDebug()
  197. {
  198. global $start_time;
  199. $this->display();
  200. $this->_memory['display'] = memory_get_usage();
  201. $this->_mempeak['display'] = memory_get_peak_usage();
  202. $this->_time['display'] = microtime(true);
  203. $memory_peak_usage = memory_get_peak_usage();
  204. $hr = '<hr>';
  205. $totalSize = 0;
  206. foreach (get_included_files() as $file)
  207. $totalSize += filesize($file);
  208. $totalQueryTime = 0;
  209. foreach (Db::getInstance()->queries as $data)
  210. $totalQueryTime += $data['time'];
  211. $executedModules = Hook::getExecutedModules();
  212. $hooktime = Hook::getHookTime();
  213. arsort($hooktime);
  214. $totalHookTime = 0;
  215. foreach ($hooktime as $time)
  216. $totalHookTime += $time;
  217. $hookMemoryUsage = Hook::getHookMemoryUsage();
  218. arsort($hookMemoryUsage);
  219. $totalHookMemoryUsage = 0;
  220. foreach ($hookMemoryUsage as $usage)
  221. $totalHookMemoryUsage += $usage;
  222. $globalSize = array();
  223. $totalGlobalSize = 0;
  224. foreach ($GLOBALS as $key => $value)
  225. if ($key != 'GLOBALS')
  226. {
  227. $totalGlobalSize += ($size = $this->sizeofvar($value));
  228. if ($size > 1024)
  229. $globalSize[$key] = round($size / 1024);
  230. }
  231. arsort($globalSize);
  232. $cache = Cache::retrieveAll();
  233. $totalCacheSize = $this->sizeofvar($cache);
  234. echo '
  235. <style>
  236. #ps_profiling{
  237. padding: 20px;
  238. }
  239. .ps_back-office.page-sidebar #ps_profiling{
  240. margin-left: 210px;
  241. }
  242. .ps_back-office.page-sidebar-closed #ps_profiling{
  243. margin-left: 50px;
  244. }
  245. .ps_back-office #ps_profiling{
  246. clear: both;
  247. padding: 10px;
  248. margin-bottom: 50px;
  249. }
  250. #ps_profiling *{
  251. box-sizing:border-box;
  252. -moz-box-sizing:border-box;
  253. color: #888;
  254. }
  255. #ps_profiling .ps_profiling_title{
  256. font-size: 20px;
  257. display: inline-block;
  258. padding-bottom: 15px;
  259. }
  260. #ps_profiling ul{
  261. margin: 0;
  262. padding: 0;
  263. list-style: none;
  264. }
  265. #ps_profiling hr{
  266. margin: 5px 0;
  267. padding: 0;
  268. border: none;
  269. border-bottom: solid 1px #ccc;
  270. }
  271. #ps_profiling td pre{
  272. padding: 6px;
  273. margin-right: 10px;
  274. border-radius: 5px;
  275. overflow: auto;
  276. display: block;
  277. color: #777;
  278. font-size: 12px;
  279. line-height: 1.42857;
  280. word-break: break-all;
  281. word-wrap: break-word;
  282. background-color: whitesmoke;
  283. border: 1px solid #cccccc;
  284. max-width: 960px;
  285. }
  286. #ps_profiling td .qbt{
  287. max-height: 140px;
  288. overflow: auto;
  289. }
  290. #ps_profiling table{
  291. width: 100%;
  292. margin-bottom: 10px;
  293. background-color: white;
  294. }
  295. #ps_profiling table th{
  296. font-weight: normal;
  297. border-bottom: 1px solid #999;
  298. color: #888;
  299. padding: 5px 0;
  300. }
  301. #ps_profiling table td{
  302. border-bottom: 1px solid #eee;
  303. padding: 6px;
  304. }
  305. .sortable thead th{
  306. cursor:pointer;
  307. }
  308. #ps_profiling table .text-right{
  309. text-align: right
  310. }
  311. #ps_profiling table .text-left{
  312. text-align: left
  313. }
  314. #ps_profiling table .text-center{
  315. text-align: center
  316. }
  317. #ps_profiling .ps_profiling_row{
  318. clear: both;
  319. margin-bottom: 60px;
  320. }
  321. #ps_profiling .ps_profiling_col4{
  322. float: left;
  323. padding: 0 10px;
  324. border-right: 1px solid #ccc;
  325. width: 25%;
  326. }
  327. @media (max-width: 1200px) {
  328. #ps_profiling .ps_profiling_col4 {
  329. width: 50%;
  330. }
  331. }
  332. @media (max-width: 600px) {
  333. #ps_profiling .ps_profiling_col4 {
  334. width: 100%;
  335. }
  336. }
  337. #ps_profiling .ps_profiling_col4:last-child{
  338. border-right: none;
  339. }
  340. #ps_profiling .ps_profiling_infobox{
  341. background-color: white;
  342. padding: 5px 10px;
  343. border: 1px solid #ccc;
  344. margin-bottom: 10px;
  345. }
  346. #ps_profiling .text-muted{
  347. color: #bbb;
  348. }
  349. </style>';
  350. echo '<div id="ps_profiling">';
  351. if (!empty($this->redirect_after))
  352. echo '<div class="ps_profiling_row"><div class="ps_profiling_col12"><h2>Caught redirection to <a href="'.htmlspecialchars($this->redirect_after).'">'.htmlspecialchars($this->redirect_after).'</a></h2></div></div>';
  353. echo '
  354. <div class="ps_profiling_row">
  355. <div class="ps_profiling_col4">
  356. <div class="ps_profiling_infobox"><b>Load time</b>: '.$this->displayLoadTimeColor($this->_time['display'] - $start_time, true).'</div>';
  357. if (self::$_footer){
  358. echo '<table>';
  359. echo '<thead><tr><th class="text-left">Execution</th><th class="text-right">Load time (ms)</th></tr><thead><tbody>';
  360. $last_time = $start_time;
  361. foreach ($this->_time as $k => $time)
  362. {
  363. echo '<tr><td>'.$k.'</td><td class="text-right">'.$this->displayLoadTimeColor($time - $last_time).'</td></tr>';
  364. $last_time = $time;
  365. }
  366. echo '</tbody></table>';
  367. }
  368. echo '</div>
  369. <div class="ps_profiling_col4">
  370. <div class="ps_profiling_infobox"><b>Hook processing</b>: '.$this->displayLoadTimeColor($totalHookTime).' ms / '.$this->displayMemoryColor($totalHookMemoryUsage).' Mb<br>
  371. '.(int)count($executedModules).' methods called in '.(int)count(array_unique($executedModules)).' modules</div>';
  372. echo '<table>';
  373. echo '<thead><tr><th class="text-left">Hook</th><th class="text-right">Processing</th></tr><thead><tbody>';
  374. foreach ($hooktime as $hook => $time)
  375. echo '<tr><td>'.$hook.'</td><td class="text-right">'.$this->displayMemoryColor($hookMemoryUsage[$hook]).' Mb in '.$this->displayLoadTimeColor($time).' ms</td></tr>';
  376. echo '</table>
  377. </div>
  378. <div class="ps_profiling_col4">
  379. <div class="ps_profiling_infobox"><b>Memory peak usage</b>: '.$this->displayPeakMemoryColor($memory_peak_usage).' Mb</div>';
  380. if (self::$_footer)
  381. {
  382. echo '<table>';
  383. echo '<thead><tr><th class="text-left">Execution</th><th class="text-right">Memory (Mb)</th><th class="text-right">Total (Mb)</th></tr><thead><tbody>';
  384. $last_memory = 0;
  385. foreach ($this->_memory as $k => $memory)
  386. {
  387. echo '<tr><td>'.$k.'</td><td class="text-right">'.$this->displayMemoryColor($memory - $last_memory).'</td><td class="text-right">'.$this->displayPeakMemoryColor($this->_mempeak[$k]).'</td></tr>';
  388. $last_memory = $memory;
  389. }
  390. echo '<tbody></table>';
  391. }
  392. echo '
  393. </div>';
  394. $compile = array(
  395. 0 => 'green">never recompile',
  396. 1 => 'orange">auto',
  397. 2 => 'red">force compile'
  398. );
  399. echo '
  400. <div class="ps_profiling_col4">
  401. <div class="ps_profiling_infobox"><b>Total cache size in Cache class</b>: '.$this->displayMemoryColor($totalCacheSize).' Mb</div>
  402. <div class="ps_profiling_infobox"><b>Smarty cache</b>: <span style="color:'.(Configuration::get('PS_SMARTY_CACHE') ? 'green">enabled' : 'red">disabled').'</span></div>
  403. <div class="ps_profiling_infobox"><b>Smarty compilation</b>: <span style="color:'.$compile[Configuration::get('PS_SMARTY_FORCE_COMPILE')].'</span></div>
  404. <div class="ps_profiling_infobox"><b>SQL Queries</b>: '.$this->displaySQLQueries(count(Db::getInstance()->queries)).' in '.$this->displayLoadTimeColor($totalQueryTime).' ms</div>
  405. <div class="ps_profiling_infobox"><b>Included files</b>: '.sizeof(get_included_files()).' ('.$this->displayMemoryColor($totalSize).' Mb)</div>
  406. <div class="ps_profiling_infobox"><b>Global vars</b> : '.$this->displayMemoryColor($totalGlobalSize).' Mb
  407. <ul>';
  408. foreach ($globalSize as $global => $size)
  409. echo '<li>$'.$global.' &asymp; '.$size.'k</li>';
  410. echo '</ul>
  411. </div>
  412. </div>';
  413. $array_queries = array();
  414. $queries = Db::getInstance()->queries;
  415. uasort($queries, 'prestashop_querytime_sort');
  416. foreach ($queries as $data)
  417. {
  418. $query_row = array(
  419. 'time' => $data['time'],
  420. 'query' => $data['query'],
  421. 'location' => $data['stack'][0]['file'].':'.$data['stack'][0]['line'],
  422. 'filesort' => false,
  423. 'rows' => 1,
  424. 'group_by' => false,
  425. 'stack' => $data['stack']
  426. );
  427. if (preg_match('/^\s*select\s+/i', $data['query']))
  428. {
  429. $explain = Db::getInstance()->executeS('explain '.$data['query']);
  430. if (stristr($explain[0]['Extra'], 'filesort'))
  431. $query_row['filesort'] = true;
  432. foreach ($explain as $row)
  433. $query_row['rows'] *= $row['rows'];
  434. if (stristr($data['query'], 'group by') && !preg_match('/(avg|count|min|max|group_concat|sum)\s*\(/i', $data['query']))
  435. $query_row['group_by'] = true;
  436. }
  437. $array_queries[] = $query_row;
  438. }
  439. echo '</div>';
  440. echo '
  441. <div class="ps_profiling_row">
  442. <ul>
  443. <li><a href="#stopwatch">Stopwatch</a></li>
  444. <li><a href="#doubles">Doubles</a></li>
  445. <li><a href="#tables">Tables stress</a></li>
  446. '.(isset(ObjectModel::$debug_list) ? '<li><a href="#objectModels">ObjectModel instances</a></li>' : '').'
  447. <li><a href="#includedFiles">Files included</a></li>
  448. </ul>
  449. </div>
  450. <div class="ps_profiling_row">
  451. <span class="ps_profiling_title"><a name="stopwatch">Stopwatch (with SQL_NO_CACHE) (total = '.count(Db::getInstance()->queries).')</a></span>';
  452. $i = 1;
  453. echo '
  454. <script type="text/javascript" src="https://raw.githubusercontent.com/drvic10k/bootstrap-sortable/master/Scripts/bootstrap-sortable.js"></script>
  455. <table class="table table-striped table-condensed sortable">
  456. <thead>
  457. <tr>
  458. <th class="text-left col-lg-6">Query</th>
  459. <th class="text-left col-lg-1">Time (ms)</th>
  460. <th class="text-left col-lg-1">Rows</th>
  461. <th class="text-left col-lg-1">Filesort</th>
  462. <th class="text-left col-lg-1">Group By</th>
  463. <th class="text-left col-lg-2">Location</th>
  464. </tr>
  465. <thead>
  466. <tbody>';
  467. foreach ($array_queries as $data)
  468. {
  469. $echo_stack = '';
  470. array_shift($data['stack']);
  471. foreach ($data['stack'] as $call)
  472. $echo_stack .= 'from '.str_replace('\\', '/', substr($call['file'], strlen(_PS_ROOT_DIR_))).':'.$call['line'].'<br />';
  473. echo '
  474. <tr>
  475. <td><pre>'.preg_replace("/(^[\s]*)/m", "", htmlspecialchars($data['query'], ENT_NOQUOTES, 'utf-8', false)).'</pre></td>
  476. <td><span '.$this->getTimeColor($data['time'] * 1000).'>'.(round($data['time'] * 1000, 1) < 0.1 ? '< 1' : round($data['time'] * 1000, 1)).'</span></td>
  477. <td>'.$data['rows'].'</td>
  478. <td>'.($data['filesort'] ? '<span style="color:red">Yes</span>' : '').'</td>
  479. <td>'.($data['group_by'] ? '<span style="color:red">Yes</span>' : '').'</td>
  480. <td>in '.$data['location'].'<br /><div class="qbt" id="qbt'.($i++).'">'.$echo_stack.'</div></td>
  481. </tr>';
  482. }
  483. echo '</table>';
  484. $queries = Db::getInstance()->uniqQueries;
  485. arsort($queries);
  486. $count = count(Db::getInstance()->uniqQueries);
  487. foreach ($queries as $q => &$nb)
  488. if ($nb == 1)
  489. $count--;
  490. if ($count)
  491. echo '</div>
  492. <div class="ps_profiling_row">
  493. <span class="ps_profiling_title"><a name="doubles">Doubles (IDs replaced by "XX") (total = '.$count.')</a></span>
  494. <table>';
  495. foreach ($queries as $q => $nb)
  496. if ($nb > 1)
  497. echo '<tr><td><span '.$this->getQueryColor($nb).'>'.$nb.'</span> '.$q.'</td></tr>';
  498. echo '</table></div>
  499. <div class="ps_profiling_row">
  500. <span class="ps_profiling_title"><a name="tables">Tables stress</a></span>
  501. <table>';
  502. $tables = Db::getInstance()->tables;
  503. arsort($tables);
  504. foreach ($tables as $table => $nb)
  505. echo '<tr><td><span '.$this->getTableColor($nb).'>'.$nb.'</span> '.$table.'</td></tr>';
  506. echo '</table></div>';
  507. if (isset(ObjectModel::$debug_list))
  508. {
  509. echo '<div class="ps_profiling_row">
  510. <span class="ps_profiling_title"><a name="objectModels">ObjectModel instances</a></span>';
  511. $list = ObjectModel::$debug_list;
  512. uasort($list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;'));
  513. $i = 0;
  514. echo '<table><thead><tr><th class="text-left">Name</th><th class="text-left">Instance</th><th class="text-left">Source</th></tr></thead><tbody>';
  515. foreach ($list as $class => $info)
  516. {
  517. echo '<tr><td>'.$class.'</td>';
  518. echo '<td><span '.$this->getObjectModelColor(count($info)).'>'.count($info).'</span></td>';
  519. echo '<td><div id="object_model_'.$i.'">';
  520. foreach ($info as $trace)
  521. echo ltrim(str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']), '/').' ['.$trace['line'].']<br />';
  522. echo '</div></td></tr>';
  523. $i++;
  524. }
  525. echo '</tbody></table></div>';
  526. }
  527. // List of included files
  528. echo '<div class="ps_profiling_row">
  529. <span class="ps_profiling_title"><a name="includedFiles">Included files</a></span>
  530. <table>';
  531. $i = 1;
  532. echo '<thead><tr><th class="text-left">#</th><th class="text-left">Filename</th></tr></thead><tbody>';
  533. foreach (get_included_files() as $file)
  534. {
  535. $file = ltrim(str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file)), '/');
  536. $file = '<span class="text-muted">'.dirname($file).'/</span><span>'.basename($file).'</span>';
  537. echo '<tr><td>'.$i.'</td><td>'.$file.'</td></tr>';
  538. $i++;
  539. }
  540. echo '</tbody></table></div></div>';
  541. }
  542. }
  543. function prestashop_querytime_sort($a, $b)
  544. {
  545. if ($a['time'] == $b['time'])
  546. return 0;
  547. return ($a['time'] > $b['time']) ? -1 : 1;
  548. }