diff --git a/dibi/Nette/IDebugPanel.php b/dibi/Nette/IDebugPanel.php new file mode 100644 index 00000000..95755b28 --- /dev/null +++ b/dibi/Nette/IDebugPanel.php @@ -0,0 +1,44 @@ +' . $matches[4] . ''; } - - - /** - * Returns brief descriptions. - * @return string - * @return array - */ - public static function getColophon($sender = NULL) - { - $arr = array( - 'Number of SQL queries: ' . dibi::$numOfQueries - . (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms'), - ); - if ($sender === 'bluescreen') { - $arr[] = 'dibi ' . dibi::VERSION . ' (revision ' . dibi::REVISION . ')'; - } - return $arr; - } - } diff --git a/dibi/libs/DibiConnection.php b/dibi/libs/DibiConnection.php index 179876c5..1d7458d4 100644 --- a/dibi/libs/DibiConnection.php +++ b/dibi/libs/DibiConnection.php @@ -42,10 +42,6 @@ class DibiConnection extends DibiObject */ public function __construct($config, $name = NULL) { - if (class_exists(/*Nette\*/'Debug', FALSE)) { - /*Nette\*/Debug::addColophon(array('dibi', 'getColophon')); - } - // DSN string if (is_string($config)) { parse_str($config, $config); diff --git a/dibi/libs/DibiException.php b/dibi/libs/DibiException.php index ccdb3098..0e6491a9 100644 --- a/dibi/libs/DibiException.php +++ b/dibi/libs/DibiException.php @@ -18,7 +18,7 @@ * @copyright Copyright (c) 2005, 2010 David Grudl * @package dibi */ -class DibiException extends Exception implements /*Nette\*/IDebuggable +class DibiException extends Exception implements /*Nette\*/IDebugPanel { /** @var string */ private $sql; @@ -59,23 +59,39 @@ class DibiException extends Exception implements /*Nette\*/IDebuggable - /********************* interface Nette\IDebuggable ****************d*g**/ + /********************* interface Nette\IDebugPanel ****************d*g**/ + /** - * Returns custom panels. - * @return array + * Returns HTML code for custom tab. + * @return mixed */ - public function getPanels() + public function getTab() + { + return 'SQL'; + } + + + + /** + * Returns HTML code for custom panel. + * @return mixed + */ + public function getPanel() { - $panels = array(); - if ($this->sql !== NULL) { - $panels['SQL'] = array( - 'expanded' => TRUE, - 'content' => dibi::dump($this->sql, TRUE), - ); - } - return $panels; + return dibi::dump($this->sql, TRUE); + } + + + + /** + * Returns panel ID. + * @return string + */ + public function getId() + { + return __CLASS__; } } diff --git a/dibi/libs/DibiProfiler.php b/dibi/libs/DibiProfiler.php index 0caa34ec..3a3bb48f 100644 --- a/dibi/libs/DibiProfiler.php +++ b/dibi/libs/DibiProfiler.php @@ -18,13 +18,13 @@ * @copyright Copyright (c) 2005, 2010 David Grudl * @package dibi */ -class DibiProfiler extends DibiObject implements IDibiProfiler +class DibiProfiler extends DibiObject implements IDibiProfiler, /*Nette\*/IDebugPanel { /** maximum number of rows */ - const FIREBUG_MAX_ROWS = 30; + const MAX_ROWS = 30; /** maximum SQL length */ - const FIREBUG_MAX_LENGTH = 500; + const MAX_LENGTH = 500; /** @var string Name of the file where SQL errors should be logged */ private $file; @@ -45,6 +45,10 @@ class DibiProfiler extends DibiObject implements IDibiProfiler public function __construct() { + if (class_exists(/*Nette\*/'Debug', FALSE) && is_callable('Debug::addPanel')) { + /*Nette\*/Debug::addPanel($this); + } + $this->useFirebug = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/'); } @@ -114,28 +118,27 @@ class DibiProfiler extends DibiObject implements IDibiProfiler $count = '?'; } - if ($this->useFirebug && !headers_sent()) { - if (count(self::$table) < self::FIREBUG_MAX_ROWS) { - self::$table[] = array( - sprintf('%0.3f', dibi::$elapsedTime * 1000), - strlen($sql) > self::FIREBUG_MAX_LENGTH ? substr($sql, 0, self::FIREBUG_MAX_LENGTH) . '...' : $sql, - $count, - $connection->getConfig('driver') . '/' . $connection->getConfig('name') - ); - } + if (count(self::$table) < self::MAX_ROWS) { + self::$table[] = array( + sprintf('%0.3f', dibi::$elapsedTime * 1000), + strlen($sql) > self::MAX_LENGTH ? substr($sql, 0, self::MAX_LENGTH) . '...' : $sql, + $count, + $connection->getConfig('driver') . '/' . $connection->getConfig('name') + ); + } + if ($this->useFirebug && !headers_sent()) { header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2'); header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0'); header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'); - $payload = array( + $payload = json_encode(array( array( 'Type' => 'TABLE', 'Label' => 'dibi profiler (' . dibi::$numOfQueries . ' SQL queries took ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms)', ), self::$table, - ); - $payload = json_encode($payload); + )); foreach (str_split($payload, 4990) as $num => $s) { $num++; header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index @@ -198,4 +201,58 @@ class DibiProfiler extends DibiObject implements IDibiProfiler fclose($handle); } + + + /********************* interface Nette\IDebugPanel ****************d*g**/ + + + + /** + * Returns HTML code for custom tab. + * @return mixed + */ + public function getTab() + { + return '' + . dibi::$numOfQueries . ' queries'; + } + + + + /** + * Returns HTML code for custom panel. + * @return mixed + */ + public function getPanel() + { + if (!dibi::$numOfQueries) return; + + $content = '

SQL queries: ' . dibi::$numOfQueries . (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms') . '

