mirror of
https://github.com/dg/dibi.git
synced 2025-08-15 02:25:10 +02:00
- added DibiProfiler (experimental)
- removed dibi::addHandler() & dibi::startLogger()
This commit is contained in:
@@ -41,6 +41,12 @@ class DibiConnection extends DibiObject
|
||||
*/
|
||||
private $driver;
|
||||
|
||||
/**
|
||||
* Profiler
|
||||
* @var IDibiProfiler
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* Is connected?
|
||||
* @var bool
|
||||
@@ -89,7 +95,7 @@ class DibiConnection extends DibiObject
|
||||
include_once dirname(__FILE__) . "/../drivers/$driver.php";
|
||||
|
||||
if (!class_exists($class, FALSE)) {
|
||||
throw new DibiException("Unable to create instance of dibi driver class '$class'.");
|
||||
throw new DibiException("Unable to create instance of dibi driver '$class'.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +103,17 @@ class DibiConnection extends DibiObject
|
||||
$this->config = $config;
|
||||
$this->driver = new $class;
|
||||
|
||||
if (!empty($config['profiler'])) {
|
||||
$class = $config['profiler'];
|
||||
if (is_numeric($class) || is_bool($class)) {
|
||||
$class = 'DibiProfiler';
|
||||
}
|
||||
if (!class_exists($class)) {
|
||||
throw new DibiException("Unable to create instance of dibi profiler '$class'.");
|
||||
}
|
||||
$this->setProfiler(new $class);
|
||||
}
|
||||
|
||||
if (empty($config['lazy'])) {
|
||||
$this->connect();
|
||||
}
|
||||
@@ -125,9 +142,14 @@ class DibiConnection extends DibiObject
|
||||
final protected function connect()
|
||||
{
|
||||
if (!$this->connected) {
|
||||
if ($this->profiler !== NULL) {
|
||||
$ticket = $this->profiler->before($this, IDibiProfiler::CONNECT);
|
||||
}
|
||||
$this->driver->connect($this->config);
|
||||
$this->connected = TRUE;
|
||||
dibi::notify($this, 'connected');
|
||||
if (isset($ticket)) {
|
||||
$this->profiler->after($ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +168,6 @@ class DibiConnection extends DibiObject
|
||||
}
|
||||
$this->driver->disconnect();
|
||||
$this->connected = FALSE;
|
||||
dibi::notify($this, 'disconnected');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,11 +292,22 @@ class DibiConnection extends DibiObject
|
||||
{
|
||||
$this->connect();
|
||||
|
||||
if ($this->profiler !== NULL) {
|
||||
$event = IDibiProfiler::QUERY;
|
||||
if (preg_match('#\s*(SELECT|UPDATE|INSERT|DELETE)#i', $sql, $matches)) {
|
||||
static $events = array(
|
||||
'SELECT' => IDibiProfiler::SELECT, 'UPDATE' => IDibiProfiler::UPDATE,
|
||||
'INSERT' => IDibiProfiler::INSERT, 'DELETE' => IDibiProfiler::DELETE,
|
||||
);
|
||||
$event = $events[strtoupper($matches[1])];
|
||||
}
|
||||
$ticket = $this->profiler->before($this, $event, $sql);
|
||||
}
|
||||
// TODO: move to profiler?
|
||||
dibi::$numOfQueries++;
|
||||
dibi::$sql = $sql;
|
||||
dibi::$elapsedTime = FALSE;
|
||||
$time = -microtime(TRUE);
|
||||
dibi::notify($this, 'beforeQuery', $sql);
|
||||
|
||||
if ($res = $this->driver->query($sql)) { // intentionally =
|
||||
$res = new DibiResult($res, $this->config);
|
||||
@@ -284,8 +316,10 @@ class DibiConnection extends DibiObject
|
||||
$time += microtime(TRUE);
|
||||
dibi::$elapsedTime = $time;
|
||||
dibi::$totalTime += $time;
|
||||
dibi::notify($this, 'afterQuery', $res);
|
||||
|
||||
if (isset($ticket)) {
|
||||
$this->profiler->after($ticket, $res);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
@@ -332,9 +366,14 @@ class DibiConnection extends DibiObject
|
||||
if ($this->inTxn) {
|
||||
throw new DibiException('There is already an active transaction.');
|
||||
}
|
||||
if ($this->profiler !== NULL) {
|
||||
$ticket = $this->profiler->before($this, IDibiProfiler::BEGIN);
|
||||
}
|
||||
$this->driver->begin();
|
||||
$this->inTxn = TRUE;
|
||||
dibi::notify($this, 'begin');
|
||||
if (isset($ticket)) {
|
||||
$this->profiler->after($ticket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -348,9 +387,14 @@ class DibiConnection extends DibiObject
|
||||
if (!$this->inTxn) {
|
||||
throw new DibiException('There is no active transaction.');
|
||||
}
|
||||
if ($this->profiler !== NULL) {
|
||||
$ticket = $this->profiler->before($this, IDibiProfiler::COMMIT);
|
||||
}
|
||||
$this->driver->commit();
|
||||
$this->inTxn = FALSE;
|
||||
dibi::notify($this, 'commit');
|
||||
if (isset($ticket)) {
|
||||
$this->profiler->after($ticket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,9 +408,14 @@ class DibiConnection extends DibiObject
|
||||
if (!$this->inTxn) {
|
||||
throw new DibiException('There is no active transaction.');
|
||||
}
|
||||
if ($this->profiler !== NULL) {
|
||||
$ticket = $this->profiler->before($this, IDibiProfiler::ROLLBACK);
|
||||
}
|
||||
$this->driver->rollback();
|
||||
$this->inTxn = FALSE;
|
||||
dibi::notify($this, 'rollback');
|
||||
if (isset($ticket)) {
|
||||
$this->profiler->after($ticket);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -490,6 +539,31 @@ class DibiConnection extends DibiObject
|
||||
|
||||
|
||||
|
||||
/********************* profiler ****************d*g**/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param IDibiProfiler
|
||||
* @return void
|
||||
*/
|
||||
public function setProfiler(IDibiProfiler $profiler = NULL)
|
||||
{
|
||||
$this->profiler = $profiler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return IDibiProfiler
|
||||
*/
|
||||
public function getProfiler()
|
||||
{
|
||||
return $this->profiler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************* misc ****************d*g**/
|
||||
|
||||
|
||||
|
@@ -61,7 +61,7 @@ class DibiDriverException extends DibiException implements /*Nette::*/IDebuggabl
|
||||
{
|
||||
parent::__construct($message, (int) $code);
|
||||
$this->sql = $sql;
|
||||
dibi::notify(NULL, 'exception', $this);
|
||||
// TODO: add $profiler->exception($this);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dibi - tiny'n'smart database abstraction layer
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
|
||||
*
|
||||
* This source file is subject to the "dibi license" that is bundled
|
||||
* with this package in the file license.txt.
|
||||
*
|
||||
* For more information please see http://dibiphp.com
|
||||
*
|
||||
* @copyright Copyright (c) 2005, 2008 David Grudl
|
||||
* @license http://dibiphp.com/license dibi license
|
||||
* @link http://dibiphp.com
|
||||
* @package dibi
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi basic logger & profiler (experimental).
|
||||
*
|
||||
* @author David Grudl
|
||||
* @copyright Copyright (c) 2005, 2008 David Grudl
|
||||
* @package dibi
|
||||
*/
|
||||
final class DibiLogger extends DibiObject
|
||||
{
|
||||
/** @var string Name of the file where SQL errors should be logged */
|
||||
private $file;
|
||||
|
||||
/** @var bool */
|
||||
public $logErrors = TRUE;
|
||||
|
||||
/** @var bool */
|
||||
public $logQueries = TRUE;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string filename
|
||||
*/
|
||||
public function __construct($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event handler (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback).
|
||||
*
|
||||
* @param DibiConnection
|
||||
* @param string event name
|
||||
* @param mixed
|
||||
* @return void
|
||||
*/
|
||||
public function handler($connection, $event, $arg)
|
||||
{
|
||||
if ($event === 'afterQuery' && $this->logQueries) {
|
||||
$this->write(
|
||||
"OK: " . dibi::$sql
|
||||
. ($arg instanceof DibiResult ? ";\n-- rows: " . count($arg) : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms'
|
||||
. "\n-- driver: " . $connection->getConfig('driver')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event === 'exception' && $this->logErrors) {
|
||||
// $arg is DibiDriverException
|
||||
$message = $arg->getMessage();
|
||||
$code = $arg->getCode();
|
||||
if ($code) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
|
||||
$this->write(
|
||||
"ERROR: $message"
|
||||
. "\n-- SQL: " . dibi::$sql
|
||||
. "\n-- driver: " //. $connection->getConfig('driver')
|
||||
. ";\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function write($message)
|
||||
{
|
||||
$handle = fopen($this->file, 'a');
|
||||
if (!$handle) return; // or throw exception?
|
||||
|
||||
flock($handle, LOCK_EX);
|
||||
fwrite($handle, $message);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
}
|
182
dibi/libs/DibiProfiler.php
Normal file
182
dibi/libs/DibiProfiler.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* dibi - tiny'n'smart database abstraction layer
|
||||
* ----------------------------------------------
|
||||
*
|
||||
* Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
|
||||
*
|
||||
* This source file is subject to the "dibi license" that is bundled
|
||||
* with this package in the file license.txt.
|
||||
*
|
||||
* For more information please see http://dibiphp.com
|
||||
*
|
||||
* @copyright Copyright (c) 2005, 2008 David Grudl
|
||||
* @license http://dibiphp.com/license dibi license
|
||||
* @link http://dibiphp.com
|
||||
* @package dibi
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi basic logger & profiler (experimental).
|
||||
*
|
||||
* @author David Grudl
|
||||
* @copyright Copyright (c) 2005, 2008 David Grudl
|
||||
* @package dibi
|
||||
*/
|
||||
class DibiProfiler extends DibiObject implements IDibiProfiler
|
||||
{
|
||||
/** @var string Name of the file where SQL errors should be logged */
|
||||
private $file;
|
||||
|
||||
/** @var bool log to firebug? */
|
||||
private $useFirebug;
|
||||
|
||||
/** @var int */
|
||||
private $filter = self::ALL;
|
||||
|
||||
/** @var array */
|
||||
public $tickets = array();
|
||||
|
||||
/** @var array */
|
||||
public static $table = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
|
||||
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->useFirebug = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string filename
|
||||
* @return void
|
||||
*/
|
||||
public function setFile($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param int
|
||||
* @return void
|
||||
*/
|
||||
public function setFilter($filter)
|
||||
{
|
||||
$this->filter = (int) $filter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Before event notification.
|
||||
* @param DibiConnection
|
||||
* @param int event name
|
||||
* @param string sql
|
||||
* @return int
|
||||
*/
|
||||
public function before(DibiConnection $connection, $event, $sql = NULL)
|
||||
{
|
||||
$this->tickets[] = array($connection, $event, $sql);
|
||||
end($this->tickets);
|
||||
return key($this->tickets);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @param int
|
||||
* @param DibiResult
|
||||
* @return void
|
||||
*/
|
||||
public function after($ticket, $res = NULL)
|
||||
{
|
||||
if (!isset($this->tickets[$ticket])) {
|
||||
throw new InvalidArgumentException('Bad ticket number.');
|
||||
}
|
||||
|
||||
list($connection, $event, $sql) = $this->tickets[$ticket];
|
||||
|
||||
if (($event & $this->filter) === 0) return;
|
||||
|
||||
if ($event & self::QUERY) {
|
||||
if ($this->useFirebug) {
|
||||
self::$table[] = array(
|
||||
sprintf('%0.3f', dibi::$elapsedTime * 1000),
|
||||
trim(preg_replace('#\s+#', ' ', $sql)),
|
||||
$res instanceof DibiResult ? count($res) : '-',
|
||||
$connection->getConfig('driver') . '/' . $connection->getConfig('name')
|
||||
);
|
||||
$caption = 'dibi profiler (' . dibi::$numOfQueries . ' SQL queries took ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms)';
|
||||
|
||||
$payload['FirePHP.Firebug.Console'][] = array('TABLE', array($caption, self::$table));
|
||||
$payload = json_encode($payload);
|
||||
foreach (str_split($payload, 4998) as $num => $s) {
|
||||
header('X-FirePHP-Data-' . str_pad(++$num, 12, '0', STR_PAD_LEFT) . ': ' . $s);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->file) {
|
||||
$this->writeFile(
|
||||
"OK: " . $sql
|
||||
. ($res instanceof DibiResult ? ";\n-- rows: " . count($res) : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms'
|
||||
. "\n-- driver: " . $connection->getConfig('driver') . '/' . $connection->getConfig('name')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* After exception notification.
|
||||
* @param DibiDriverException
|
||||
* @return void
|
||||
*/
|
||||
public function exception(DibiDriverException $exception)
|
||||
{
|
||||
if ((self::EXCEPTION & $this->filter) === 0) return;
|
||||
|
||||
if ($this->useFirebug) {
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
if ($this->file) {
|
||||
$message = $exception->getMessage();
|
||||
$code = $exception->getCode();
|
||||
if ($code) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
$this->writeFile(
|
||||
"ERROR: $message"
|
||||
. "\n-- SQL: " . dibi::$sql
|
||||
. "\n-- driver: " //. $connection->getConfig('driver')
|
||||
. ";\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function writeFile($message)
|
||||
{
|
||||
$handle = fopen($this->file, 'a');
|
||||
if (!$handle) return; // or throw exception?
|
||||
flock($handle, LOCK_EX);
|
||||
fwrite($handle, $message);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
}
|
@@ -29,7 +29,7 @@ interface IDibiVariable
|
||||
/**
|
||||
* Format for SQL.
|
||||
*
|
||||
* @param object DibiTranslator
|
||||
* @param DibiTranslator
|
||||
* @param string optional modifier
|
||||
* @return string SQL code
|
||||
*/
|
||||
@@ -54,6 +54,57 @@ interface IDataSource extends Countable, IteratorAggregate
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines method that must profiler implement.
|
||||
* @package dibi
|
||||
*/
|
||||
interface IDibiProfiler
|
||||
{
|
||||
/**#@+ event type */
|
||||
const CONNECT = 1;
|
||||
const SELECT = 4;
|
||||
const INSERT = 8;
|
||||
const DELETE = 16;
|
||||
const UPDATE = 32;
|
||||
const QUERY = 60;
|
||||
const BEGIN = 64;
|
||||
const COMMIT = 128;
|
||||
const ROLLBACK = 256;
|
||||
const TRANSACTION = 448;
|
||||
const EXCEPTION = 512;
|
||||
const ALL = 1023;
|
||||
/**#@-*/
|
||||
|
||||
/**
|
||||
* Before event notification.
|
||||
* @param DibiConnection
|
||||
* @param int event name
|
||||
* @param string sql
|
||||
* @return int
|
||||
*/
|
||||
function before(DibiConnection $connection, $event, $sql = NULL);
|
||||
|
||||
/**
|
||||
* After event notification.
|
||||
* @param int
|
||||
* @param DibiResult
|
||||
* @return void
|
||||
*/
|
||||
function after($ticket, $result = NULL);
|
||||
|
||||
/**
|
||||
* After exception notification.
|
||||
* @param DibiDriverException
|
||||
* @return void
|
||||
*/
|
||||
function exception(DibiDriverException $exception);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi driver interface.
|
||||
*
|
||||
|
Reference in New Issue
Block a user