* @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 '
';
}
protected function displayProfilingStyle()
{
echo '
';
}
protected function displayProfilingSummary()
{
global $start_time;
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 ';
foreach ($this->global_var_size as $global => $size) {
echo '- global $'.$global.' '.$size.'k ';
}
echo '
';
}
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 Cache enabled' : 'red">disabled').'
Smarty Compilation never recompile' : (Configuration::get('PS_SMARTY_FORCE_COMPILE') == 1 ? '#EF8B00">auto' : 'red">force compile')).'
';
}
protected function displayProfilingRun()
{
global $start_time;
echo '
Time Cumulated Time Memory Usage Memory Peak Usage ';
$last = array('time' => $start_time, 'memory_usage' => 0);
foreach ($this->profiler as $row) {
if ($row['block'] == 'checkAccess' && $row['time'] == $last['time']) {
continue;
}
echo '
'.$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
';
$last = $row;
}
echo '
';
}
protected function displayProfilingHooks()
{
$count_hooks = count($this->hooks_perfs);
echo '
Hook
Time
Memory Usage
';
foreach ($this->hooks_perfs as $hook => $hooks_perfs) {
echo '
'.$hook.'
'.$this->getLoadTimeColor($hooks_perfs['time']).' ms
'.$this->getMemoryColor($hooks_perfs['memory']).' Mb
';
foreach ($hooks_perfs['modules'] as $module => $perfs) {
echo '
=> '.$module.'
'.$this->getLoadTimeColor($perfs['time']).' ms
'.$this->getMemoryColor($perfs['memory']).' Mb
';
}
}
echo '
'.($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 '
Module
Time
Memory Usage
';
foreach ($this->modules_perfs as $module => $modules_perfs) {
echo '
'.$module.'
'.$this->getLoadTimeColor($modules_perfs['time']).' ms
'.$this->getMemoryColor($modules_perfs['memory']).' Mb
';
foreach ($modules_perfs['methods'] as $hook => $perfs) {
echo '
=> '.$hook.'
'.$this->getLoadTimeColor($perfs['time']).' ms
'.$this->getMemoryColor($perfs['memory']).' Mb
';
}
}
echo '
'.($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 '
Query
Time (ms)
Rows
Filesort
Group By
Location
';
foreach ($this->array_queries as $data) {
$callstack = implode(' ', $data['stack']);
$callstack_md5 = md5($callstack);
echo '
'.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'].'
'.implode(' ', $data['stack']).'
';
}
echo '
';
}
protected function displayProfilingDoubles()
{
echo '
';
foreach (Db::getInstance()->uniqQueries as $q => $nb) {
if ($nb > 1) {
echo 'getQueryColor($nb).'>'.$nb.' '.$q.' ';
}
}
echo '
';
}
protected function displayProfilingTableStress()
{
echo '
';
foreach (Db::getInstance()->tables as $table => $nb) {
echo 'getTableColor($nb).'>'.$nb.' '.$table.' ';
}
echo '
';
}
protected function displayProfilingObjectModel()
{
echo '
Name Instances Source ';
foreach (ObjectModel::$debug_list as $class => $info) {
echo '
'.$class.'
getObjectModelColor(count($info)).'>'.count($info).'
';
foreach ($info as $trace) {
echo str_replace(array(_PS_ROOT_DIR_, '\\'), array('', '/'), $trace['file']).' ['.$trace['line'].'] ';
}
echo '
';
}
echo '
';
}
protected function displayProfilingFiles()
{
$i = 0;
echo '
# Filename ';
foreach (get_included_files() as $file) {
$file = str_replace('\\', '/', str_replace(_PS_ROOT_DIR_, '', $file));
if (strpos($file, '/tools/profiling/') === 0) {
continue;
}
echo ''.(++$i).' '.$file.' ';
}
echo '
';
}
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;
}