* @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * International Registered Trademark & Property of PrestaShop SA */ abstract class Controller extends ControllerCore { protected $total_filesize = 0; protected $total_query_time = 0; protected $total_global_var_size = 0; protected $total_modules_time = 0; protected $total_modules_memory = 0; protected $global_var_size = array(); protected $modules_perfs = array(); protected $hooks_perfs = array(); protected $array_queries = array(); protected $profiler = array(); private function getMemoryColor($n) { $n /= 1048576; if ($n > 3) { return ''.sprintf('%0.2f', $n).''; } elseif ($n > 1) { return ''.sprintf('%0.2f', $n).''; } elseif (round($n, 2) > 0) { return ''.sprintf('%0.2f', $n).''; } return '-'; } private function getPeakMemoryColor($n) { $n /= 1048576; if ($n > 16) { return ''.sprintf('%0.1f', $n).''; } if ($n > 12) { return ''.sprintf('%0.1f', $n).''; } return ''.sprintf('%0.1f', $n).''; } private function displaySQLQueries($n) { if ($n > 150) { return ''.$n.' queries'; } if ($n > 100) { return ''.$n.' queries'; } return ''.$n.' quer'.($n == 1 ? 'y' : 'ies').''; } private function displayRowsBrowsed($n) { if ($n > 400) { return ''.$n.' rows browsed'; } if ($n > 100) { return ''.$n.' rows browsed'; } return ''.$n.' row'.($n == 1 ? '' : 's').' browsed'; } private function getPhpVersionColor($version) { if (version_compare($version, '5.3') < 0) { return ''.$version.' (Upgrade strongly recommended)'; } elseif (version_compare($version, '5.4') < 0) { return ''.$version.' (Consider upgrading)'; } return ''.$version.' (OK)'; } private function getMySQLVersionColor($version) { if (version_compare($version, '5.5') < 0) { return ''.$version.' (Upgrade strongly recommended)'; } elseif (version_compare($version, '5.6') < 0) { return ''.$version.' (Consider upgrading)'; } return ''.$version.' (OK)'; } private function getLoadTimeColor($n, $kikoo = false) { if ($n > 1.6) { return ''.round($n * 1000).''.($kikoo ? ' ms - You\'d better run your shop on a toaster' : ''); } elseif ($n > 0.8) { return ''.round($n * 1000).''.($kikoo ? ' ms - OK... for a shared hosting' : ''); } elseif ($n > 0) { return ''.round($n * 1000).''.($kikoo ? ' ms - Unicorn powered webserver!' : ''); } return '-'.($kikoo ? ' ms - Faster than light' : ''); } private function getTotalQueriyingTimeColor($n) { if ($n >= 100) { return ''.$n.''; } elseif ($n >= 50) { return ''.$n.''; } return ''.$n.''; } private function getNbQueriesColor($n) { if ($n >= 100) { return ''.$n.''; } elseif ($n >= 50) { return ''.$n.''; } return ''.$n.''; } private function getTimeColor($n) { if ($n > 4) { return 'style="color:red"'; } if ($n > 2) { return 'style="color:#EF8B00"'; } return 'style="color:green"'; } private function getQueryColor($n) { if ($n > 5) { return 'style="color:red"'; } if ($n > 2) { return 'style="color:#EF8B00"'; } return 'style="color:green"'; } private function getTableColor($n) { if ($n > 30) { return 'style="color:red"'; } if ($n > 20) { return 'style="color:#EF8B00"'; } return 'style="color:green"'; } private function getObjectModelColor($n) { if ($n > 50) { return 'style="color:red"'; } if ($n > 10) { return 'style="color:#EF8B00"'; } return 'style="color:green"'; } protected function stamp($block) { return array('block' => $block, 'memory_usage' => memory_get_usage(), 'peak_memory_usage' => memory_get_peak_usage(), 'time' => microtime(true)); } public function __construct() { $this->profiler[] = $this->stamp('config'); parent::__construct(); $this->profiler[] = $this->stamp('__construct'); } public function run() { $this->init(); $this->profiler[] = $this->stamp('init'); if ($this->checkAccess()) { $this->profiler[] = $this->stamp('checkAccess'); if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) { $this->setMedia(); $this->profiler[] = $this->stamp('setMedia'); } $this->postProcess(); $this->profiler[] = $this->stamp('postProcess'); if (!$this->content_only && ($this->display_header || (isset($this->className) && $this->className))) { $this->initHeader(); $this->profiler[] = $this->stamp('initHeader'); } $this->initContent(); $this->profiler[] = $this->stamp('initContent'); if (!$this->content_only && ($this->display_footer || (isset($this->className) && $this->className))) { $this->initFooter(); $this->profiler[] = $this->stamp('initFooter'); } if ($this->ajax) { $action = Tools::toCamelCase(Tools::getValue('action'), true); if (!empty($action) && method_exists($this, 'displayAjax'.$action)) { $this->{'displayAjax'.$action}(); } elseif (method_exists($this, 'displayAjax')) { $this->displayAjax(); } return; } } else { $this->initCursedPage(); } $this->displayProfiling(); } private function getVarSize($var) { $start_memory = memory_get_usage(); try { $tmp = Tools::unSerialize(serialize($var)); } catch (Exception $e) { $tmp = $this->getVarData($var); } $size = memory_get_usage() - $start_memory; return $size; } private function getVarData($var) { if (is_object($var)) { return $var; } return (string)$var; } protected function processProfilingData() { global $start_time; // Including a lot of files uses memory foreach (get_included_files() as $file) { $this->total_filesize += filesize($file); } // Sum querying time foreach (Db::getInstance()->queries as $data) { $this->total_query_time += $data['time']; } foreach ($GLOBALS as $key => $value) { if ($key != 'GLOBALS') { $this->total_global_var_size += ($size = $this->getVarSize($value)); if ($size > 1024) { $this->global_var_size[$key] = round($size / 1024); } } } arsort($this->global_var_size); $cache = Cache::retrieveAll(); $this->total_cache_size = $this->getVarSize($cache); // Retrieve module perfs $result = Db::getInstance()->ExecuteS(' SELECT * FROM '._DB_PREFIX_.'modules_perfs WHERE session = '.(int)Module::$_log_modules_perfs_session.' AND time_start >= '.(float)$start_time.' AND time_end <= '.(float)$this->profiler[count($this->profiler) - 1]['time'] ); foreach ($result as $row) { $tmp_time = $row['time_end'] - $row['time_start']; $tmp_memory = $row['memory_end'] - $row['memory_start']; $this->total_modules_time += $tmp_time; $this->total_modules_memory += $tmp_memory; if (!isset($this->modules_perfs[$row['module']])) { $this->modules_perfs[$row['module']] = array('time' => 0, 'memory' => 0, 'methods' => array()); } $this->modules_perfs[$row['module']]['time'] += $tmp_time; $this->modules_perfs[$row['module']]['methods'][$row['method']]['time'] = $tmp_time; $this->modules_perfs[$row['module']]['memory'] += $tmp_memory; $this->modules_perfs[$row['module']]['methods'][$row['method']]['memory'] = $tmp_memory; if (!isset($this->hooks_perfs[$row['method']])) { $this->hooks_perfs[$row['method']] = array('time' => 0, 'memory' => 0, 'modules' => array()); } $this->hooks_perfs[$row['method']]['time'] += $tmp_time; $this->hooks_perfs[$row['method']]['modules'][$row['module']]['time'] = $tmp_time; $this->hooks_perfs[$row['method']]['memory'] += $tmp_memory; $this->hooks_perfs[$row['method']]['modules'][$row['module']]['memory'] = $tmp_memory; } uasort($this->modules_perfs, 'prestashop_querytime_sort'); uasort($this->hooks_perfs, 'prestashop_querytime_sort'); $queries = Db::getInstance()->queries; uasort($queries, 'prestashop_querytime_sort'); foreach ($queries as $data) { $query_row = array( 'time' => $data['time'], 'query' => $data['query'], 'location' => str_replace('\\', '/', substr($data['stack'][0]['file'], strlen(_PS_ROOT_DIR_))).':'.$data['stack'][0]['line'], 'filesort' => false, 'rows' => 1, 'group_by' => false, 'stack' => array(), ); if (preg_match('/^\s*select\s+/i', $data['query'])) { $explain = Db::getInstance()->executeS('explain '.$data['query']); if (stristr($explain[0]['Extra'], 'filesort')) { $query_row['filesort'] = true; } foreach ($explain as $row) { $query_row['rows'] *= $row['rows']; } if (stristr($data['query'], 'group by') && !preg_match('/(avg|count|min|max|group_concat|sum)\s*\(/i', $data['query'])) { $query_row['group_by'] = true; } } array_shift($data['stack']); foreach ($data['stack'] as $call) { $query_row['stack'][] = str_replace('\\', '/', substr($call['file'], strlen(_PS_ROOT_DIR_))).':'.$call['line']; } $this->array_queries[] = $query_row; } uasort(ObjectModel::$debug_list, create_function('$a,$b', 'return (count($a) < count($b)) ? 1 : -1;')); arsort(Db::getInstance()->tables); arsort(Db::getInstance()->uniqQueries); } protected function displayProfilingLinks() { echo '
  1. Stopwatch SQL
  2. Doubles
  3. Tables stress
  4. '.(isset(ObjectModel::$debug_list) ? '
  5. ObjectModel instances
  6. ' : '').'
  7. Included Files
'; } protected function displayProfilingStyle() { echo ' '; } protected function displayProfilingSummary() { global $start_time; echo '
'; foreach ($this->global_var_size as $global => $size) { echo ''; } echo '
Load Time'.$this->getLoadTimeColor($this->profiler[count($this->profiler) - 1]['time'] - $start_time, true).'
Querying Time'.$this->getTotalQueriyingTimeColor(round(1000 * $this->total_query_time)).' ms
Queries'.$this->getNbQueriesColor(count($this->array_queries)).'
Memory Peak Usage'.$this->getPeakMemoryColor($this->profiler[count($this->profiler) - 1]['peak_memory_usage']).' Mb
Included Files'.count(get_included_files()).' files - '.$this->getMemoryColor($this->total_filesize).' Mb
PrestaShop Cache'.$this->getMemoryColor($this->total_cache_size).' Mb
Global vars'.$this->getMemoryColor($this->total_global_var_size).' Mb
'; } protected function displayProfilingConfiguration() { echo '
PrestaShop Version'._PS_VERSION_.'
PHP Version'.$this->getPhpVersionColor(phpversion()).'
MySQL Version'.$this->getMySQLVersionColor(Db::getInstance()->getVersion()).'
Memory Limit'.ini_get('memory_limit').'
Max Execution Time'.ini_get('max_execution_time').'s
Smarty Cacheenabled' : 'red">disabled').'
Smarty Compilationnever recompile' : (Configuration::get('PS_SMARTY_FORCE_COMPILE') == 1 ? '#EF8B00">auto' : 'red">force compile')).'
'; } protected function displayProfilingRun() { global $start_time; echo '
'; $last = array('time' => $start_time, 'memory_usage' => 0); foreach ($this->profiler as $row) { if ($row['block'] == 'checkAccess' && $row['time'] == $last['time']) { continue; } echo ''; $last = $row; } echo '
 TimeCumulated TimeMemory UsageMemory Peak Usage
'.$row['block'].' '.$this->getLoadTimeColor($row['time'] - $last['time']).' ms '.$this->getLoadTimeColor($row['time'] - $start_time).' ms '.$this->getMemoryColor($row['memory_usage'] - $last['memory_usage']).' Mb '.$this->getMemoryColor($row['peak_memory_usage']).' Mb
'; } protected function displayProfilingHooks() { $count_hooks = count($this->hooks_perfs); echo '
'; foreach ($this->hooks_perfs as $hook => $hooks_perfs) { echo ' '; foreach ($hooks_perfs['modules'] as $module => $perfs) { echo ' '; } } echo '
Hook Time Memory Usage
'.$hook.' '.$this->getLoadTimeColor($hooks_perfs['time']).' ms '.$this->getMemoryColor($hooks_perfs['memory']).' Mb
'.($count_hooks == 1 ? '1 hook' : (int)$count_hooks.' hooks').' '.$this->getLoadTimeColor($this->total_modules_time).' ms '.$this->getMemoryColor($this->total_modules_memory).' Mb
'; } protected function displayProfilingModules() { $count_modules = count($this->modules_perfs); echo '
'; foreach ($this->modules_perfs as $module => $modules_perfs) { echo ' '; foreach ($modules_perfs['methods'] as $hook => $perfs) { echo ' '; } } echo '
Module Time Memory Usage
'.$module.' '.$this->getLoadTimeColor($modules_perfs['time']).' ms '.$this->getMemoryColor($modules_perfs['memory']).' Mb
'.($count_modules == 1 ? '1 module' : (int)$count_modules.' modules').' '.$this->getLoadTimeColor($this->total_modules_time).' ms '.$this->getMemoryColor($this->total_modules_memory).' Mb
'; } protected function displayProfilingStopwatch() { echo '

Stopwatch SQL - '.count($this->array_queries).' queries

'; foreach ($this->array_queries as $data) { $callstack = implode('
', $data['stack']); $callstack_md5 = md5($callstack); echo ' '; } echo '
Query Time (ms) Rows Filesort Group By Location
'.preg_replace("/(^[\s]*)/m", "", htmlspecialchars($data['query'], ENT_NOQUOTES, 'utf-8', false)).'
getTimeColor($data['time'] * 1000).'>'.(round($data['time'] * 1000, 1) < 0.1 ? '< 1' : round($data['time'] * 1000, 1)).' '.(int)$data['rows'].' '.($data['filesort'] ? 'Yes' : '').' '.($data['group_by'] ? 'Yes' : '').' '.$data['location'].'
'; } protected function displayProfilingDoubles() { echo '

Doubles

'; foreach (Db::getInstance()->uniqQueries as $q => $nb) { if ($nb > 1) { echo ''; } } echo '
getQueryColor($nb).'>'.$nb.'
'.$q.'
'; } protected function displayProfilingTableStress() { echo '

Tables stress

'; foreach (Db::getInstance()->tables as $table => $nb) { echo ''; } echo '
getTableColor($nb).'>'.$nb.' '.$table.'
'; } protected function displayProfilingObjectModel() { echo '

ObjectModel instances

'; foreach (ObjectModel::$debug_list as $class => $info) { echo ''; } echo '
NameInstancesSource
'.$class.' getObjectModelColor(count($info)).'>'.count($info).' '; foreach ($info as $trace) { echo str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']).' ['.$trace['line'].']
'; } echo '
'; } protected function displayProfilingFiles() { $i = 0; echo '

Included Files

'; foreach (get_included_files() as $file) { $file = str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file)); if (strpos($file, '/tools/profiling/') === 0) { continue; } echo ''; } echo '
#Filename
'.(++$i).''.$file.'
'; } public function displayProfiling() { if (!empty($this->redirect_after)) { echo '
'; } else { // Call original display method $this->display(); $this->profiler[] = $this->stamp('display'); } // Process all profiling data $this->processProfilingData(); // Add some specific style for profiling information $this->displayProfilingStyle(); echo '
'; echo '
'; $this->displayProfilingSummary(); $this->displayProfilingConfiguration(); $this->displayProfilingRun(); echo '
'; $this->displayProfilingHooks(); $this->displayProfilingModules(); $this->displayProfilingLinks(); echo '
'; $this->displayProfilingStopwatch(); $this->displayProfilingDoubles(); $this->displayProfilingTableStress(); if (isset(ObjectModel::$debug_list)) { $this->displayProfilingObjectModel(); } $this->displayProfilingFiles(); if (!empty($this->redirect_after)) { echo '
'; } } } function prestashop_querytime_sort($a, $b) { if ($a['time'] == $b['time']) { return 0; } return ($a['time'] > $b['time']) ? -1 : 1; }