96212ec5608b3aa9280d3db48bbb39442d07528e
Author: chawbacca
Date: 2009-03-26 09:58:39 -0500
diff --git a/controllers/components/toolbar.php b/controllers/components/toolbar.php
index 7028fd8..9b65a71 100644
--- a/controllers/components/toolbar.php
+++ b/controllers/components/toolbar.php
@@ -92,7 +92,7 @@ class ToolbarComponent extends Object {
return false;
}
App::import('Vendor', 'DebugKit.DebugKitDebugger');
-
+
DebugKitDebugger::startTimer('componentInit', __('Component initialization and startup', true));
$panels = $this->_defaultPanels;
@@ -104,14 +104,14 @@ class ToolbarComponent extends Object {
if (!isset($settings['history']) || (isset($settings['history']) && $settings['history'] !== false)) {
$this->_createCacheConfig();
}
-
+
if (isset($settings['javascript'])) {
$settings['javascript'] = $this->_setJavascript($settings['javascript']);
} else {
$settings['javascript'] = $this->_defaultJavascript;
}
$this->_loadPanels($panels, $settings);
-
+
$this->_set($settings);
$this->controller =& $controller;
return false;
@@ -127,7 +127,7 @@ class ToolbarComponent extends Object {
$this->_makeViewClass($currentViewClass);
$controller->view = 'DebugKit.Debug';
$isHtml = (
- !isset($controller->params['url']['ext']) ||
+ !isset($controller->params['url']['ext']) ||
(isset($controller->params['url']['ext']) && $controller->params['url']['ext'] == 'html')
);
@@ -202,7 +202,7 @@ class ToolbarComponent extends Object {
*
* @return array Array of all panel beforeRender()
* @access protected
- **/
+ **/
function _gatherVars(&$controller) {
$vars = array();
$panels = array_keys($this->panels);
@@ -282,7 +282,7 @@ class ToolbarComponent extends Object {
* Makes the DoppleGangerView class if it doesn't already exist.
* This allows DebugView to be compatible with all view classes.
*
- * @param string $baseClassName
+ * @param string $baseClassName
* @access protected
* @return void
*/
@@ -372,7 +372,7 @@ class HistoryPanel extends DebugPanel {
var $history = 5;
/**
* Constructor
- *
+ *
* @param array $settings Array of settings.
* @return void
**/
@@ -506,6 +506,13 @@ class TimerPanel extends DebugPanel {
class sqlLogPanel extends DebugPanel {
var $plugin = 'debug_kit';
/**
+ * Minimum number of Rows Per Millisecond that must be returned by a query before an explain
+ * is done.
+ *
+ * @var int
+ **/
+ var $slowRate = 20;
+/**
* Get Sql Logs for each DB config
*
* @param string $controller
@@ -513,21 +520,87 @@ class sqlLogPanel extends DebugPanel {
* @return void
*/
function beforeRender(&$controller) {
- $queryLogs = array();
if (!class_exists('ConnectionManager')) {
return array();
}
+ App::import('Core', 'Xml');
+ $queryLogs = array();
+
$dbConfigs = ConnectionManager::sourceList();
foreach ($dbConfigs as $configName) {
$db =& ConnectionManager::getDataSource($configName);
if ($db->isInterfaceSupported('showLog')) {
ob_start();
$db->showLog();
- $queryLogs[$configName] = ob_get_clean();
+ $htmlBlob = ob_get_clean();
+
+ $Xml =& new Xml($htmlBlob);
+ $sqlLog = $Xml->toArray();
+ if (empty($sqlLog['Table']['Tbody']['Tr'])) {
+ continue;
+ }
+ $queries = $explained = array();
+ foreach ($sqlLog['Table']['Tbody']['Tr'] as $query) {
+ $tds = $this->_restructureCells($query['Td']);
+ $queries[] = $tds;
+ $isSlow = ($tds[5] > 0 && $tds[4] / $tds[5] <= $this->slowRate);
+ if ($isSlow && preg_match('/^SELECT /', $tds[1])) {
+ $explain = $this->_explainQuery($db, $tds[1]);
+ if (!empty($explain)) {
+ $explained[] = $explain;
+ }
+ }
+ }
+ $queryLogs[$configName]['queries'] = $queries;
+ $queryLogs[$configName]['explains'] = $explained;
}
}
return $queryLogs;
}
+/**
+ * Restructure a row if error cell is empty
+ *
+ * @return void
+ **/
+ function _restructureCells($tds) {
+ if (count($tds) == 5) {
+ $tds[5] = $tds[4]['value'];
+ $tds[4] = $tds[3]['value'];
+ $tds[3] = $tds[2]['value'];
+ $tds[2] = '';
+ } else {
+ $tds[2] = $tds[2]['value'];
+ $tds[3] = $tds[3]['value'];
+ $tds[4] = $tds[4]['value'];
+ $tds[5] = $tds[5]['value'];
+ }
+ return $tds;
+ }
+/**
+ * Run an explain query for a slow query.
+ *
+ * @param object $db Dbo instance
+ * @param string $queryString The Query to explain
+ * @access public
+ * @return void
+ **/
+ function _explainQuery(&$db, $queryString) {
+ $driver = $db->config['driver'];
+ $results = null;
+ if ($driver === 'myslqi' || $driver === 'mysql' || $driver === 'postgres') {
+ $results = $db->query('EXPLAIN ' . $queryString);
+ if ($driver === 'postgres') {
+ $queryPlan = array();
+ foreach ($results as $postgreValue) {
+ $queryPlan[] = $postgreValue[0]['QUERY PLAN'];
+ }
+ $results[0][0] = array('Query Plan' => implode("<br />", $queryPlan));
+ }
+ $results = $results[0][0];
+ $results['query'] = $queryString;
+ }
+ return $results;
+ }
}
/**
@@ -591,4 +664,5 @@ class LogPanel extends DebugPanel {
return array_values($chunks);
}
}
+
?>
\ No newline at end of file
diff --git a/controllers/toolbar_access_controller.php b/controllers/toolbar_access_controller.php
index 2085470..b45f2e5 100644
--- a/controllers/toolbar_access_controller.php
+++ b/controllers/toolbar_access_controller.php
@@ -72,6 +72,9 @@ class ToolbarAccessController extends DebugKitAppController {
* @return void
**/
function history_state($key = null) {
+ if (Configure::read('debug') == 0) {
+ return $this->redirect($this->referer());
+ }
$oldState = $this->Toolbar->loadState($key);
$this->set('toolbarState', $oldState);
$this->set('debugKitInHistoryMode', true);
diff --git a/tests/cases/controllers/components/toolbar.test.php b/tests/cases/controllers/components/toolbar.test.php
index ae32e41..fbfe3be 100644
--- a/tests/cases/controllers/components/toolbar.test.php
+++ b/tests/cases/controllers/components/toolbar.test.php
@@ -18,12 +18,8 @@
* @filesource
* @copyright Copyright 2006-2008, Cake Software Foundation, Inc.
* @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
- * @package cake
- * @subpackage cake.cake.libs.
- * @since CakePHP v 1.2.0.4487
- * @version $Revision$
- * @modifiedby $LastChangedBy$
- * @lastmodified $Date$
+ * @package debug_kit
+ * @subpackage debug_kit.tests
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*/
App::import('Component', 'DebugKit.Toolbar');
@@ -36,20 +32,49 @@ class TestToolbarComponent extends ToolbarComponent {
Mock::generate('DebugPanel');
+if (!class_exists('AppController')) {
+ class AppController extends Controller {
+
+ }
+}
+
/**
* DebugToolbar Test case
*/
class DebugToolbarTestCase extends CakeTestCase {
- function setUp() {
+ function startTest() {
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
$this->Controller =& ClassRegistry::init('Controller');
$this->Controller->params = Router::parse('/');
$this->Controller->params['url']['url'] = '/';
$this->Controller->Component =& ClassRegistry::init('Component');
$this->Controller->Toolbar =& ClassRegistry::init('TestToolBarComponent', 'Component');
+
+ $this->_server = $_SERVER;
+ $this->_paths = array();
+ $this->_paths['plugin'] = Configure::read('pluginPaths');
+ $this->_paths['view'] = Configure::read('viewPaths');
+ $this->_paths['vendor'] = Configure::read('vendorPaths');
+ $this->_paths['controller'] = Configure::read('controllerPaths');
}
+/**
+ * endTest
+ *
+ * @return void
+ **/
+ function endTest() {
+ $_SERVER = $this->_server;
+ Configure::write('pluginPaths', $this->_paths['plugin']);
+ Configure::write('viewPaths', $this->_paths['view']);
+ Configure::write('vendorPaths', $this->_paths['vendor']);
+ Configure::write('controllerPaths', $this->_paths['controller']);
+ unset($this->Controller);
+ if (class_exists('DebugKitDebugger')) {
+ DebugKitDebugger::clearTimers();
+ }
+ }
/**
* test Loading of panel classes
*
@@ -72,7 +97,6 @@ class DebugToolbarTestCase extends CakeTestCase {
*/
function testVendorPanels() {
$f = Configure::read('pluginPaths');
- $_back = Configure::read('vendorPaths');
Configure::write('vendorPaths', array($f[1] . 'debug_kit' . DS . 'tests' . DS . 'test_app' . DS . 'vendors' . DS));
$this->Controller->components = array(
'DebugKit.Toolbar' => array(
@@ -84,8 +108,6 @@ class DebugToolbarTestCase extends CakeTestCase {
$this->Controller->Component->startup($this->Controller);
$this->assertTrue(isset($this->Controller->Toolbar->panels['test']));
$this->assertTrue(is_a($this->Controller->Toolbar->panels['test'], 'TestPanel'));
-
- Configure::write('vendorPaths', $_back);
}
/**
@@ -356,15 +378,28 @@ class DebugToolbarTestCase extends CakeTestCase {
$this->assertEqual($this->Controller->helpers['DebugKit.Toolbar']['output'], 'DebugKit.FirePhpToolbar');
}
/**
- * teardown
+ * Test that the toolbar does not interfere with requestAction
*
* @return void
**/
- function tearDown() {
- unset($this->Controller);
- if (class_exists('DebugKitDebugger')) {
- DebugKitDebugger::clearTimers();
- }
+ function testNoRequestActionInterference() {
+ $f = Configure::read('pluginPaths');
+ $testapp = $f[1] . 'debug_kit' . DS . 'tests' . DS . 'test_app' . DS . 'controllers' . DS;
+ array_unshift($f, $testapp);
+ Configure::write('controllerPaths', $f);
+
+ $plugins = Configure::read('pluginPaths');
+ $views = Configure::read('viewPaths');
+ $testapp = $plugins[1] . 'debug_kit' . DS . 'tests' . DS . 'test_app' . DS . 'views' . DS;
+ array_unshift($views, $testapp);
+ Configure::write('viewPaths', $views);
+
+ $result = $this->Controller->requestAction('/debug_kit_test/request_action_return', array('return'));
+ $this->assertEqual($result, 'I am some value from requestAction.');
+
+ $result = $this->Controller->requestAction('/debug_kit_test/request_action_render', array('return'));
+ $this->assertEqual($result, 'I have been rendered.');
}
+
}
?>
\ No newline at end of file
diff --git a/tests/cases/views/debug.test.php b/tests/cases/views/debug.test.php
index 916a67d..17ce7c1 100644
--- a/tests/cases/views/debug.test.php
+++ b/tests/cases/views/debug.test.php
@@ -45,12 +45,32 @@ class DebugViewTestCase extends CakeTestCase {
*
* @return void
**/
- function setUp() {
+ function startTest() {
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Router::parse('/');
$this->Controller =& ClassRegistry::init('Controller');
$this->View =& new DebugView($this->Controller, false);
$this->_debug = Configure::read('debug');
+ $this->_paths = array();
+ $this->_paths['plugin'] = Configure::read('pluginPaths');
+ $this->_paths['view'] = Configure::read('viewPaths');
+ $this->_paths['vendor'] = Configure::read('vendorPaths');
+ $this->_paths['controller'] = Configure::read('controllerPaths');
+ }
+/**
+ * tear down function
+ *
+ * @return void
+ **/
+ function endTest() {
+ Configure::write('pluginPaths', $this->_paths['plugin']);
+ Configure::write('viewPaths', $this->_paths['view']);
+ Configure::write('vendorPaths', $this->_paths['vendor']);
+ Configure::write('controllerPaths', $this->_paths['controller']);
+
+ unset($this->View, $this->Controller);
+ DebugKitDebugger::clearTimers();
+ Configure::write('debug', $this->_debug);
}
/**
@@ -120,25 +140,25 @@ class DebugViewTestCase extends CakeTestCase {
$this->assertTrue(is_object($result['Javascript']));
$this->assertTrue(is_object($result['Number']));
}
-
/**
- * reset the view paths
+ * test that $out is returned when a layout is rendered instead of the empty
+ * $this->output. As this causes issues with requestAction()
*
* @return void
**/
- function endCase() {
- Configure::write('viewPaths', $this->_viewPaths);
- }
+ function testProperReturnUnderRequestAction() {
+ $plugins = Configure::read('pluginPaths');
+ $views = Configure::read('viewPaths');
+ $testapp = $plugins[1] . 'debug_kit' . DS . 'tests' . DS . 'test_app' . DS . 'views' . DS;
+ array_unshift($views, $testapp);
+ Configure::write('viewPaths', $views);
+
+ $this->View->set('test', 'I have been rendered.');
+ $this->View->viewPath = 'debug_kit_test';
+ $this->View->layout = false;
+ $result = $this->View->render('request_action_render');
-/**
- * tear down function
- *
- * @return void
- **/
- function tearDown() {
- unset($this->View, $this->Controller);
- DebugKitDebugger::clearTimers();
- Configure::write('debug', $this->_debug);
+ $this->assertEqual($result, 'I have been rendered.');
}
}
?>
\ No newline at end of file
diff --git a/tests/test_app/controllers/debug_kit_test_controller.php b/tests/test_app/controllers/debug_kit_test_controller.php
new file mode 100644
index 0000000..7ee1d13
--- /dev/null
+++ b/tests/test_app/controllers/debug_kit_test_controller.php
@@ -0,0 +1,38 @@
+<?php
+/* SVN FILE: $Id$ */
+/**
+ * DebugKitTestController
+ *
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * CakePHP : Rapid Development Framework <http://www.cakephp.org/>
+ * Copyright 2006-2008, Cake Software Foundation, Inc.
+ * 1785 E. Sahara Avenue, Suite 490-204
+ * Las Vegas, Nevada 89104
+ *
+ * Licensed under The MIT License
+ * Redistributions of files must retain the above copyright notice.
+ *
+ * @filesource
+ * @copyright Copyright 2006-2008, Cake Software Foundation, Inc.
+ * @link http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
+ * @package debug_kit
+ * @subpackage tests.test_app.controllers
+ * @license http://www.opensource.org/licenses/mit-license.php The MIT License
+ */
+class DebugKitTestController extends Controller {
+ var $uses = array();
+ var $components = array('DebugKit.Toolbar');
+
+ function request_action_return() {
+ $this->autoRender = false;
+ return 'I am some value from requestAction.';
+ }
+
+ function request_action_render() {
+ $this->set('test', 'I have been rendered.');
+ }
+
+}
\ No newline at end of file
diff --git a/tests/test_app/views/debug_kit_test/request_action_render.ctp b/tests/test_app/views/debug_kit_test/request_action_render.ctp
new file mode 100644
index 0000000..172d541
--- /dev/null
+++ b/tests/test_app/views/debug_kit_test/request_action_render.ctp
@@ -0,0 +1 @@
+<?php echo $test; ?>
\ No newline at end of file
diff --git a/vendors/css/debug_toolbar.css b/vendors/css/debug_toolbar.css
index 8e6258c..6106bcb 100644
--- a/vendors/css/debug_toolbar.css
+++ b/vendors/css/debug_toolbar.css
@@ -1,4 +1,4 @@
-/* @override http://localhost/cake_debug_kit/debug_kit/css/debug_toolbar.css */
+/* @override http://localhost/mark_story/site/debug_kit/css/debug_toolbar.css */
#debug-kit-toolbar {
position: fixed;
top: 0px;
@@ -8,7 +8,9 @@
overflow: visible;
z-index:10000;
font-family: helvetica, arial, sans-serif;
+ font-size: 12px;
}
+
/* panel tabs */
#debug-kit-toolbar #panel-tabs {
float: right;
@@ -22,7 +24,6 @@
padding: 0;
list-style: none;
}
-
#debug-kit-toolbar .panel-tab a {
float: left;
clear: none;
@@ -48,6 +49,12 @@
#debug-kit-toolbar .panel-tab a + .panel-content:hover {
display: block;
}
+#debug-kit-toolbar .panel-tab.icon a{
+ -webkit-border-top-left-radius: 8px 8px;
+ -webkit-border-bottom-left-radius: 8px 8px;
+ -moz-border-radius-topleft: 8px;
+ -moz-border-radius-bottomleft: 8px
+}
/* panel content */
#debug-kit-toolbar .panel-content {
@@ -69,7 +76,6 @@
.panel-content {
display: none;
}
-
.panel-content p {
margin: 1em 0;
}
@@ -92,10 +98,21 @@
#debug-kit-toolbar h4,
#debug-kit-toolbar h5,
#debug-kit-toolbar th {
+ color: #5d1717;
font-family: "Trebuchet MS", trebuchet, helvetica, arial, sans-serif;
margin-bottom:0.6em;
background:none;
}
+#debug-kit-toolbar h1 {
+ font-size: 18px;
+}
+#debug-kit-toolbar h2 {
+ font-size: 16px;
+}
+#debug-kit-toolbar h4 {
+ font-size: 14px;
+}
+
/* panel tables */
#debug-kit-toolbar table.debug-table {
@@ -110,7 +127,8 @@
}
#debug-kit-toolbar table.debug-table th {
border-bottom: 1px solid #222;
- background: 0;;
+ background: 0;
+ color: #252525;
}
#debug-kit-toolbar table.debug-table tr:nth-child(2n+1) td {
background:#f6f6f6;
@@ -136,7 +154,6 @@
display: block;
}
-
/** Neat Array styles **/
#debug-kit-toolbar .neat-array,
#debug-kit-toolbar .neat-array li {
diff --git a/vendors/js/js_debug_toolbar.js b/vendors/js/js_debug_toolbar.js
index dffef71..8b71e4f 100755
--- a/vendors/js/js_debug_toolbar.js
+++ b/vendors/js/js_debug_toolbar.js
@@ -92,6 +92,10 @@ DEBUGKIT.Util.Element = {
hide : function (element) {
element.style.display = 'none';
+ },
+
+ toggle : function (element) {
+ element.style.display == 'none' ? this.show(element) : this.hide(element);
}
};
diff --git a/views/debug.php b/views/debug.php
index d296bc6..b950292 100644
--- a/views/debug.php
+++ b/views/debug.php
@@ -84,6 +84,9 @@ class DebugView extends DoppelGangerView {
if (isset($this->loaded['toolbar'])) {
$this->loaded['toolbar']->postRender();
}
+ if (empty($this->output)) {
+ return $out;
+ }
//Temporary work around to hide the SQL dump at page bottom
Configure::write('debug', 0);
return $this->output;
diff --git a/views/elements/sql_log_panel.ctp b/views/elements/sql_log_panel.ctp
index 56c20fa..7e7e2f6 100644
--- a/views/elements/sql_log_panel.ctp
+++ b/views/elements/sql_log_panel.ctp
@@ -32,9 +32,50 @@
<?php foreach ($content as $dbName => $queryLog) : ?>
<div class="sql-log-panel-query-log">
<h4><?php echo $dbName ?></h4>
- <?php echo $queryLog; ?>
+ <?php
+ $headers = array('Nr', 'Query', 'Error', 'Affected', 'Num. rows', 'Took (ms)');
+ echo $toolbar->table($queryLog['queries'], $headers, array('title' => 'SQL Log ' . $dbName));
+
+ if (!empty($queryLog['explains'])):
+ $name = sprintf(__('toggle (%s) query explains for %s', true), count($queryLog['explains']), $dbName);
+ echo $html->link($name, '#', array('class' => 'show-slow'));
+
+ echo '<div class="slow-query-container">';
+ $headers = array_keys($queryLog['explains'][0]);
+ echo $toolbar->table($queryLog['explains'], $headers, array('title' => 'Slow Queries ' . $dbName));
+ echo '</div>';
+ else:
+ echo $toolbar->message('Warning', __('No slow queries!, or your database does not support EXPLAIN', true));
+ endif; ?>
</div>
<?php endforeach; ?>
-<?php else: ?>
- <p class="warning"><?php __('No active database connections'); ?></p>
-<?php endif; ?>
\ No newline at end of file
+<?php else:
+ echo $toolbar->message('Warning', __('No active database connections', true));
+endif; ?>
+
+<script type="text/javascript">
+DEBUGKIT.module('sqlLog');
+DEBUGKIT.sqlLog = function () {
+ var Element = DEBUGKIT.Util.Element,
+ Event = DEBUGKIT.Util.Event;
+
+ return {
+ init : function () {
+ var sqlPanel = document.getElementById('sql_log-tab');
+ var buttons = sqlPanel.getElementsByTagName('A');
+ for (var i in buttons) {
+ var button = buttons[i];
+ if (Element.hasClass(button, 'show-slow')) {
+ var nextDiv = button.nextSibling;
+ Event.addEvent(button, 'click', function (event) {
+ event.preventDefault();
+ Element.toggle(nextDiv);
+ });
+ Element.hide(nextDiv);
+ }
+ }
+ }
+ };
+}();
+DEBUGKIT.loader.register(DEBUGKIT.sqlLog);
+</script>
\ No newline at end of file
