mirror of
https://github.com/dg/dibi.git
synced 2025-08-03 04:37:35 +02:00
- added DibiProfiler (experimental)
- removed dibi::addHandler() & dibi::startLogger()
This commit is contained in:
@@ -72,6 +72,7 @@ require_once dirname(__FILE__) . '/libs/DibiTable.php';
|
||||
require_once dirname(__FILE__) . '/libs/DibiDataSource.php';
|
||||
require_once dirname(__FILE__) . '/libs/DibiFluent.php';
|
||||
require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php';
|
||||
require_once dirname(__FILE__) . '/libs/DibiProfiler.php';
|
||||
|
||||
|
||||
|
||||
@@ -239,7 +240,7 @@ class dibi
|
||||
* Retrieve active connection.
|
||||
*
|
||||
* @param string connection registy name
|
||||
* @return object DibiConnection object.
|
||||
* @return DibiConnection
|
||||
* @throws DibiException
|
||||
*/
|
||||
public static function getConnection($name = NULL)
|
||||
@@ -275,6 +276,18 @@ class dibi
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve active connection profiler.
|
||||
* @return IDibiProfiler
|
||||
* @throws DibiException
|
||||
*/
|
||||
public static function getProfiler()
|
||||
{
|
||||
return self::getConnection()->getProfiler();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************* monostate for active connection ****************d*g**/
|
||||
|
||||
|
||||
@@ -676,64 +689,6 @@ class dibi
|
||||
|
||||
|
||||
|
||||
/********************* event handling ****************d*g**/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add new event handler.
|
||||
*
|
||||
* @param callback
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public static function addHandler($callback)
|
||||
{
|
||||
if (!is_callable($callback)) {
|
||||
throw new InvalidArgumentException("Invalid callback.");
|
||||
}
|
||||
|
||||
self::$handlers[] = $callback;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Event notification (events: exception, connected, beforeQuery, afterQuery, begin, commit, rollback).
|
||||
*
|
||||
* @param DibiConnection
|
||||
* @param string event name
|
||||
* @param mixed
|
||||
* @return void
|
||||
*/
|
||||
public static function notify(DibiConnection $connection = NULL, $event, $arg = NULL)
|
||||
{
|
||||
foreach (self::$handlers as $handler) {
|
||||
call_user_func($handler, $connection, $event, $arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Enable profiler & logger.
|
||||
*
|
||||
* @param string filename
|
||||
* @param bool log all queries?
|
||||
* @return DibiProfiler
|
||||
*/
|
||||
public static function startLogger($file, $logQueries = FALSE)
|
||||
{
|
||||
require_once dirname(__FILE__) . '/libs/DibiLogger.php';
|
||||
|
||||
$logger = new DibiLogger($file);
|
||||
$logger->logQueries = $logQueries;
|
||||
self::addHandler(array($logger, 'handler'));
|
||||
return $logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/********************* misc tools ****************d*g**/
|
||||
|
||||
|
||||
|
@@ -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.
|
||||
*
|
||||
|
@@ -3,17 +3,18 @@
|
||||
|
||||
require_once '../dibi/dibi.php';
|
||||
|
||||
// enable log to this file, TRUE means "log all queries"
|
||||
dibi::startLogger('log.sql', TRUE);
|
||||
|
||||
|
||||
|
||||
dibi::connect(array(
|
||||
'driver' => 'sqlite',
|
||||
'database' => 'sample.sdb',
|
||||
'profiler' => TRUE,
|
||||
));
|
||||
|
||||
|
||||
// enable log to this file
|
||||
dibi::getProfiler()->setFile('log.sql');
|
||||
|
||||
|
||||
|
||||
try {
|
||||
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = %i', 1);
|
||||
|
@@ -7,6 +7,7 @@ require_once '../dibi/dibi.php';
|
||||
dibi::connect(array(
|
||||
'driver' => 'sqlite',
|
||||
'database' => 'sample.sdb',
|
||||
'profiler' => TRUE,
|
||||
));
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user