From ae52f65a393111d44cc6ac2c6b42bdc72ac6809a Mon Sep 17 00:00:00 2001 From: Andreas Schmidt Date: Tue, 11 Dec 2018 07:53:00 +0100 Subject: [PATCH 1/3] fixed for Yii 1.1.16 with PHP 5.6, single Toolbar, SQL Syntax Highlighting, options for hiding categories --- XWebDebugRouter.php | 255 ++++++++++++++++++++++++++++++++++++++----- views/debugPanel.php | 29 +++-- 2 files changed, 248 insertions(+), 36 deletions(-) diff --git a/XWebDebugRouter.php b/XWebDebugRouter.php index e1335e4..1cffe5b 100644 --- a/XWebDebugRouter.php +++ b/XWebDebugRouter.php @@ -1,5 +1,14 @@ /', '', $result, 1); + self::$_output = preg_replace('/<\\?php/', '', $result, 1); } return self::$_output; @@ -175,7 +184,36 @@ private static function dumpInternal($var, $level) { * Render debug panel to document using view and configuration parameters */ class yiiDebugPanel { - public function render($items = array(), $config = array()) { + protected static $instance = null; + protected static $output = null; + protected static $config = null; + protected static $items; + + public function setConfig($config) { + self::$config['alignLeft'] = !empty($config['alignLeft']); + self::$config['opaque'] = !empty($config['opaque']); + self::$config['fixedPos'] = !empty($config['fixedPos']); + self::$config['collapsed'] = !empty($config['collapsed']); + } + + public function setItems($items) { + self::$items = $items; + } + + public static function getInstance() { + if( null === self::$instance ) { + self::$instance = new self; + } + return self::$instance; + } + + protected function __clone() {} + + protected function __construct() {} + + public static function render() { + $items = self::$items; + $config = self::$config; $msg = "Run rendering...\n"; $alignLeft = !empty($config['alignLeft']); $opaque = !empty($config['opaque']); @@ -183,8 +221,10 @@ public function render($items = array(), $config = array()) { $collapsed = !empty($config['collapsed']); $viewFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'debugPanel.php'; - include(Yii::app()->findLocalizedFile($viewFile, 'en')); + + require(Yii::app()->findLocalizedFile($viewFile, 'en')); } + } /** @@ -198,26 +238,37 @@ public static function timestampToTime($timestamp) { return date('H:i:s.', $timestamp) . (int)(($timestamp - (int)$timestamp) * 1000000); } - public static function render($items) { + public static function render($items, $config=null) { $result = ''; $odd = true; + $categories = array(); foreach ($items as $item) { list($message, $level, $category, $timestamp) = $item; + if( !in_array($category, $categories) ) + $categories[] = $category; $message = CHtml::encode($message); // put each source file on its own line $message = implode("
", explode("\n", $message)); $time = yiiDebugTrace::timestampToTime($timestamp); $odd = !$odd; - $result .= '' . $time . '' . $level . '' . $category . '' . $message . ''; + $result .= '' . $time . '' . $level . '' . $category . '' . $message . ''; } if ($result !== '') { $result = '' . $result . ''; } - $result = '' . $result . '
TimeLevelCategoryMessage
'; + $category_controls = ''; + foreach($categories as $category) { + $category_controls .= '
'.$category.'
'; + } + + $comment = ''; + if(!is_null($config) && isset($config['comment'])) + $comment = '
'.$config['comment'].'
'.PHP_EOL; + $result = $comment.$category_controls.PHP_EOL.'' . $result . '
TimeLevelCategoryMessage
'; return $result; } @@ -230,62 +281,200 @@ public static function getInfo($data, $config = null) { } class yiiDebugDB extends yiiDebugClass { + static $count = 0; + static $cached = 0; + static $items = array(); + static $minimized = false; + static $information_schema_count = 0; + static $queryCount = 0; + static $activeRecordCount = 0; + static $config = ''; + static $textHighlighter = null; + static $highlightSql = true; + public static function getInfo($data, $config = null) { parent::getInfo($data); $result = array(); $result['panelTitle'] = 'Database Queries'; - - $count = 0; - $cached = 0; - $items = array(); + if( self::$minimized ) $result['panelTitle'].=' ('.self::$information_schema_count.' information_schema Queries)'; foreach ($data as $row) { + switch($row[2]) { + case 'system.db.ar.CActiveRecord' : self::$activeRecordCount++; break; + } if (substr($row[2], 0, 9) == 'system.db') { - $items[] = $row; - - if ($row[2] == 'system.db.CDbCommand') { + if ($row[2] == 'system.db.CDbCommand') { if (strpos($row[0], 'Querying SQL') !== false) { - $count++; + $row[0] = str_replace('Querying SQL:', 'Querying SQL:'.PHP_EOL,$row[0]); + self::$count++; } if (strpos($row[0], 'Query result found in cache') !== false) { - $cached++; + self::$cached++; + } + + if( strpos($row[0], '[INFORMATION_SCHEMA]') !== false ) { + $row[2] = $row[2].'_information'; // we modify the category + self::$information_schema_count++; + // don't log information schema if minimized is enabled + if( self::$minimized ) + return $result; + } else { + // Query Count contains only Application Queries, no DBAL (information_schema) + self::$queryCount++; } } + //PC::debug($row); + $row = self::formatLogEntry($row); + self::$items[] = $row; + //debugging in PHP Console.... PC::debug($row,'DB'); } } - if (count($items) > 0) { - $result['content'] = yiiDebugTrace::render($items); + self::$config['comment'] = 'Operations: '.self::$count.' | Application Queries: '.self::$queryCount.' | DBAL Queries: '.self::$information_schema_count.' | ActiveRecords: '.self::$activeRecordCount; + + if (count(self::$items) > 0) { + $result['content'] = yiiDebugDB::render(self::$items, self::$config); } - $result['title'] = 'DB Query: ' . $count; + $result['title'] = 'DB Ops: ' . self::$count; - if ($cached > 0) { - $result['title'] .= " ($cached found in cache)"; + if (self::$cached > 0) { + $result['title'] .= " (".self::$cached." cached)"; } return $result; } + + public static function render($items, $config=null) { + $result = ''; + $odd = true; + $categories = array(); + + foreach ($items as $item) { + list($message, $level, $category, $timestamp) = $item; + if( !in_array($category, $categories) ) + $categories[] = $category; + //$message = CHtml::encode($message); + // put each source file on its own line + $message = implode("
", explode("\n", $message)); + $time = yiiDebugTrace::timestampToTime($timestamp); + $odd = !$odd; + + $result .= '' . $time . '' . $level . '' . $category . '' . $message . ''; + } + + if ($result !== '') { + $result = '' . $result . ''; + } + + $category_controls = ''; + foreach($categories as $category) { + $category_controls .= '
'.$category.'
'; + } + + $comment = ''; + if(!is_null($config) && isset($config['comment'])) + $comment = '
'.$config['comment'].'
'.PHP_EOL; + $result = $comment.$category_controls.PHP_EOL.'' . $result . '
TimeLevelCategoryMessage
'; + + return $result; + } + + /** + * Format log entry + * + * @param array $entry + * @return array + */ + public static function formatLogEntry(array $entry) + { + // extract query from the entry + $queryString = $entry[0]; + $sqlStart = strpos($queryString, "Querying SQL:"); + // if we have no query we return the whole entry as is + if($sqlStart === FALSE) return $entry; + $sqlStart += 14; + $sqlEnd = strpos($queryString , "\nin D:"); + if(strstr($queryString, 'Bound')) { + PC::debug($sqlStart.' - '.$sqlEnd, 'queryposition'); + PC::debug($queryString, 'considered query'); + PC::debug($entry[0], 'Full Text'); + } + //$sqlEnd = strlen($queryString); + $sqlLength = $sqlEnd - $sqlStart; + + $beforeQuery = substr($queryString, 0, $sqlStart); + $afterQuery = substr($queryString, $sqlEnd); + $queryString = substr($queryString, $sqlStart, $sqlLength); + + if (false !== strpos($queryString, '. Bound with ')) + { + list($query, $params) = explode('. Bound with ', $queryString); + + $binds = array(); + $matchResult = preg_match_all("/(?[a-z0-9\.\_\-\:]+)=(?[\d\.e\-\+]+|''|'.+?(?highlight($entry[0]); + $entry[0] = $entry[0].$afterQuery; + $entry[0] = $entry[0].PHP_EOL.'Origin: '.str_replace('Bound with',PHP_EOL.'Bound with',$queryString); + } + + $entry[0] = strip_tags($entry[0], '
,'); + return $entry; + } + + + /** + * @return CTextHighlighter the text highlighter + */ + private static function getTextHighlighter() + { + if (null === self::$textHighlighter) + { + self::$textHighlighter = Yii::createComponent(array( + 'class' => 'CTextHighlighter', + 'language' => 'sql', + 'showLineNumbers' => false, + )); + } + return self::$textHighlighter; + } + } class yiiDebugTrace extends yiiDebugClass { + static $items = array(); + public static function getInfo($data, $config = null) { parent::getInfo($data); $result = array(); $result['title'] = 'App Log'; $result['panelTitle'] = 'Application Log'; - $items = array(); foreach ($data as $row) { if (substr($row[2], 0, 9) != 'system.db') - $items[] = $row; + self::$items[] = $row; } - if (count($items) > 0) { - $result['content'] = yiiDebugTrace::render($items); + if (count(self::$items) > 0) { + $result['content'] = yiiDebugTrace::render(self::$items); } return $result; @@ -427,7 +616,7 @@ public static function requestAsArray() { public static function yiiAppAsArray() { $result = Yii::app(); - return Yii::app(); + return $result; } protected static function formatArrayAsHtml($id, $values, $highlight = false) { @@ -482,6 +671,9 @@ public function init() { if ( !empty($this->_config['dbProfiling']) && $this->_isLoggerAllowed() ) { Yii::app()->db->enableProfiling = true; Yii::app()->db->enableParamLogging = true; + + // we only want to siplay once, so we call render onEndRequest + Yii::app()->onEndRequest = array('YiiDebugPanel', 'render'); } } @@ -507,8 +699,15 @@ public function processLogs($logs) { $items[] = yiiDebugDB::getInfo($logs); $items[] = yiiDebugTrace::getInfo($logs); - $panel = new yiiDebugPanel(); - $panel->render($items, $this->_config); + // new code + $panel = yiiDebugPanel::getInstance(); + $panel->setConfig($this->_config); + $panel->setItems($items); + // end new code + + // if do this + //$panel->render($items, $this->_config); + //PC::debug($this->levels); show loglevels } public function setConfig($config) { diff --git a/views/debugPanel.php b/views/debugPanel.php index df275e2..b83a05e 100644 --- a/views/debugPanel.php +++ b/views/debugPanel.php @@ -10,21 +10,20 @@ float: left; left: 0; border-right: solid 1px #000; - border-bottom: solid 1px #000; float: right; right: 0; border-left: solid 1px #000; - border-bottom: solid 1px #000; + border-bottom: solid 1px #000; top: 0; - height: 16px; + height: 26px; background-color: #eef; color: #444; padding: 1px; z-index: 65535; - font: normal 10px Arial, Helvetica, sans-serif; + font: normal 18px Arial, Helvetica, sans-serif; } .yiiWebDebugOpacity { @@ -72,11 +71,11 @@ margin-left: 5px; + display: none; } ul#yiiWebDebugToolbarItems li { margin-top: 1px; - font-size: 11px; font-weight: bold; } @@ -189,6 +188,10 @@ .yiiDebugInfoList div pre code span span { font-size: 10pt; } + + div.yiiDebugInfoList>h2 { + background-color: #eee; + }
@@ -199,10 +202,10 @@
    - -
  • [  + +
  • '.$item['title'].'' : $item['title'] ?> -  ] +  
@@ -249,6 +252,16 @@ var _curPanel = ''; var panelMaxHeight = 100; + $('input:checkbox.debug_category_trigger').on('click', function() { + var category = $(this).prop('name'); + var isChecked = $(this).prop('checked'); + + // now get table row and add toggle hidden class by checkbox + // table#debugInfo>tbody>tr + var element = 'table#debugInfo>tbody>tr.'+category; + $(element).toggleClass('hidden'); + }); + //Selector function function _$(element) { return document.getElementById(element); } From 0e21865486cda8152f7a8ea2946b3d41bc530ab4 Mon Sep 17 00:00:00 2001 From: Andreas Schmidt Date: Wed, 12 Dec 2018 09:50:52 +0100 Subject: [PATCH 2/3] removed debug output --- XWebDebugRouter.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/XWebDebugRouter.php b/XWebDebugRouter.php index 1cffe5b..2cb3392 100644 --- a/XWebDebugRouter.php +++ b/XWebDebugRouter.php @@ -325,10 +325,8 @@ public static function getInfo($data, $config = null) { self::$queryCount++; } } - //PC::debug($row); $row = self::formatLogEntry($row); self::$items[] = $row; - //debugging in PHP Console.... PC::debug($row,'DB'); } } @@ -397,11 +395,6 @@ public static function formatLogEntry(array $entry) if($sqlStart === FALSE) return $entry; $sqlStart += 14; $sqlEnd = strpos($queryString , "\nin D:"); - if(strstr($queryString, 'Bound')) { - PC::debug($sqlStart.' - '.$sqlEnd, 'queryposition'); - PC::debug($queryString, 'considered query'); - PC::debug($entry[0], 'Full Text'); - } //$sqlEnd = strlen($queryString); $sqlLength = $sqlEnd - $sqlStart; @@ -707,7 +700,6 @@ public function processLogs($logs) { // if do this //$panel->render($items, $this->_config); - //PC::debug($this->levels); show loglevels } public function setConfig($config) { From 8d3f72ca145ea66d82a7c6f5dee636d4cff4c728 Mon Sep 17 00:00:00 2001 From: Andreas Schmidt Date: Thu, 17 Jan 2019 10:00:26 +0100 Subject: [PATCH 3/3] fixed for running with autoflush per line or default - which made it depend on phpconsole or autoflush set to 1 in the last version, some cleanup --- XWebDebugRouter.php | 48 +++- views/debugPanel.php | 607 ++++++++++++++++++++++--------------------- 2 files changed, 339 insertions(+), 316 deletions(-) diff --git a/XWebDebugRouter.php b/XWebDebugRouter.php index 2cb3392..3e60af8 100644 --- a/XWebDebugRouter.php +++ b/XWebDebugRouter.php @@ -188,6 +188,7 @@ class yiiDebugPanel { protected static $output = null; protected static $config = null; protected static $items; + public static $isRendered = false; public function setConfig($config) { self::$config['alignLeft'] = !empty($config['alignLeft']); @@ -200,6 +201,10 @@ public function setItems($items) { self::$items = $items; } + public function getItems() { + return self::$items; + } + public static function getInstance() { if( null === self::$instance ) { self::$instance = new self; @@ -214,6 +219,7 @@ protected function __construct() {} public static function render() { $items = self::$items; $config = self::$config; + $msg = "Run rendering...\n"; $alignLeft = !empty($config['alignLeft']); $opaque = !empty($config['opaque']); @@ -221,8 +227,7 @@ public static function render() { $collapsed = !empty($config['collapsed']); $viewFile = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . 'debugPanel.php'; - - require(Yii::app()->findLocalizedFile($viewFile, 'en')); + require(Yii::app()->findLocalizedFile($viewFile, 'en')); } } @@ -432,7 +437,6 @@ public static function formatLogEntry(array $entry) return $entry; } - /** * @return CTextHighlighter the text highlighter */ @@ -473,7 +477,6 @@ public static function getInfo($data, $config = null) { return $result; } } - class yiiDebugTime extends yiiDebugClass { public static function getInfo($data, $config = null) { parent::getInfo($data); @@ -501,7 +504,6 @@ public static function getInfo($data, $config = null) { return $result; } } - class yiiDebugConfig extends yiiDebugClass { public static $yamlStyle = false; @@ -647,6 +649,7 @@ public static function getInfo($data, $config = null) { * and renders self output to the end of server output (after tag of document). */ class XWebDebugRouter extends CLogRoute { + private $isRegistered = false; public $allowedIPs = array('127.0.0.1', '::1'); // IPv4 and IPv6 localhost addresses private $_config = array( @@ -661,12 +664,13 @@ class XWebDebugRouter extends CLogRoute { public function init() { parent::init(); - if ( !empty($this->_config['dbProfiling']) && $this->_isLoggerAllowed() ) { - Yii::app()->db->enableProfiling = true; - Yii::app()->db->enableParamLogging = true; - - // we only want to siplay once, so we call render onEndRequest - Yii::app()->onEndRequest = array('YiiDebugPanel', 'render'); + if( $this->_isLoggerAllowed() ) { + if ( !empty($this->_config['dbProfiling']) ) { + Yii::app()->db->enableProfiling = true; + Yii::app()->db->enableParamLogging = true; + } + $panel = yiiDebugPanel::getInstance(); + $panel->setConfig($this->_config); } } @@ -695,11 +699,27 @@ public function processLogs($logs) { // new code $panel = yiiDebugPanel::getInstance(); $panel->setConfig($this->_config); + $panel->setItems($items); + + // If any Logger sets autoFlush to 1 (like PHPConsole) we don't want to render here, instead we wait for onEndRequest + // actually we need to find out if we render more than once - onEndRequest would be great actually + // probably another way would be to see if log count is same as autoflush + $autoFlush = Yii::getLogger()->autoFlush; + if( $autoFlush == 1 ) { + // we only want to register the event if it's not done already + if(!($this->isRegistered)) { + // this event is not raised, without phpconsole (or autoflush before end) - doesn't make any sense + // even registering it in init doesn't work + Yii::app()->onEndRequest = array('YiiDebugPanel', 'render'); + $this->isRegistered = true; + } + } else { + // without php console we have to render here, as processlogs happens after onEndRequest + // problem - with php console it renders here twice (on first entry and later on endrequest event) + $panel->render($items, $this->_config); + } // end new code - - // if do this - //$panel->render($items, $this->_config); } public function setConfig($config) { diff --git a/views/debugPanel.php b/views/debugPanel.php index b83a05e..3f797c2 100644 --- a/views/debugPanel.php +++ b/views/debugPanel.php @@ -1,303 +1,306 @@ - - -
- - - - -
    - -
  • |  - '.$item['title'].'' : $item['title'] ?> -   -
  • - -
- - - - -
- -
- - - - - -
- - \ No newline at end of file