mirror of
https://github.com/dg/dibi.git
synced 2025-08-14 10:05:44 +02:00
* removed $throwExceptions (always throws)
* added DibiLogger, dibi::notify(), dibi::startLogger() * miniprofiler dibi::$numOfQueries, $totalTime, $elapsedTime * simplified DibiException, added DibiDatabaseException * Dibi::nativeQuery splitted into DibiDriver::doQuery & nativeQuery() * moved dibi::dumpResult -> DibiResult::dump() * moved dibi::test() -> DibiDriver::test() * DibiTranslator generates $mask
This commit is contained in:
@@ -12,10 +12,6 @@
|
||||
*/
|
||||
|
||||
|
||||
// security - include dibi.php, not this file
|
||||
if (!class_exists('dibi', FALSE)) die();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi Common Driver
|
||||
@@ -27,7 +23,7 @@ abstract class DibiDriver
|
||||
* Current connection configuration
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* Connection resource
|
||||
@@ -57,7 +53,10 @@ abstract class DibiDriver
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
if (empty($config['lazy'])) $this->connection = $this->connect();
|
||||
|
||||
if (empty($config['lazy'])) {
|
||||
$this->connection = $this->connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +101,9 @@ abstract class DibiDriver
|
||||
*/
|
||||
final public function getConnection()
|
||||
{
|
||||
if (!$this->connection) $this->connection = $this->connect();
|
||||
if (!$this->connection) {
|
||||
$this->connection = $this->connect();
|
||||
}
|
||||
|
||||
return $this->connection;
|
||||
}
|
||||
@@ -110,7 +111,7 @@ abstract class DibiDriver
|
||||
|
||||
|
||||
/**
|
||||
* Generates and executes SQL query
|
||||
* Generates (translates) and executes SQL query
|
||||
*
|
||||
* @param array|mixed one or more arguments
|
||||
* @return int|DibiResult
|
||||
@@ -118,25 +119,32 @@ abstract class DibiDriver
|
||||
*/
|
||||
final public function query($args)
|
||||
{
|
||||
// receive arguments
|
||||
if (!is_array($args)) $args = func_get_args();
|
||||
|
||||
// and generate SQL
|
||||
$trans = new DibiTranslator($this);
|
||||
$sql = $trans->translate($args);
|
||||
if ($trans->translate($args)) {
|
||||
return $this->nativeQuery($trans->sql);
|
||||
} else {
|
||||
throw new DibiException('SQL translate error: ' . $trans->sql);
|
||||
}
|
||||
}
|
||||
|
||||
// event handler
|
||||
dibi::invokeEvent('beforeQuery', array($this, $sql));
|
||||
|
||||
if ($sql === FALSE) return FALSE;
|
||||
|
||||
// execute SQL
|
||||
$res = $this->nativeQuery($sql);
|
||||
/**
|
||||
* Generates and prints SQL query
|
||||
*
|
||||
* @param array|mixed one or more arguments
|
||||
* @return bool
|
||||
*/
|
||||
final public function test($args)
|
||||
{
|
||||
if (!is_array($args)) $args = func_get_args();
|
||||
|
||||
// event handler
|
||||
dibi::invokeEvent('afterQuery', array($this, $sql, $res));
|
||||
|
||||
return $res;
|
||||
$trans = new DibiTranslator($this);
|
||||
$ok = $trans->translate($args);
|
||||
dibi::dump($trans->sql);
|
||||
return $ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -145,9 +153,27 @@ abstract class DibiDriver
|
||||
* Executes the SQL query
|
||||
*
|
||||
* @param string SQL statement.
|
||||
* @return object|bool Result set object or TRUE on success, FALSE on failure
|
||||
* @return DibiResult|NULL Result set object
|
||||
* @throws DibiException
|
||||
*/
|
||||
abstract public function nativeQuery($sql);
|
||||
public function nativeQuery($sql)
|
||||
{
|
||||
dibi::notify('beforeQuery', $this, $sql);
|
||||
$res = $this->doQuery($sql);
|
||||
dibi::notify('afterQuery', $this, $res);
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Internal: Executes the SQL query
|
||||
*
|
||||
* @param string SQL statement.
|
||||
* @return DibiResult|NULL Result set object
|
||||
* @throws DibiDatabaseException
|
||||
*/
|
||||
abstract protected function doQuery($sql);
|
||||
|
||||
|
||||
|
||||
@@ -171,6 +197,7 @@ abstract class DibiDriver
|
||||
|
||||
/**
|
||||
* Begins a transaction (if supported).
|
||||
* @return void
|
||||
*/
|
||||
abstract public function begin();
|
||||
|
||||
@@ -178,6 +205,7 @@ abstract class DibiDriver
|
||||
|
||||
/**
|
||||
* Commits statements in a transaction.
|
||||
* @return void
|
||||
*/
|
||||
abstract public function commit();
|
||||
|
||||
@@ -185,6 +213,7 @@ abstract class DibiDriver
|
||||
|
||||
/**
|
||||
* Rollback changes in a transaction.
|
||||
* @return void
|
||||
*/
|
||||
abstract public function rollback();
|
||||
|
||||
|
@@ -12,28 +12,29 @@
|
||||
*/
|
||||
|
||||
|
||||
// security - include dibi.php, not this file
|
||||
if (!class_exists('dibi', FALSE)) die();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi exception class
|
||||
*
|
||||
* dibi common exception
|
||||
*/
|
||||
class DibiException extends Exception
|
||||
{
|
||||
private
|
||||
$sql,
|
||||
$dbError;
|
||||
}
|
||||
|
||||
|
||||
public function __construct($message, $dbError = NULL, $sql = NULL)
|
||||
/**
|
||||
* database server exception
|
||||
*/
|
||||
class DibiDatabaseException extends DibiException
|
||||
{
|
||||
/** @var string */
|
||||
private $sql;
|
||||
|
||||
|
||||
public function __construct($message = NULL, $code = 0, $sql = NULL)
|
||||
{
|
||||
$this->dbError = $dbError;
|
||||
$this->sql = $sql;
|
||||
|
||||
parent::__construct($message);
|
||||
$this->sql = $sql;
|
||||
dibi::notify('exception', NULL, $this);
|
||||
}
|
||||
|
||||
|
||||
@@ -45,31 +46,13 @@ class DibiException extends Exception
|
||||
|
||||
|
||||
|
||||
final public function getDbError()
|
||||
{
|
||||
return $this->dbError;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$s = parent::__toString();
|
||||
|
||||
if ($this->dbError) {
|
||||
$s .= "\n\nDatabase error: ";
|
||||
if (isset($this->dbError['code'])) {
|
||||
$s .= "[" . $this->dbError['code'] . "] ";
|
||||
}
|
||||
|
||||
$s .= $this->dbError['message'];
|
||||
}
|
||||
|
||||
if ($this->sql) {
|
||||
$s .= "\nSQL: " . $this->sql;
|
||||
}
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
} // class DibiException
|
||||
}
|
||||
|
105
dibi/libs/logger.php
Normal file
105
dibi/libs/logger.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the "dibi" project (http://dibi.texy.info/)
|
||||
*
|
||||
* @author David Grudl
|
||||
* @copyright Copyright (c) 2005-2007 David Grudl aka -dgx- (http://www.dgx.cz)
|
||||
* @license New BSD License
|
||||
* @version $Revision$ $Date$
|
||||
* @category Database
|
||||
* @package Dibi
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi basic logger & profiler
|
||||
*/
|
||||
final class DibiLogger
|
||||
{
|
||||
/** @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 string event name
|
||||
* @param mixed
|
||||
* @param mixed
|
||||
* @return void
|
||||
*/
|
||||
public function handler($event, $driver, $arg)
|
||||
{
|
||||
if ($event === 'afterQuery' && $this->logQueries) {
|
||||
$this->write(
|
||||
"OK: " . dibi::$sql
|
||||
. ($arg instanceof DibiResult ? ";\n-- rows: " . $arg->rowCount() : '')
|
||||
. "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms'
|
||||
. "\n-- driver: " . $driver->getConfig('driver')
|
||||
. "\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event === 'exception' && $this->logErrors) {
|
||||
// $arg is DibiDatabaseException
|
||||
$message = $arg->getMessage();
|
||||
$code = $arg->getCode();
|
||||
if ($code) {
|
||||
$message = "[$code] $message";
|
||||
}
|
||||
|
||||
$this->write(
|
||||
"ERROR: $message"
|
||||
. "\n-- SQL: " . dibi::$sql
|
||||
. "\n-- driver: " //. $driver->getConfig('driver')
|
||||
. ";\n-- " . date('Y-m-d H:i:s')
|
||||
. "\n\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function write($message)
|
||||
{
|
||||
$handle = fopen($this->file, 'a');
|
||||
if (!$handle) return; // or throw exception?
|
||||
|
||||
flock($handle, LOCK_EX);
|
||||
fwrite($handle, $message);
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**#@+
|
||||
* Access to undeclared property
|
||||
* @throws Exception
|
||||
*/
|
||||
private function &__get($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); }
|
||||
private function __set($name, $value) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); }
|
||||
private function __unset($name) { throw new Exception("Access to undeclared property: " . get_class($this) . "::$$name"); }
|
||||
/**#@-*/
|
||||
|
||||
}
|
@@ -12,13 +12,9 @@
|
||||
*/
|
||||
|
||||
|
||||
// security - include dibi.php, not this file
|
||||
if (!class_exists('dibi', FALSE)) die();
|
||||
|
||||
|
||||
|
||||
// PHP < 5.1 compatibility
|
||||
if (!interface_exists('Countable', false)) {
|
||||
if (!interface_exists('Countable', FALSE)) {
|
||||
interface Countable
|
||||
{
|
||||
function count();
|
||||
@@ -54,6 +50,13 @@ abstract class DibiResult implements IteratorAggregate, Countable
|
||||
protected $meta;
|
||||
|
||||
|
||||
/**
|
||||
* Resultset resource
|
||||
* @var resource
|
||||
*/
|
||||
protected $resource;
|
||||
|
||||
|
||||
private static $types = array(
|
||||
dibi::FIELD_TEXT => 'string',
|
||||
dibi::FIELD_BINARY => 'string',
|
||||
@@ -65,6 +68,25 @@ abstract class DibiResult implements IteratorAggregate, Countable
|
||||
|
||||
|
||||
|
||||
|
||||
public function __construct($resource)
|
||||
{
|
||||
$this->resource = $resource;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the resultset resource
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
final public function getResource()
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Moves cursor position without fetching row
|
||||
*
|
||||
@@ -402,6 +424,39 @@ abstract class DibiResult implements IteratorAggregate, Countable
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Displays complete result-set as HTML table
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function dump()
|
||||
{
|
||||
echo '<table class="dump">';
|
||||
echo '<thead>';
|
||||
echo '<tr>';
|
||||
echo '<th>#row</th>';
|
||||
foreach ($this->getFields() as $field) {
|
||||
echo '<th>' . htmlSpecialChars($field) . '</th>';
|
||||
}
|
||||
echo '</tr>';
|
||||
echo '</thead>';
|
||||
echo '<tbody>';
|
||||
|
||||
foreach ($this as $row => $fields) {
|
||||
echo '<tr><th>', $row, '</th>';
|
||||
foreach ($fields as $field) {
|
||||
//if (is_object($field)) $field = $field->__toString();
|
||||
echo '<td>', htmlSpecialChars($field), '</td>';
|
||||
}
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
echo '</tbody>';
|
||||
echo '</table>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** these are the required IteratorAggregate functions */
|
||||
final public function getIterator($offset = NULL, $count = NULL)
|
||||
{
|
||||
|
@@ -12,10 +12,6 @@
|
||||
*/
|
||||
|
||||
|
||||
// security - include dibi.php, not this file
|
||||
if (!class_exists('dibi', FALSE)) die();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* dibi translator
|
||||
@@ -23,13 +19,29 @@ if (!class_exists('dibi', FALSE)) die();
|
||||
*/
|
||||
final class DibiTranslator
|
||||
{
|
||||
private
|
||||
$driver,
|
||||
$modifier,
|
||||
$hasError,
|
||||
$comment,
|
||||
$ifLevel,
|
||||
$ifLevelStart;
|
||||
/** @var string */
|
||||
public $sql;
|
||||
|
||||
/** @var string NOT USED YET */
|
||||
public $mask;
|
||||
|
||||
/** @var DibiDriver */
|
||||
private $driver;
|
||||
|
||||
/** @var string last modifier */
|
||||
private $modifier;
|
||||
|
||||
/** @var bool */
|
||||
private $hasError;
|
||||
|
||||
/** @var bool */
|
||||
private $comment;
|
||||
|
||||
/** @var int */
|
||||
private $ifLevel;
|
||||
|
||||
/** @var int */
|
||||
private $ifLevelStart;
|
||||
|
||||
|
||||
|
||||
@@ -44,8 +56,7 @@ final class DibiTranslator
|
||||
* Generates SQL
|
||||
*
|
||||
* @param array
|
||||
* @return string|FALSE
|
||||
* @throws DibiException
|
||||
* @return bool
|
||||
*/
|
||||
public function translate($args)
|
||||
{
|
||||
@@ -61,7 +72,7 @@ final class DibiTranslator
|
||||
$comment = FALSE;
|
||||
|
||||
// iterate
|
||||
$sql = array();
|
||||
$sql = $mask = array();
|
||||
$i = 0;
|
||||
foreach ($args as $arg)
|
||||
{
|
||||
@@ -84,7 +95,7 @@ final class DibiTranslator
|
||||
if (is_string($arg) && (!$mod || $mod === 'sql')) {
|
||||
$mod = FALSE;
|
||||
// will generate new mod
|
||||
$sql[] = $this->formatValue($arg, 'sql');
|
||||
$mask[] = $sql[] = $this->formatValue($arg, 'sql');
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -96,12 +107,13 @@ final class DibiTranslator
|
||||
$mod = $commandIns ? 'v' : 'a';
|
||||
} else {
|
||||
$mod = $commandIns ? 'l' : 'a';
|
||||
if ($lastArr === $i - 1) $sql[] = ',';
|
||||
if ($lastArr === $i - 1) $mask[] = $sql[] = ',';
|
||||
}
|
||||
$lastArr = $i;
|
||||
}
|
||||
|
||||
// default processing
|
||||
$mask[] = '?';
|
||||
if (!$comment) {
|
||||
$sql[] = $this->formatValue($arg, $mod);
|
||||
}
|
||||
@@ -110,33 +122,15 @@ final class DibiTranslator
|
||||
|
||||
if ($comment) $sql[] = "\0";
|
||||
|
||||
$sql = implode(' ', $sql);
|
||||
//$this->mask = implode(' ', $mask);
|
||||
|
||||
$this->sql = implode(' ', $sql);
|
||||
|
||||
// remove comments
|
||||
// TODO: check !!!
|
||||
$sql = preg_replace('#\x00.*?\x00#s', '', $sql);
|
||||
$this->sql = preg_replace('#\x00.*?\x00#s', '', $this->sql);
|
||||
|
||||
if ($this->hasError) {
|
||||
// TODO: do it better, remove dibi::$...
|
||||
if (dibi::$logFile) { // log to file
|
||||
dibi::log(
|
||||
"ERROR: SQL generate error"
|
||||
. "\n-- SQL: " . $sql
|
||||
. ";\n-- " . date('Y-m-d H:i:s ')
|
||||
);
|
||||
}
|
||||
|
||||
if (dibi::$throwExceptions) {
|
||||
throw new DibiException('SQL generate error', NULL, $sql);
|
||||
|
||||
} else {
|
||||
trigger_error("dibi: SQL generate error: $sql", E_USER_WARNING);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
return $sql;
|
||||
return !$this->hasError;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +197,7 @@ final class DibiTranslator
|
||||
|
||||
if (!is_scalar($value)) { // array is already processed
|
||||
$this->hasError = TRUE;
|
||||
return '**Unexpected ' . gettype($value) . '**';
|
||||
return '**Unexpected type ' . gettype($value) . '**';
|
||||
}
|
||||
|
||||
switch ($modifier) {
|
||||
@@ -280,7 +274,7 @@ final class DibiTranslator
|
||||
case 'a':
|
||||
case 'v':
|
||||
$this->hasError = TRUE;
|
||||
return "**Unexpected ".gettype($value)."**";
|
||||
return '**Unexpected type ' . gettype($value) . '**';
|
||||
|
||||
case 'if':
|
||||
$this->hasError = TRUE;
|
||||
|
Reference in New Issue
Block a user