'; + if (self::$table) { + $content .= ''; + foreach (self::$table as $i => $row) { + if ($i === 0) { + $content .= ""; + } else { + $content .= ""; + } + } + $content .= '
$row[0]$row[1]$row[2]$row[3]
$row[0] $row[1]$row[2]$row[3]
'; + } else { + $content .= '

no query

'; + } + return $content; + } + + + + /** + * Returns panel ID. + * @return string + */ + public function getId() + { + return get_class($this); + } + } diff --git a/examples/Nette/Debug.php b/examples/Nette/Debug.php index 71d62d04..e6515d11 100644 --- a/examples/Nette/Debug.php +++ b/examples/Nette/Debug.php @@ -23,8 +23,7 @@ static$productionMode;public static$consoleMode;public static$time;private static$firebugDetected;private -static$ajaxDetected;private -static$consoleData;public +static$ajaxDetected;public static$maxDepth=3;public static$maxLen=150;public static$showLocation=FALSE;const @@ -40,8 +39,10 @@ static$logFile;private static$logHandle;private static$sendEmails;private static$emailHeaders=array('To'=>'','From'=>'noreply@%host%','X-Mailer'=>'Nette Framework','Subject'=>'PHP: An error occurred on the server %host%','Body'=>'[%date%] %message%');private -static$colophons=array(array(__CLASS__,'getDefaultColophons'));private -static$enabledProfiler=FALSE;public +static$enabledBar=TRUE;private +static$panels=array();private +static$dumps;private +static$errors;public static$counters=array();const LOG='LOG';const INFO='INFO';const @@ -58,316 +59,788 @@ new LogicException("Cannot instantiate static class ".get_class($this));}public static function -_init(){self::$time=microtime(TRUE);self::$consoleMode=PHP_SAPI==='cli';self::$productionMode=self::DETECT;self::$firebugDetected=isset($_SERVER['HTTP_USER_AGENT'])&&strpos($_SERVER['HTTP_USER_AGENT'],'FirePHP/');self::$ajaxDetected=isset($_SERVER['HTTP_X_REQUESTED_WITH'])&&$_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';register_shutdown_function(array(__CLASS__,'_shutdownHandler'));}public +_init(){self::$time=microtime(TRUE);self::$consoleMode=PHP_SAPI==='cli';self::$productionMode=self::DETECT;self::$firebugDetected=isset($_SERVER['HTTP_USER_AGENT'])&&strpos($_SERVER['HTTP_USER_AGENT'],'FirePHP/');self::$ajaxDetected=isset($_SERVER['HTTP_X_REQUESTED_WITH'])&&$_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';self::addPanel(new +DebugPanel('time',array(__CLASS__,'getDefaultPanel')));self::addPanel(new +DebugPanel('memory',array(__CLASS__,'getDefaultPanel')));self::addPanel(new +DebugPanel('errors',array(__CLASS__,'getDefaultPanel')));self::addPanel(new +DebugPanel('dumps',array(__CLASS__,'getDefaultPanel')));register_shutdown_function(array(__CLASS__,'_shutdownHandler'));}public static function -_shutdownHandler(){static$types=array(E_ERROR=>1,E_CORE_ERROR=>1,E_COMPILE_ERROR=>1,E_PARSE=>1);$error=error_get_last();if(self::$enabled&&isset($types[$error['type']])){if(!headers_sent()){header('HTTP/1.1 500 Internal Server Error');}if(ini_get('html_errors')){$error['message']=html_entity_decode(strip_tags($error['message']));}self::processException(new -FatalErrorException($error['message'],0,$error['type'],$error['file'],$error['line'],NULL),TRUE);}if(self::$productionMode){return;}foreach(headers_list()as$header){if(strncasecmp($header,'Content-Type:',13)===0){if(substr($header,14,9)==='text/html'){break;}return;}}if(self::$enabledProfiler){if(self::$firebugDetected){self::fireLog('Nette profiler',self::GROUP_START);foreach(self::$colophons -as$callback){foreach((array)call_user_func($callback,'profiler')as$line)self::fireLog(strip_tags($line));}self::fireLog(NULL,self::GROUP_END);}if(!self::$ajaxDetected){$colophons=self::$colophons;?> +_shutdownHandler(){static$types=array(E_ERROR=>1,E_CORE_ERROR=>1,E_COMPILE_ERROR=>1,E_PARSE=>1);$error=error_get_last();if(self::$enabled&&isset($types[$error['type']])){if(!headers_sent()){header('HTTP/1.1 500 Internal Server Error');}if(ini_get('html_errors')){$error['message']=html_entity_decode(strip_tags($error['message']),ENT_QUOTES,'UTF-8');}self::processException(new +FatalErrorException($error['message'],0,$error['type'],$error['file'],$error['line'],NULL),TRUE);}if(self::$enabledBar&&!self::$productionMode&&!self::$ajaxDetected){foreach(headers_list()as$header){if(strncasecmp($header,'Content-Type:',13)===0){if(substr($header,14,9)==='text/html'){break;}return;}}$panels=array();foreach(self::$panels +as$panel){$panels[]=array('id'=>preg_replace('#[^a-z0-9]+#i','-',$panel->getId()),'tab'=>$tab=(string)$panel->getTab(),'panel'=>$tab?(string)$panel->getPanel():NULL);}?> - -
-
- -
-
- - -$m[2]($m[3]) ".($m[3]<7?' ':' ';}function @@ -709,12 +1187,10 @@ _netteDump(self::_dump($v,0));echo"\n";}?> - getPanels()as$name=>$panel):?> - - +IDebugPanel&&$panel=$ex->getPanel()):?> + getTab(),FALSE)?> + - @@ -826,10 +1302,17 @@ htmlspecialchars($s),'
';?> @@ -851,39 +1334,156 @@ defaultMailer($message){$host=isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST'] as$key=>$value){$header.="$key: $value\r\n";}$body=str_replace("\r\n","\n",$body);if(PHP_OS!='Linux')$body=str_replace("\n","\r\n",$body);mail($to,$subject,$body,$header);}public static function -enableProfiler(){self::$enabledProfiler=TRUE;}public +enableBar(){self::$enabledBar=TRUE;}public static function -disableProfiler(){self::$enabledProfiler=FALSE;}public +disableBar(){self::$enabledBar=FALSE;}public static function -addColophon($callback){if(!is_callable($callback)){$able=is_callable($callback,TRUE,$textual);throw -new -InvalidArgumentException("Colophon handler '$textual' is not ".($able?'callable.':'valid PHP callback.'));}if(!in_array($callback,self::$colophons,TRUE)){self::$colophons[]=$callback;}}private +addPanel(IDebugPanel$panel){self::$panels[]=$panel;}public static function -getDefaultColophons($sender){if($sender==='profiler'){$arr[]='Elapsed time: '.number_format((microtime(TRUE)-self::$time)*1000,1,'.',' ').' ms | Allocated memory: '.number_format(memory_get_peak_usage()/1000,1,'.',' ').' kB';foreach((array)self::$counters -as$name=>$value){if(is_array($value))$value=implode(', ',$value);$arr[]=htmlSpecialChars($name).' = '.htmlSpecialChars($value).'';}$autoloaded=class_exists('AutoLoader',FALSE)?AutoLoader::$count:0;$s=''.count(get_included_files()).'/'.$autoloaded.' files, ';$exclude=array('stdClass','Exception','ErrorException','Traversable','IteratorAggregate','Iterator','ArrayAccess','Serializable','Closure');foreach(get_loaded_extensions()as$ext){$ref=new -ReflectionExtension($ext);$exclude=array_merge($exclude,$ref->getClassNames());}$classes=array_diff(get_declared_classes(),$exclude);$intf=array_diff(get_declared_interfaces(),$exclude);$func=get_defined_functions();$func=(array)@$func['user'];$consts=get_defined_constants(TRUE);$consts=array_keys((array)@$consts['user']);foreach(array('classes','intf','func','consts')as$item){$s.=''.count($$item).' '.$item.', ';}$arr[]=$s;}if($sender==='bluescreen'){$arr[]='Report generated at '.@date('Y/m/d H:i:s',self::$time);if(isset($_SERVER['HTTP_HOST'],$_SERVER['REQUEST_URI'])){$url=(isset($_SERVER['HTTPS'])&&strcasecmp($_SERVER['HTTPS'],'off')?'https://':'http://').htmlSpecialChars($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);$arr[]=''.$url.'';}$arr[]='PHP '.htmlSpecialChars(PHP_VERSION);if(isset($_SERVER['SERVER_SOFTWARE']))$arr[]=htmlSpecialChars($_SERVER['SERVER_SOFTWARE']);if(class_exists('Framework'))$arr[]=htmlSpecialChars('Nette Framework '.Framework::VERSION).' (revision '.htmlSpecialChars(Framework::REVISION).')';}return$arr;}public -static -function -fireDump($var,$key){self::fireSend(2,array((string)$key=>$var));return$var;}public +getDefaultPanel($id){switch($id){case'time:tab':?>ms + kB +variables +$m[2]($m[3]) ".($m[3]<7?' ':' ');}}?> + + + +

Dumped variables

+ +
+ + +

+ + + + + $dump):?> + + + + + +
|\s+)?(.*)\((\d+)\) #','_netteDumpCb2',$dump)?>
+ +
+ + errors +

