<?php
/**
* @version $Id: performance.class.php 2307 2019-10-28 19:27:02Z IOS $
* @package Elxis
* @subpackage Debug
* @copyright Copyright (c) 2006-2019 Elxis CMS (http://www.elxis.org). All rights reserved.
* @license Elxis Public License ( http://www.elxis.org/elxis-public-license.html )
* @author Elxis Team ( http://www.elxis.org )
* @description Elxis CMS is free software. Read the license for copyright notices and details
*/
defined('_ELXIS_') or die ('Direct access to this location is not allowed');
class elxisPerformance {
private $blocks = array();
private $global_time = 0;
private $block_time = 0;
private $current_block = '';
private $sys_errors = 0;
private $sys_queries = 0;
private $sys_sql = array();
private $blocks_sql = array();
/*********************/
/* MAGIC CONSTRUCTOR */
/*********************/
public function __construct() {
if (defined('ELXIS_DEFENDER_DT') && (ELXIS_DEFENDER_DT > 0)) {
$dt2 = (defined('ELXIS_DEFENDER_DT2')) ? ELXIS_DEFENDER_DT2 : 0;
$this->global_time = microtime(true) - (ELXIS_DEFENDER_DT + $dt2);
$dt = (ELXIS_DEFENDER_DT + $dt2) * 1000;
$dt = number_format($dt, 2, '.', '');
$this->blocks['Elxis_Defender'] = array(
'file' => 'includes/libraries/elxis/defender.class.php',
'exec_time' => $dt,
'db_queries' => 0,
'errors' => 0
);
} else {
$this->global_time = microtime(true);
}
}
/**************************/
/* START BLOCK MONITORING */
/**************************/
public function startBlock($name, $file='') {
$this->block_time = microtime(true);
if ($name == '') { $name = 'Block_'.$this->block_time; }
$this->current_block = $name;
$this->blocks[$name] = array(
'file' => $file,
'exec_time' => 0,
'db_queries' => 0,
'errors' => 0
);
}
/*************************/
/* STOP BLOCK MONITORING */
/*************************/
public function stopBlock() {
if ($this->current_block == '') { return; }
$name = $this->current_block;
$this->blocks[$name]['exec_time'] = $this->stopBenchmark($this->block_time, microtime(true));
$this->current_block = '';
}
/****************************************/
/* INCREASE CURRENT BLOCK ERRORS BY ONE */
/****************************************/
public function addError() {
if ($this->current_block == '') {
$this->sys_errors++;
return;
}
$name = $this->current_block;
$this->blocks[$name]['errors']++;
}
/********************************************/
/* INCREASE CURRENT BLOCK DB QUERIES BY ONE */
/********************************************/
public function addQuery($sql='') {
if ($this->current_block == '') {
$this->sys_queries++;
if ($sql != '') { $this->sys_sql[] = $sql; }
return;
}
$name = $this->current_block;
$this->blocks[$name]['db_queries']++;
if ($sql != '') {
if (!isset($this->blocks_sql[$name])) {
$this->blocks_sql[$name] = array();
}
$this->blocks_sql[$name][] = $sql;
}
}
/*******************************/
/* GENERATE PERFORMANCE REPORT */
/*******************************/
private function getReport() {
$report = array();
$report['global'] = array(
'file' => ELXIS_SELF,
'exec_time' => $this->stopBenchmark($this->global_time, microtime(true)),
'db_queries' => $this->sys_queries,
'errors' => $this->sys_errors
);
$report['system'] = array(
'file' => 'includes/loader.php',
'exec_time' => $report['global']['exec_time'],
'db_queries' => $this->sys_queries,
'errors' => $this->sys_errors
);
if ($this->blocks) {
foreach ($this->blocks as $name => $block) {
$report['global']['db_queries'] += $block['db_queries'];
$report['global']['errors'] += $block['errors'];
$report['system']['exec_time'] -= $block['exec_time'];
$report[$name] = $block;
}
}
return $report;
}
/********************************************/
/* GENERATE PERFORMANCE MONITOR HTML REPORT */
/********************************************/
public function makeReport($debuglevel=0) {
$eLang = eFactory::getLang();
$report = $this->getReport();
$str = '<div class="elx5_box elx5_border_orange">'."\n";
$str .= '<div class="elx5_box_body">'."\n";
$str .= '<table dir="'.$eLang->getinfo('DIR').'" class="elx5_datatable">'."\n";
$str .= '<tr><th colspan="5" class="elx5_themphasis">'.$eLang->get('ELX_PERF_MONITOR').'</th></tr>'."\n";
$str .= "<tr>\n";
$str .= "\t".'<th>'.$eLang->get('ITEM').'</th>'."\n";
$str .= "\t".'<th class="elx5_tabhide">'.$eLang->get('INIT_FILE').'</th>'."\n";
$str .= "\t".'<th>'.$eLang->get('EXEC_TIME').'</th>'."\n";
$str .= "\t".'<th class="elx5_center elx5_lmobhide">'.$eLang->get('DB_QUERIES').'</th>'."\n";
$str .= "\t".'<th class="elx5_center">'.$eLang->get('ERRORS').'</th>'."\n";
$str .= "</tr>\n";
$k = 0;
foreach ($report as $name => $data) {
$rowclass = ($name === 'global') ? 'elx_trx' : 'elx_tr'.$k;
if (strlen($data['file']) > 20) {
$file_txt = '<span title="'.$data['file'].'" style="cursor: pointer;">...'.substr($data['file'], -17).'</span>';
} else {
$file_txt = $data['file'];
}
$name = ucfirst(str_replace('_', ' ', $name));
$timestr = ($data['exec_time'] > 400) ? number_format(($data['exec_time']/1000), 2, '.', '').' sec' : $data['exec_time'].' ms';
$str .= '<tr class="'.$rowclass.'">'."\n";
$str .= "\t<td>".$name."</td>\n";
$str .= "\t<td class=\"elx5_tabhide\" dir=\"ltr\">".$file_txt."</td>\n";
$str .= "\t<td>".$timestr."</td>\n";
$str .= "\t".'<td class="elx5_center elx5_lmobhide">'.$data['db_queries']."</td>\n";
$str .= "\t".'<td class="elx5_center">'.$data['errors']."</td>\n";
$str .= "</tr>\n";
$k = 1 - $k;
}
$str .= "</table>\n";
$str .= "</div>\n</div>\n";
if ($debuglevel > 1) {
$str .= $this->makeMemoryReport();
}
if ($debuglevel > 3) {
$str .= $this->makePluginsReport();
}
$str .= $this->makeSQLReport();
return $str;
}
/****************************/
/* MAKE SQL QUERRIES REPORT */
/****************************/
private function makeSQLReport() {
if (!$this->sys_sql && !$this->blocks_sql) { return ''; }
$eLang = eFactory::getLang();
$str = '<div class="elx5_box elx5_border_orange">'."\n";
$str .= '<div class="elx5_box_body">'."\n";
$str .= '<table dir="'.$eLang->getinfo('DIR').'" class="elx5_datatable">'."\n";
$str .= '<tr><th class="elx5_themphasis">'.$eLang->get('DB_QUERIES').'</th></tr>'."\n";
if ($this->sys_sql) {
$str .= '<tr><th>System</th></tr>'."\n";
foreach ($this->sys_sql as $sql) {
$str .= '<tr><td dir="ltr">'.htmlspecialchars($sql)."</td></tr>\n";
}
}
if ($this->blocks_sql) {
foreach ($this->blocks_sql as $name => $sqls) {
if ($sqls) {
$str .= '<tr><th>'.ucfirst($name)."</th></tr>\n";
foreach ($sqls as $sql) {
$str .= '<tr><td dir="ltr">'.htmlspecialchars($sql)."</td></tr>\n";
}
}
}
}
$str .= "</table>\n";
$str .= "</div>\n</div>\n";
return $str;
}
/**************************/
/* MAKE PHP MEMORY REPORT */
/**************************/
private function makeMemoryReport() {
if (!function_exists('memory_get_usage')) { return ''; }
if (!function_exists('memory_get_peak_usage')) { return ''; }
$mu = memory_get_usage();
$mu = $this->getSize($mu);
$mup = memory_get_peak_usage();
$mup = $this->getSize($mup);
$str = '<div class="elx5_box elx5_border_orange">'."\n";
$str .= '<div class="elx5_box_body">'."\n";
$str .= '<table class="elx5_datatable">'."\n";
$str .= '<tr><th colspan="2" class="elx5_themphasis">Memory usage by PHP</th></tr>'."\n";
$str .= '<tr><th>Used memory</th><td dir="ltr">'.$mu."</td></tr>\n";
$str .= '<tr><th>Memory peak</th><td dir="ltr">'.$mup."</td></tr>\n";
$str .= "</table>\n";
$str .= "</div>\n</div>\n";
return $str;
}
/*******************************/
/* MAKE CONTENT PLUGINS REPORT */
/*******************************/
private function makePluginsReport() {
if (defined('ELXIS_ADMIN')) { return; }
$eLang = eFactory::getLang();
$data = eFactory::getPlugin()->runData();
$str = '<div class="elx5_box elx5_border_orange">'."\n";
$str .= '<div class="elx5_box_body">'."\n";
$str .= '<table dir="'.$eLang->getinfo('DIR').'" class="elx5_datatable">'."\n";
$str .= '<tr><th colspan="2" class="elx5_themphasis">'.$eLang->get('CONTENT_PLUGINS').'</th></tr>'."\n";
$str .= '<tr><th>Available plugins</th><td dir="ltr">'.implode(', ', $data['plugins'])."</td></tr>\n";
$str .= '<tr><th>Articles processed</th><td dir="ltr">'.$data['runtimes']."</td></tr>\n";
$str .= '<tr><th>Plugin executions</th><td dir="ltr">'.$data['plugintimes']."</td></tr>\n";
$str .= "</table>\n";
$str .= "</div>\n</div>\n";
return $str;
}
/******************************/
/* HUMAN FRIENDLY SIZE FORMAT */
/******************************/
private function getSize($bytes) {
if ($bytes < 1024) { return $bytes.' bytes'; }
if ($bytes < 1048576) { return round($bytes/1024, 2).' KB'; }
return round($bytes/1048576, 2).' MB';
}
/******************/
/* STOP BENCHMARK */
/******************/
private function stopBenchmark($start, $end) {
$total_time = $end - $start;
$total_time = $total_time * 1000;
return number_format($total_time, 2, '.', '');
}
}
?>