<?php
/**
* Class profiler
*
*
* @copyright software distributed under the PHP License
*
* @author Piotr Jasiulewicz pjasiulewicz[at]gmail.com
*
* @example
*
* Start the profiling
* profiler::getInstance(true);
*
* Part profile start
* profiler::getInstance()->start();
*
* execute something..
*
* Part profile end
* profiler::getInstance()->end(profiler::TYPE_SQL, 'Dome profiel description eg. SELECT * FROM USERS');
*
* Get result table
* echo profiler::getResult();
*
*/
class profiler {
/**
* @var Type constants
*
* Define various types you might want to profile
*
*/
const TYPE_PLAIN = 1;
const TYPE_SQL = 2;
const TYPE_CACHE = 3;
/**
* @var TYPE_NAMES
*
* Define the names for your various profiel types
*
*/
private $TYPE_NAMES = array(1 => '-', 2 => 'DB', 3 => 'CACHE');
/**
* @const PRECISION
*
* Defines the floating point precision od time duration displayed
*
*/
const PRECISION = 6;
/**
* Singleton pattern reinfercement
*
* @var profiler object
*/
static private $_instance;
/**
* Indicates debug mode
*
* @var bool
*/
private $enabled;
/**
* Start of time measurement
*
* @var float
*/
private $timestamp_start;
/**
* End of time measurement
*
* @var bool
*/
private $timestamp_end;
/**
* Private SPL ArrayObject with all profiled events strored
*
* @var ArrayObject
*/
private $events;
/**
* Holds the currently profiled event
*
* @var ArrayObject
*/
private $current_event;
/**
* Private constructor reinforces Singleton
*
* @param bool $enabled
*/
private function __construct($enabled){
$this->timestamp_start = $this->getMicrotime();
$this->enabled = $enabled;
$this->events = new ArrayObject();
return $this;
}
/**
* Function returns instance
*
* @param bool $enabled Indicates if the profiler is enabled od not
* @return profiler object
*/
static public function getInstance($enabled = false){
if(is_null(self::$_instance)){
self::$_instance = new profiler($enabled);
}
return self::$_instance;
}
/**
* Returns the current microtime
*
* @return float
*/
private function getMicrotime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
/**
* Function starts part profiling
*
*/
public function start(){
$this->current_event = new ArrayObject();
$this->current_event['start'] = $this->getMicrotime();
}
/**
* Stop of part profile
*
* @param profiler::[TYPE_PLAIN|TYPE_SQL|TYPE_CACHE $type
*
* @param string $description
*/
public function stop($description = '', $type = TYPE_PLAIN){
if(!$this->enabled) return false;
if(!empty($this->current_event)){
$this->current_event['type'] = $type;
$this->current_event['description'] = $description;
$this->current_event['end'] = $this->getMicrotime();
$this->current_event['duration'] = $this->current_event['end'] - $this->current_event['start'];
$this->events[] =$this->current_event;
unset($this->current_event);
}else{
throw new Exception('Part profile not started');
}
}
/**
* Method getResult()
*
* @return unknown
*/
public function getResult(){
if(!$this->enabled) return false;
$this->timestamp_end = $this->getMicrotime();
$duration_total = number_format(($this->timestamp_end - $this->timestamp_start),8,',',' ');
$first_element = reset($this->events);
$last_element=end($this->events);
$duration_total = $this->timestamp_end - $this->timestamp_start;
if(count($this->events)==1){
$duration_profiled = $first_element['duration'];
} else {
$duration_profiled = 0;
foreach($this->events as $event){
$duration_profiled += $event['duration'];
}
}
$duration_rest =$duration_total - $duration_profiled;
$res='
<style type="text/css">
#profiler {
font-family:Verdana;
width:900px !important;
color:#red;
background-color:#FFFFEF;
}
#profiler th {
font-size:12px;
}
#profiler td {
font-size:11px;
border-top:1px solid grey;
text-align:center;
padding:2px 0 2px 2px;
}
#profiler .profiler_td0 {
width:20px;
padding:0;
}
#profiler .profiler_td1 {
width:80px;
}
#profiler .profiler_td2 {
width:80px;
font-weight:700;
}
#profiler .profiler_td3 {
width:50px;
font-weight:700;
}
#profiler .profiler_td4 {
width:500px;
text-align:left;
}
/*********** Profile Type Colors ***********/
#profiler .profiler_color1 {
color:#AFAFAF;
}
#profiler .profiler_color2 {
color:#DF3737;
}
#profiler .profiler_color3 {
color:#599F5C;
}
</style>
';
$res .= '<table id="profiler">';
if(!empty($this->events)){
$res.='<tr><th colspan="5" style="font-size:15px">PROFILER</th></tr>';
$res.='<tr><td colspan="2" style="text-align:left;">Total duration:</td><td style="border:0">'.number_format($duration_total,self::PRECISION).' s</td></tr>';
$res.='<tr><td colspan="2" style="text-align:left;">Unprofiled duration:</td><td style="border:0">'.number_format($duration_rest,self::PRECISION).' s</td></tr>';
$res.='<tr><td colspan="2" style="text-align:left;">Profiled:</td><td style="border:0">'.number_format($duration_profiled,self::PRECISION).' s</td></tr>';
$res.='<tr><td style="border:0" colspan="5"> </td></tr>';
$res.='<th></th>';
$res.='<th>timestamp</th>';
$res.='<th>duration</th>';
$res.='<th>type</th>';
$res.='<th>description</th>';
$number = 1;
foreach ($this->events as $key => $event){
$res.='<tr>';
$res.='<td class="profiler_td0">'.$number++.'</td>';
$res.='<td class="profiler_td1">'.number_format($event['start'],4,'.','').'</td>';
$res.='<td class="profiler_td2">'.number_format($event['duration'],self::PRECISION,',',' ').' s</td>';
$res.='<td class="profiler_td3 profiler_color'.$event['type'].'">'.$this->TYPE_NAMES[$event['type']].'</td>';
$res.='<td class="profiler_td4 profiler_color'.$event['type'].'" >'.$event['description'].'</td>';
$res.='</tr>';
}
$res.='</table>';
}
return $res;
}
}