Errors

+ + + + +$item):?> + + + + +
+get_class($message),'Message'=>$message->getMessage(),'File'=>$message->getFile(),'Line'=>$message->getLine(),'Trace'=>$message->getTrace(),'Type'=>'','Function'=>'');foreach($message['Trace']as&$row){if(empty($row['file']))$row['file']='?';if(empty($row['line']))$row['line']='?';}}elseif($priority===self::GROUP_START){$label=$message;$message=NULL;}return -self::fireSend(1,self::replaceObjects(array(array('Type'=>$priority,'Label'=>$label),$message)));}private +self::fireSend('FirebugConsole/0.1',self::replaceObjects(array(array('Type'=>$priority,'Label'=>$label),$message)));}private static function -fireSend($index,$payload){if(self::$productionMode)return +fireSend($struct,$payload){if(self::$productionMode)return NULL;if(headers_sent())return -FALSE;header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');if($index===1){header('X-Wf-nette-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');}elseif($index===2){header('X-Wf-nette-Structure-2: http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');}$payload=json_encode($payload);static$counter;foreach(str_split($payload,4990)as$s){$num=++$counter;header("X-Wf-nette-$index-1-n$num: |$s|\\");}header("X-Wf-nette-$index-1-n$num: |$s|");return +FALSE;header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');static$structures;$index=isset($structures[$struct])?$structures[$struct]:($structures[$struct]=count($structures)+1);header("X-Wf-nette-Structure-$index: http://meta.firephp.org/Wildfire/Structure/FirePHP/$struct");$payload=json_encode($payload);static$counter;foreach(str_split($payload,4990)as$s){$num=++$counter;header("X-Wf-nette-$index-1-n$num: |$s|\\");}header("X-Wf-nette-$index-1-n$num: |$s|");return TRUE;}static private function replaceObjects($val){if(is_object($val)){return'object '.get_class($val).'';}elseif(is_string($val)){return@iconv('UTF-16','UTF-8//IGNORE',iconv('UTF-8','UTF-16//IGNORE',$val));}elseif(is_array($val)){foreach($val -as$k=>$v){unset($val[$k]);$k=@iconv('UTF-16','UTF-8//IGNORE',iconv('UTF-8','UTF-16//IGNORE',$k));$val[$k]=self::replaceObjects($v);}}return$val;}}Debug::_init();if(!function_exists('dump')){function +as$k=>$v){unset($val[$k]);$k=@iconv('UTF-16','UTF-8//IGNORE',iconv('UTF-8','UTF-16//IGNORE',$k));$val[$k]=self::replaceObjects($v);}}return$val;}}interface +IDebugPanel{function +getTab();function +getPanel();function +getId();}class +DebugPanel +implements +IDebugPanel{private$id;private$callback;public +function +__construct($id,$callback){$this->id=$id;$this->callback=$callback;}public +function +getId(){return$this->id;}public +function +getTab(){ob_start();call_user_func($this->callback,"$this->id:tab");return +ob_get_clean();}public +function +getPanel(){ob_start();call_user_func($this->callback,"$this->id:panel");return +ob_get_clean();}}Debug::_init();if(!function_exists('dump')){function dump($var){foreach($args=func_get_args()as$arg)Debug::dump($arg);return$var;}}class FatalErrorException extends diff --git a/examples/nette-debug.php b/examples/nette-debug.php index 20660f95..5c58761a 100644 --- a/examples/nette-debug.php +++ b/examples/nette-debug.php @@ -1,3 +1,5 @@ + +

Nette\Debug & dibi example

diff --git a/examples/nette-debug2.php b/examples/nette-debug2.php index 003c6635..ebe6556e 100644 --- a/examples/nette-debug2.php +++ b/examples/nette-debug2.php @@ -1,3 +1,5 @@ + +

Nette\Debug & dibi example 2

@@ -25,4 +27,4 @@ dibi::connect(array( // throws error -Debug::consoleDump( dibi::fetchAll('SELECT * FROM [customers] WHERE [customer_id] < %i', 38), '[customers]' ); +Debug::barDump( dibi::fetchAll('SELECT * FROM [customers] WHERE [customer_id] < %i', 38), '[customers]' );