Source for file DibiProfiler.php
Documentation is available at DibiProfiler.php
4: * dibi - tiny'n'smart database abstraction layer
5: * ----------------------------------------------
7: * @copyright Copyright (c) 2005, 2010 David Grudl
8: * @license http://dibiphp.com/license dibi license
9: * @link http://dibiphp.com
16: * dibi basic logger & profiler (experimental).
19: * - 'explain' - explain SELECT queries?
20: * - 'filter' - which queries to log?
22: * @copyright Copyright (c) 2005, 2010 David Grudl
27: /** maximum number of rows */
30: /** maximum SQL length */
33: /** @var string Name of the file where SQL errors should be logged */
36: /** @var bool log to firebug? */
39: /** @var bool explain queries? */
43: private $filter =
self::ALL;
46: public static $tickets =
array();
49: public static $fireTable =
array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
55: if (is_callable('Nette\Debug::addPanel')) {
57: } elseif (is_callable('NDebug::addPanel')) {
58: NDebug::addPanel($this);
60: Debug::addPanel($this);
63: $this->useFirebug =
isset($_SERVER['HTTP_USER_AGENT']) &&
strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
65: if (isset($config['filter'])) {
69: if (isset($config['explain'])) {
77: * @param string filename
78: * @return DibiProfiler provides a fluent interface
90: * @return DibiProfiler provides a fluent interface
94: $this->filter = (int)
$filter;
101: * Before event notification.
102: * @param DibiConnection
103: * @param int event name
107: public function before(DibiConnection $connection, $event, $sql =
NULL)
109: if ($event & self::QUERY) dibi::$numOfQueries++
;
110: dibi::$elapsedTime =
FALSE;
111: self::$tickets[] =
array($connection, $event, trim($sql), -
microtime(TRUE), NULL, NULL);
113: return key(self::$tickets);
119: * After event notification.
124: public function after($ticket, $res =
NULL)
126: if (!isset(self::$tickets[$ticket])) {
127: throw new InvalidArgumentException('Bad ticket number.');
130: $ticket =
& self::$tickets[$ticket];
131: $ticket[3] +=
microtime(TRUE);
132: list($connection, $event, $sql, $time) =
$ticket;
135: dibi::$totalTime +=
$time;
137: if (($event & $this->filter) ===
0) return;
139: if ($event & self::QUERY) {
142: } catch (Exception $e) {
146: if (count(self::$fireTable) <
self::$maxQueries) {
147: self::$fireTable[] =
array(
148: sprintf('%0.3f', $time *
1000),
149: strlen($sql) >
self::$maxLength ?
substr($sql, 0, self::$maxLength) .
'...' :
$sql,
151: $connection->getConfig('driver') .
'/' .
$connection->getConfig('name')
157: $ticket[5] =
dibi::dump($connection->setProfiler(NULL)->nativeQuery('EXPLAIN ' .
$sql), TRUE);
159: $connection->setProfiler($this);
164: header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
165: header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
166: header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
171: 'Label' =>
'dibi profiler (' .
dibi::$numOfQueries .
' SQL queries took ' .
sprintf('%0.3f', dibi::$totalTime *
1000) .
' ms)',
175: foreach (str_split($payload, 4990) as $num =>
$s) {
177: header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
187: .
"\n-- takes: " .
sprintf('%0.3f', $time *
1000) .
' ms'
188: .
"\n-- driver: " .
$connection->getConfig('driver') .
'/' .
$connection->getConfig('name')
199: * After exception notification.
200: * @param DibiDriverException
205: if ((self::EXCEPTION & $this->filter) ===
0) return;
212: $message =
$exception->getMessage();
213: $code =
$exception->getCode();
215: $message =
"[$code] $message";
220: .
"\n-- driver: " //. $connection->getConfig('driver')
229: private function writeFile($message)
232: if (!$handle) return; // or throw exception?
240: /********************* interface Nette\IDebugPanel ****************d*g**/
245: * Returns HTML code for custom tab.
250: return '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC">'
251: .
dibi::$numOfQueries .
' queries';
257: * Returns HTML code for custom panel.
260: public function getPanel()
265: <h1>Queries: " .
dibi::$numOfQueries .
(dibi::$totalTime ===
NULL ?
'' :
', time: ' .
sprintf('%0.3f', dibi::$totalTime *
1000) .
' ms') .
"</h1>
268: #nette-debug-DibiProfiler td.dibi-sql { background: white }
269: #nette-debug-DibiProfiler .nette-alt td.dibi-sql { background: #F5F5F5 }
270: #nette-debug-DibiProfiler .dibi-sql div { display: none; margin-top: 10px; max-height: 150px; overflow:auto }
273: <div class='nette-inner'>
276: <th>Time</th><th>SQL Statement</th><th>Rows</th><th>Connection</th>
279: $i =
0; $classes =
array('class="nette-alt"', '');
280: foreach (self::$tickets as $ticket) {
281: list($connection, $event, $sql, $time, $count, $explain) =
$ticket;
282: if (!($event & self::QUERY)) continue;
284: <tr {$classes[++$i%2]}>
285: <td>" .
sprintf('%0.3f', $time *
1000) .
($explain ?
"
286: <br><a href='#' class='nette-toggler' rel='#nette-debug-DibiProfiler-row-$i'>explain ►</a>" :
'') .
"</td>
287: <td class='dibi-sql'>" .
dibi::dump(strlen($sql) >
self::$maxLength ?
substr($sql, 0, self::$maxLength) .
'...' :
$sql, TRUE) .
($explain ?
"
288: <div id='nette-debug-DibiProfiler-row-$i'>{$explain}</div>" :
'') .
"</td>
290: <td>" .
htmlSpecialChars($connection->getConfig('driver') .
'/' .
$connection->getConfig('name')) .
"</td>
294: $content .=
'</table></div>';