1
0
mirror of https://github.com/dg/dibi.git synced 2025-09-03 19:12:33 +02:00

Compare commits

..

1 Commits
v2.0 ... v1.0

Author SHA1 Message Date
David Grudl
da09733a3d released version 1.0 2008-10-30 13:09:47 +00:00
98 changed files with 5027 additions and 9455 deletions

4
.gitattributes vendored
View File

@@ -1,4 +0,0 @@
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
tests/ export-ignore

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
/vendor
/composer.lock

View File

@@ -1,16 +0,0 @@
{
"name": "dibi/dibi",
"description": "Dibi is Database Abstraction Library for PHP 5.",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "oracle", "access", "pdo", "odbc"],
"homepage": "http://dibiphp.com/",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
"name": "David Grudl",
"homepage": "http://davidgrudl.com"
}
],
"autoload": {
"classmap": ["dibi/"]
}
}

View File

@@ -0,0 +1,40 @@
<?php
/**
* Nette Framework
*
* Copyright (c) 2004, 2008 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "Nette license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://nettephp.com
*
* @copyright Copyright (c) 2004, 2008 David Grudl
* @license http://nettephp.com/license Nette license
* @link http://nettephp.com
* @category Nette
* @package Nette
*/
/*namespace Nette;*/
/**
* Custom output for Nette::Debug.
*
* @author David Grudl
* @copyright Copyright (c) 2004, 2008 David Grudl
* @package Nette
*/
interface IDebuggable
{
/**
* Returns custom panels.
* @return array
*/
function getPanels();
}

View File

@@ -1,55 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* Dibi extension for Nette Framework. Creates 'connection' service.
*
* @author David Grudl
* @package dibi\nette
* @phpversion 5.3
*/
class DibiNetteExtension extends Nette\Config\CompilerExtension
{
public function loadConfiguration()
{
$container = $this->getContainerBuilder();
$config = $this->getConfig();
$useProfiler = isset($config['profiler'])
? $config['profiler']
: !$container->parameters['productionMode'];
unset($config['profiler']);
if (isset($config['flags'])) {
$flags = 0;
foreach ((array) $config['flags'] as $flag) {
$flags |= constant($flag);
}
$config['flags'] = $flags;
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('DibiConnection', array($config));
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::$bar->addPanel(?)', array('@self'))
->addSetup('Nette\Diagnostics\Debugger::$blueScreen->addPanel(?)', array(array('@self', 'renderException')));
$connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
}
}
}

View File

@@ -1,159 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
if (interface_exists('Nette\Diagnostics\IBarPanel')) {
class_alias('Nette\Diagnostics\IBarPanel', 'IBarPanel');
}
/**
* Dibi panel for Nette\Diagnostics.
*
* @author David Grudl
* @package dibi\nette
*/
class DibiNettePanel extends DibiObject implements IBarPanel
{
/** @var int maximum SQL length */
static public $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = array();
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->explain = $explain;
}
public function register(DibiConnection $connection)
{
if (is_callable('Nette\Diagnostics\Debugger::enable') && !class_exists('NDebugger')) {
class_alias('Nette\Diagnostics\Debugger', 'NDebugger'); // PHP 5.2 code compatibility
}
if (is_callable('NDebugger::enable')) {
NDebugger::$bar && NDebugger::$bar->addPanel($this);
NDebugger::$blueScreen && NDebugger::$blueScreen->addPanel(array($this, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('Debugger::enable')) {
Debugger::$bar && Debugger::$bar->addPanel($this);
Debugger::$blueScreen && Debugger::$blueScreen->addPanel(array($this, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
}
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public function renderException($e)
{
if ($e instanceof DibiException && $e->getSql()) {
return array(
'tab' => 'SQL',
'panel' => dibi::dump($e->getSql(), TRUE),
);
}
}
/**
* Returns HTML code for custom tab. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getTab()
{
$totalTime = 0;
foreach ($this->events as $event) {
$totalTime += $event->time;
}
return '<span title="dibi"><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" />'
. count($this->events) . ' queries'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
. '</span>';
}
/**
* Returns HTML code for custom panel. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getPanel()
{
$totalTime = $s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === DibiEvent::SELECT) {
try {
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $e) {}
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
if ($explain) {
static $counter;
$counter++;
$s .= "<br /><a href='#' class='nette-toggler' rel='#nette-debug-DibiProfiler-row-$counter'>explain&nbsp;&#x25ba;</a>";
}
$s .= '</td><td class="nette-DibiProfiler-sql">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
if ($event->source) {
$helpers = 'Nette\Diagnostics\Helpers';
if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
}
$s .= call_user_func(array($helpers, 'editorLink'), $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
}
return empty($this->events) ? '' :
'<style> #nette-debug td.nette-DibiProfiler-sql { background: white !important }
#nette-debug .nette-DibiProfiler-source { color: #999 !important }
#nette-debug nette-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . count($this->events) . ($totalTime === NULL ? '' : sprintf(', time: %0.3f ms', $totalTime * 1000)) . '</h1>
<div class="nette-inner nette-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
</table>
</div>';
}
}

View File

@@ -1,40 +1,81 @@
<?php <?php
/** /**
* dibi - smart database abstraction layer (http://dibiphp.com) * dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* *
* Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com) * 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$
*/ */
/** /**
* Check PHP configuration. * Check PHP configuration.
*/ */
if (version_compare(PHP_VERSION, '5.2.0', '<')) { if (version_compare(PHP_VERSION, '5.1.0', '<')) {
throw new Exception('dibi needs PHP 5.2.0 or newer.'); throw new Exception('dibi needs PHP 5.1.0 or newer.');
} }
/**
* Compatibility with Nette
*/
if (!class_exists('NotImplementedException', FALSE)) {
class NotImplementedException extends LogicException {}
}
if (!class_exists('NotSupportedException', FALSE)) {
class NotSupportedException extends LogicException {}
}
if (!class_exists('MemberAccessException', FALSE)) {
class MemberAccessException extends LogicException {}
}
if (!class_exists('InvalidStateException', FALSE)) {
class InvalidStateException extends RuntimeException {}
}
if (!class_exists('IOException', FALSE)) {
class IOException extends RuntimeException {}
}
if (!class_exists('FileNotFoundException', FALSE)) {
class FileNotFoundException extends IOException {}
}
if (!interface_exists(/*Nette::*/'IDebuggable', FALSE)) {
require_once dirname(__FILE__) . '/Nette/IDebuggable.php';
}
// dibi libraries
require_once dirname(__FILE__) . '/libs/interfaces.php'; require_once dirname(__FILE__) . '/libs/interfaces.php';
require_once dirname(__FILE__) . '/libs/DibiDateTime.php';
require_once dirname(__FILE__) . '/libs/DibiObject.php'; require_once dirname(__FILE__) . '/libs/DibiObject.php';
require_once dirname(__FILE__) . '/libs/DibiLiteral.php';
require_once dirname(__FILE__) . '/libs/DibiHashMap.php';
require_once dirname(__FILE__) . '/libs/DibiException.php'; require_once dirname(__FILE__) . '/libs/DibiException.php';
require_once dirname(__FILE__) . '/libs/DibiConnection.php'; require_once dirname(__FILE__) . '/libs/DibiConnection.php';
require_once dirname(__FILE__) . '/libs/DibiResult.php'; require_once dirname(__FILE__) . '/libs/DibiResult.php';
require_once dirname(__FILE__) . '/libs/DibiResultIterator.php';
require_once dirname(__FILE__) . '/libs/DibiRow.php';
require_once dirname(__FILE__) . '/libs/DibiTranslator.php'; require_once dirname(__FILE__) . '/libs/DibiTranslator.php';
require_once dirname(__FILE__) . '/libs/DibiVariable.php';
require_once dirname(__FILE__) . '/libs/DibiTableX.php';
require_once dirname(__FILE__) . '/libs/DibiDataSource.php'; require_once dirname(__FILE__) . '/libs/DibiDataSource.php';
require_once dirname(__FILE__) . '/libs/DibiFluent.php'; require_once dirname(__FILE__) . '/libs/DibiFluent.php';
require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php'; require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php';
require_once dirname(__FILE__) . '/libs/DibiEvent.php'; require_once dirname(__FILE__) . '/libs/DibiProfiler.php';
require_once dirname(__FILE__) . '/libs/DibiFileLogger.php';
require_once dirname(__FILE__) . '/libs/DibiFirePhpLogger.php';
if (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPanel')) {
require_once dirname(__FILE__) . '/bridges/Nette-2.0/DibiNettePanel.php';
}
/** /**
@@ -44,39 +85,40 @@ if (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPan
* store connections info. * store connections info.
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*/ */
class dibi class dibi
{ {
/** column type */ /**#@+
const TEXT = 's', // as 'string' * dibi column type
BINARY = 'bin', */
BOOL = 'b', const FIELD_TEXT = 's'; // as 'string'
INTEGER = 'i', const FIELD_BINARY = 'bin';
FLOAT = 'f', const FIELD_BOOL = 'b';
DATE = 'd', const FIELD_INTEGER = 'i';
DATETIME = 't', const FIELD_FLOAT = 'f';
TIME = 't'; const FIELD_DATE = 'd';
const FIELD_DATETIME = 't';
const FIELD_TIME = 't';
/**#@-*/
const IDENTIFIER = 'n'; /**
* Identifier type
*/
const IDENTIFIER = 'n';
/** @deprecated */ /**#@+
const FIELD_TEXT = dibi::TEXT, * dibi version
FIELD_BINARY = dibi::BINARY, */
FIELD_BOOL = dibi::BOOL, const VERSION = '1.0';
FIELD_INTEGER = dibi::INTEGER, const REVISION = '$WCREV$ released on $WCDATE$';
FIELD_FLOAT = dibi::FLOAT, /**#@-*/
FIELD_DATE = dibi::DATE,
FIELD_DATETIME = dibi::DATETIME,
FIELD_TIME = dibi::TIME;
/** version */ /**
const VERSION = '2.0.5', * Configuration options
REVISION = 'released on 2015-01-13'; */
const RESULT_WITH_TABLES = 'resultWithTables'; // for MySQL
/** sorting order */
const ASC = 'ASC',
DESC = 'DESC';
/** @var DibiConnection[] Connection registry storage for DibiConnection objects */ /** @var DibiConnection[] Connection registry storage for DibiConnection objects */
private static $registry = array(); private static $registry = array();
@@ -84,6 +126,12 @@ class dibi
/** @var DibiConnection Current connection */ /** @var DibiConnection Current connection */
private static $connection; private static $connection;
/** @var array Substitutions for identifiers */
private static $substs = array();
/** @var callback Substitution fallback */
private static $substFallBack;
/** @var array @see addHandler */ /** @var array @see addHandler */
private static $handlers = array(); private static $handlers = array();
@@ -103,6 +151,7 @@ class dibi
public static $defaultDriver = 'mysql'; public static $defaultDriver = 'mysql';
/** /**
* Static class - cannot be instantiated. * Static class - cannot be instantiated.
*/ */
@@ -112,13 +161,15 @@ class dibi
} }
/********************* connections handling ****************d*g**/ /********************* connections handling ****************d*g**/
/** /**
* Creates a new DibiConnection object and connects it to specified database. * Creates a new DibiConnection object and connects it to specified database.
* @param mixed connection parameters * @param array|string|ArrayObject connection parameters
* @param string connection name * @param string connection name
* @return DibiConnection * @return DibiConnection
* @throws DibiException * @throws DibiException
*/ */
@@ -128,6 +179,7 @@ class dibi
} }
/** /**
* Disconnects from database (doesn't destroy DibiConnection object). * Disconnects from database (doesn't destroy DibiConnection object).
* @return void * @return void
@@ -138,6 +190,7 @@ class dibi
} }
/** /**
* Returns TRUE when connection was established. * Returns TRUE when connection was established.
* @return bool * @return bool
@@ -148,6 +201,7 @@ class dibi
} }
/** /**
* Retrieve active connection. * Retrieve active connection.
* @param string connection registy name * @param string connection registy name
@@ -172,16 +226,6 @@ class dibi
} }
/**
* Sets connection.
* @param DibiConnection
* @return DibiConnection
*/
public static function setConnection(DibiConnection $connection)
{
return self::$connection = $connection;
}
/** /**
* Change active connection. * Change active connection.
@@ -195,13 +239,27 @@ class dibi
} }
/**
* Retrieve active connection profiler.
* @return IDibiProfiler
* @throws DibiException
*/
public static function getProfiler()
{
return self::getConnection()->getProfiler();
}
/********************* monostate for active connection ****************d*g**/ /********************* monostate for active connection ****************d*g**/
/** /**
* Generates and executes SQL query - Monostate for DibiConnection::query(). * Generates and executes SQL query - Monostate for DibiConnection::query().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any) * @return DibiResult|NULL result set object (if any)
* @throws DibiException * @throws DibiException
*/ */
public static function query($args) public static function query($args)
@@ -211,10 +269,11 @@ class dibi
} }
/** /**
* Executes the SQL query - Monostate for DibiConnection::nativeQuery(). * Executes the SQL query - Monostate for DibiConnection::nativeQuery().
* @param string SQL statement. * @param string SQL statement.
* @return DibiResult|int result set object (if any) * @return DibiResult|NULL result set object (if any)
*/ */
public static function nativeQuery($sql) public static function nativeQuery($sql)
{ {
@@ -222,6 +281,7 @@ class dibi
} }
/** /**
* Generates and prints SQL query - Monostate for DibiConnection::test(). * Generates and prints SQL query - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
@@ -234,17 +294,6 @@ class dibi
} }
/**
* Generates and returns SQL query as DibiDataSource - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments
* @return DibiDataSource
*/
public static function dataSource($args)
{
$args = func_get_args();
return self::getConnection()->dataSource($args);
}
/** /**
* Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch(). * Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch().
@@ -259,10 +308,11 @@ class dibi
} }
/** /**
* Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll(). * Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return DibiRow[] * @return array of DibiRow
* @throws DibiException * @throws DibiException
*/ */
public static function fetchAll($args) public static function fetchAll($args)
@@ -272,6 +322,7 @@ class dibi
} }
/** /**
* Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle(). * Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
@@ -285,6 +336,7 @@ class dibi
} }
/** /**
* Executes SQL query and fetch pairs - Monostate for DibiConnection::query() & fetchPairs(). * Executes SQL query and fetch pairs - Monostate for DibiConnection::query() & fetchPairs().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
@@ -298,90 +350,70 @@ class dibi
} }
/** /**
* Gets the number of affected rows. * Gets the number of affected rows.
* Monostate for DibiConnection::getAffectedRows() * Monostate for DibiConnection::affectedRows()
* @return int number of rows
* @throws DibiException
*/
public static function getAffectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows * @return int number of rows
* @throws DibiException * @throws DibiException
*/ */
public static function affectedRows() public static function affectedRows()
{ {
return self::getConnection()->getAffectedRows(); return self::getConnection()->affectedRows();
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* Monostate for DibiConnection::getInsertId() * Monostate for DibiConnection::insertId()
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public static function getInsertId($sequence=NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name * @param string optional sequence name
* @return int * @return int
* @throws DibiException * @throws DibiException
*/ */
public static function insertId($sequence=NULL) public static function insertId($sequence=NULL)
{ {
return self::getConnection()->getInsertId($sequence); return self::getConnection()->insertId($sequence);
} }
/** /**
* Begins a transaction - Monostate for DibiConnection::begin(). * Begins a transaction - Monostate for DibiConnection::begin().
* @param string optional savepoint name
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public static function begin($savepoint = NULL) public static function begin()
{ {
self::getConnection()->begin($savepoint); self::getConnection()->begin();
} }
/** /**
* Commits statements in a transaction - Monostate for DibiConnection::commit($savepoint = NULL). * Commits statements in a transaction - Monostate for DibiConnection::commit().
* @param string optional savepoint name
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public static function commit($savepoint = NULL) public static function commit()
{ {
self::getConnection()->commit($savepoint); self::getConnection()->commit();
} }
/** /**
* Rollback changes in a transaction - Monostate for DibiConnection::rollback(). * Rollback changes in a transaction - Monostate for DibiConnection::rollback().
* @param string optional savepoint name
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public static function rollback($savepoint = NULL) public static function rollback()
{ {
self::getConnection()->rollback($savepoint); self::getConnection()->rollback();
} }
/** /**
* Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo(). * Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo().
* @return DibiDatabaseInfo * @return DibiDatabaseInfo
@@ -392,6 +424,7 @@ class dibi
} }
/** /**
* Import SQL dump from file - extreme fast! * Import SQL dump from file - extreme fast!
* @param string filename * @param string filename
@@ -403,21 +436,24 @@ class dibi
} }
/** /**
* Replacement for majority of dibi::methods() in future. * Replacement for majority of dibi::methods() in future.
*/ */
public static function __callStatic($name, $args) public static function __callStatic($name, $args)
{ {
//if ($name = 'select', 'update', ...') { //if ($name = 'select', 'update', ...') {
// return self::command()->$name($args); // return self::command()->$name($args);
//} //}
return call_user_func_array(array(self::getConnection(), $name), $args); return call_user_func_array(array(self::getConnection(), $name), $args);
} }
/********************* fluent SQL builders ****************d*g**/ /********************* fluent SQL builders ****************d*g**/
/** /**
* @return DibiFluent * @return DibiFluent
*/ */
@@ -427,6 +463,7 @@ class dibi
} }
/** /**
* @param string column name * @param string column name
* @return DibiFluent * @return DibiFluent
@@ -434,32 +471,35 @@ class dibi
public static function select($args) public static function select($args)
{ {
$args = func_get_args(); $args = func_get_args();
return call_user_func_array(array(self::getConnection(), 'select'), $args); return self::getConnection()->command()->__call('select', $args);
} }
/** /**
* @param string table * @param string table
* @param array * @param array
* @return DibiFluent * @return DibiFluent
*/ */
public static function update($table, $args) public static function update($table, array $args)
{ {
return self::getConnection()->update($table, $args); return self::getConnection()->update($table, $args);
} }
/** /**
* @param string table * @param string table
* @param array * @param array
* @return DibiFluent * @return DibiFluent
*/ */
public static function insert($table, $args) public static function insert($table, array $args)
{ {
return self::getConnection()->insert($table, $args); return self::getConnection()->insert($table, $args);
} }
/** /**
* @param string table * @param string table
* @return DibiFluent * @return DibiFluent
@@ -470,76 +510,135 @@ class dibi
} }
/********************* data types ****************d*g**/ /********************* data types ****************d*g**/
/** /**
* @deprecated * Pseudotype for timestamp representation.
* @param mixed datetime
* @return DibiVariable
*/ */
public static function datetime($time = NULL) public static function datetime($time = NULL)
{ {
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING); if ($time === NULL) {
return new DibiDateTime($time); $time = time(); // current time
} elseif (is_string($time)) {
$time = strtotime($time); // try convert to timestamp
} else {
$time = (int) $time;
}
return new DibiVariable($time, dibi::FIELD_DATETIME);
} }
/** /**
* @deprecated * Pseudotype for date representation.
* @param mixed date
* @return DibiVariable
*/ */
public static function date($date = NULL) public static function date($date = NULL)
{ {
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING); $var = self::datetime($date);
return new DibiDateTime($date); $var->modifier = dibi::FIELD_DATE;
return $var;
} }
/********************* substitutions ****************d*g**/ /********************* substitutions ****************d*g**/
/** /**
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes(). * Create a new substitution pair for indentifiers.
* @return DibiHashMap * @param string from
* @param string to
* @return void
*/ */
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/** @deprecated */
public static function addSubst($expr, $subst) public static function addSubst($expr, $subst)
{ {
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->expr = val; instead.', E_USER_WARNING); self::$substs[$expr] = $subst;
self::getSubstitutes()->$expr = $subst;
} }
/** @deprecated */
/**
* Sets substitution fallback handler.
* @param callback
* @return void
*/
public static function setSubstFallback($callback)
{
if (!is_callable($callback)) {
throw new InvalidArgumentException("Invalid callback.");
}
self::$substFallBack = $callback;
}
/**
* Remove substitution pair.
* @param mixed from or TRUE
* @return void
*/
public static function removeSubst($expr) public static function removeSubst($expr)
{ {
trigger_error(__METHOD__ . '() is deprecated; use unset(dibi::getSubstitutes()->expr) instead.', E_USER_WARNING);
$substitutes = self::getSubstitutes();
if ($expr === TRUE) { if ($expr === TRUE) {
foreach ($substitutes as $expr => $foo) { self::$substs = array();
unset($substitutes->$expr);
}
} else { } else {
unset($substitutes->$expr); unset(self::$substs[':'.$expr.':']);
} }
} }
/** @deprecated */
public static function setSubstFallback($callback) /**
* Provides substitution.
* @param string
* @return string
*/
public static function substitute($value)
{ {
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->setCallback() instead.', E_USER_WARNING); if (strpos($value, ':') === FALSE) {
self::getSubstitutes()->setCallback($callback); return $value;
} else {
return preg_replace_callback('#:(.*):#U', array('dibi', 'subCb'), $value);
}
} }
/**
* Substitution callback.
* @param array
* @return string
*/
private static function subCb($m)
{
$m = $m[1];
if (isset(self::$substs[$m])) {
return self::$substs[$m];
} elseif (self::$substFallBack) {
return self::$substs[$m] = call_user_func(self::$substFallBack, $m);
} else {
return $m;
}
}
/********************* misc tools ****************d*g**/ /********************* misc tools ****************d*g**/
/** /**
* Prints out a syntax highlighted version of the SQL command or DibiResult. * Prints out a syntax highlighted version of the SQL command or DibiResult.
* @param string|DibiResult * @param string|DibiResult
@@ -553,31 +652,26 @@ class dibi
$sql->dump(); $sql->dump();
} else { } else {
if ($sql === NULL) { if ($sql === NULL) $sql = self::$sql;
$sql = self::$sql;
}
static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE'; static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE'; static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE';
// insert new lines // insert new lines
$sql = " $sql "; $sql = ' ' . $sql;
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql); $sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces // reduce spaces
$sql = preg_replace('#[ \t]{2,}#', " ", $sql); $sql = preg_replace('#[ \t]{2,}#', " ", $sql);
$sql = wordwrap($sql, 100); $sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql); $sql = htmlSpecialChars($sql);
$sql = preg_replace("#\n{2,}#", "\n", $sql);
if (PHP_SAPI === 'cli') { // syntax highlight
echo trim($sql) . "\n\n"; $sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql);
} else { $sql = trim($sql);
// syntax highlight echo '<pre class="dump">', $sql, "</pre>\n";
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n";
}
} }
if ($return) { if ($return) {
@@ -588,20 +682,39 @@ class dibi
} }
private static function highlightCallback($matches) private static function highlightCallback($matches)
{ {
if (!empty($matches[1])) { // comment if (!empty($matches[1])) // comment
return '<em style="color:gray">' . $matches[1] . '</em>'; return '<em style="color:gray">' . $matches[1] . '</em>';
} elseif (!empty($matches[2])) { // error if (!empty($matches[2])) // error
return '<strong style="color:red">' . $matches[2] . '</strong>'; return '<strong style="color:red">' . $matches[2] . '</strong>';
} elseif (!empty($matches[3])) { // most important keywords if (!empty($matches[3])) // most important keywords
return '<strong style="color:blue">' . $matches[3] . '</strong>'; return '<strong style="color:blue">' . $matches[3] . '</strong>';
} elseif (!empty($matches[4])) { // other keywords if (!empty($matches[4])) // other keywords
return '<strong style="color:green">' . $matches[4] . '</strong>'; return '<strong style="color:green">' . $matches[4] . '</strong>';
}
/**
* Returns brief descriptions.
* @return string
* @return array
*/
public static function getColophon($sender = NULL)
{
$arr = array(
'Number of SQL queries: ' . dibi::$numOfQueries
. (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms'),
);
if ($sender === 'bluescreen') {
$arr[] = 'dibi ' . dibi::VERSION . ' (revision ' . dibi::REVISION . ')';
} }
return $arr;
} }
} }

View File

@@ -1,840 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* The dibi driver for Firebird/InterBase database.
*
* Driver options:
* - database => the path to database file (server:/path/database.fdb)
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author Tomáš Kraina, Roman Sklenář
* @package dibi\drivers
*/
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var resource Resultset resource */
private $transaction;
/** @var bool */
private $inTransaction = FALSE;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
DibiConnection::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
if (!isset($config['username'])) $config['username'] = ini_get('ibase.default_password');
if (!isset($config['password'])) $config['password'] = ini_get('ibase.default_user');
if (!isset($config['database'])) $config['database'] = ini_get('ibase.default_db');
if (!isset($config['charset'])) $config['charset'] = ini_get('ibase.default_charset');
if (!isset($config['buffers'])) $config['buffers'] = 0;
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, ibase_errcode());
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
}
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
ibase_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), dibi::$sql);
}
}
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return ibase_affected_rows($this->connection);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string generator name
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return ibase_gen_id($sequence, 0, $this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->resource);
$this->inTransaction = TRUE;
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return $this->inTransaction;
}
/**
* Returns the connection resource.
* @return resource
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ********************/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return $value;
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new DibiNotImplementedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit >= 0 && $offset > 0) {
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
}
}
/********************* result set ********************/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return ibase_num_fields($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else {
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
}
}
return $result;
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
public function seek($row)
{
throw new DibiNotSupportedException("Firebird/Interbase do not support seek in result set.");
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
ibase_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns the result set resource.
* @return mysqli_result
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
);
}
return $columns;
}
/********************* IDibiReflector ********************/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->query("
SELECT TRIM(RDB\$RELATION_NAME),
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'TRUE',
);
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME,
CASE f.RDB\$FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'INT64'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS FIELD_TYPE,
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
r.RDB\$DEFAULT_VALUE AS DEFAULT_VALUE,
CASE r.RDB\$NULL_FLAG
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
END AS NULLABLE
FROM RDB\$RELATION_FIELDS r
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
WHERE r.RDB\$RELATION_NAME = '$table'
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = array(
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
'size' => $row['FIELD_LENGTH'],
'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table (the constraints are included).
* @param string
* @return array
*/
public function getIndexes($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG,
i.RDB\$FOREIGN_KEY AS FOREIGN_KEY,
TRIM(r.RDB\$CONSTRAINT_TYPE) AS CONSTRAINT_TYPE,
s.RDB\$FIELD_POSITION AS FIELD_POSITION
FROM RDB\$INDEX_SEGMENTS s
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
$indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
$indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$key]['table'] = $table;
$indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
}
return $indexes;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
FROM RDB\$INDEX_SEGMENTS s
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = array(
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
);
}
return $keys;
}
/**
* Returns list of indices in given table (the constraints are not listed).
* @param string
* @return array
*/
public function getIndices($table)
{
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = array();
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
return $indices;
}
/**
* Returns list of constraints in given table.
* @param string
* @return array
*/
public function getConstraints($table)
{
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND (
RDB\$UNIQUE_FLAG IS NOT NULL
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = array();
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
return $constraints;
}
/**
* Returns metadata for all triggers in a table or database.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
* @param string
* @param string
* @return array
*/
public function getTriggersMeta($table = NULL)
{
$res = $this->query("
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'BEFORE'
WHEN 2 THEN 'AFTER'
WHEN 3 THEN 'BEFORE'
WHEN 4 THEN 'AFTER'
WHEN 5 THEN 'BEFORE'
WHEN 6 THEN 'AFTER'
END AS TRIGGER_TYPE,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'INSERT'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'UPDATE'
WHEN 4 THEN 'UPDATE'
WHEN 5 THEN 'DELETE'
WHEN 6 THEN 'DELETE'
END AS TRIGGER_EVENT,
CASE RDB\$TRIGGER_INACTIVE
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
END AS TRIGGER_ENABLED
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = array();
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = array(
'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']),
'event' => trim($row['TRIGGER_EVENT']),
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
);
}
return $triggers;
}
/**
* Returns list of triggers for given table.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
* @param string
* @return array
*/
public function getTriggers($table = NULL)
{
$q = "SELECT TRIM(RDB\$TRIGGER_NAME)
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0";
$q .= $table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = array();
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
return $triggers;
}
/**
* Returns metadata from stored procedures and their input and output parameters.
* @param string
* @return array
*/
public function getProceduresMeta()
{
$res = $this->query("
SELECT
TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME,
TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME,
CASE p.RDB\$PARAMETER_TYPE
WHEN 0 THEN 'INPUT'
WHEN 1 THEN 'OUTPUT'
ELSE 'UNKNOWN'
END AS PARAMETER_TYPE,
CASE f.RDB\$FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'INT64'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS FIELD_TYPE,
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
FROM RDB\$PROCEDURE_PARAMETERS p
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
$num = $row['PARAMETER_NUMBER'];
$procedures[$key]['name'] = $row['PROCEDURE_NAME'];
$procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME'];
$procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
$procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
}
return $procedures;
}
/**
* Returns list of stored procedures.
* @return array
*/
public function getProcedures()
{
$res = $this->query("
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = array();
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
return $procedures;
}
/**
* Returns list of generators.
* @return array
*/
public function getGenerators()
{
$res = $this->query("
SELECT TRIM(RDB\$GENERATOR_NAME)
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = array();
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
return $generators;
}
/**
* Returns list of user defined functions (UDF).
* @return array
*/
public function getFunctions()
{
$res = $this->query("
SELECT TRIM(RDB\$FUNCTION_NAME)
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = array();
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
return $functions;
}
}
/**
* Database procedure exception.
*
* @author Roman Sklenář
* @copyright Copyright (c) 2010
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}

View File

@@ -1,60 +1,75 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
require_once dirname(__FILE__) . '/mssql.reflector.php';
/** /**
* The dibi driver for MS SQL database. * The dibi driver for MS SQL database.
* *
* Driver options: * Connection options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port) * - 'host' - the MS SQL server host name. It can also include a port number (hostname:port)
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - database => the database name to select * - 'persistent' - try to find a persistent link?
* - persistent (bool) => try to find a persistent link? * - 'database' - the database name to select
* - resource (resource) => existing connection resource * - 'lazy' - if TRUE, connection will be established only when required
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver class DibiMsSqlDriver extends DibiObject implements IDibiDriver
{ {
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('mssql')) { if (!extension_loaded('mssql')) {
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded."); throw new DibiDriverException("PHP extension 'mssql' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { DibiConnection::alias($config, 'username', 'user');
$this->connection = $config['resource']; DibiConnection::alias($config, 'password', 'pass');
} elseif (empty($config['persistent'])) { DibiConnection::alias($config, 'host', 'hostname');
if (empty($config['persistent'])) {
$this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @ $this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @
} else { } else {
$this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @ $this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @
@@ -64,12 +79,13 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
throw new DibiDriverException("Can't connect to DB."); throw new DibiDriverException("Can't connect to DB.");
} }
if (isset($config['database']) && !@mssql_select_db($this->escape($config['database'], dibi::IDENTIFIER), $this->connection)) { // intentionally @ if (isset($config['database']) && !@mssql_select_db($config['database'], $this->connection)) { // intentionally @
throw new DibiDriverException("Can't select DB '$config[database]'."); throw new DibiDriverException("Can't select DB '$config[database]'.");
} }
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -80,228 +96,189 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
$res = @mssql_query($sql, $this->connection); // intentionally @ $this->resultSet = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) { if ($this->resultSet === FALSE) {
throw new DibiDriverException(mssql_get_last_message(), 0, $sql); throw new DibiDriverException('Query error', 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
} }
return is_resource($this->resultSet) ? clone $this : NULL;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return mssql_rows_affected($this->connection); return mssql_rows_affected($this->connection);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
$res = mssql_query('SELECT @@IDENTITY', $this->connection); throw new NotSupportedException('MS SQL does not support autoincrementing.');
if (is_resource($res)) {
$row = mssql_fetch_row($res);
return $row[0];
}
return FALSE;
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->query('BEGIN TRANSACTION'); $this->query('BEGIN TRANSACTION');
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->query('COMMIT'); $this->query('COMMIT');
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->query('ROLLBACK'); $this->query('ROLLBACK');
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return mixed
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiMsSqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
case dibi::BINARY: case dibi::FIELD_BINARY:
return "'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx // @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']'; $value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
case dibi::BOOL: case dibi::FIELD_BOOL:
return $value ? 1 : 0; return $value ? -1 : 0;
case dibi::DATE: case dibi::FIELD_DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value); return date("'Y-m-d'", $value);
case dibi::DATETIME: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value); return date("'Y-m-d H:i:s'", $value);
default: default:
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
// offset support is missing // offset suppot is missing...
if ($limit >= 0) { if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
} }
if ($offset) { if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.'); throw new NotImplementedException('Offset is not implemented.');
} }
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
return mssql_num_rows($this->resultSet); return mssql_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -309,10 +286,12 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
@@ -320,6 +299,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -331,36 +311,87 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = mssql_num_fields($this->resultSet); $count = mssql_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i); $row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = array( $res[] = array(
'name' => $row['name'], 'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'], 'table' => $row['column_source'],
'nativetype' => $row['type'], 'nativetype' => $row['type'],
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
} }
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
throw new NotImplementedException;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
}
} }

View File

@@ -1,216 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2010 David Grudl (http://davidgrudl.com)
*
* @package dibi\drivers
*/
/**
* The dibi reflector for MsSQL databases.
*
* @author Steven Bredenberg
* @package dibi\drivers
* @internal
*/
class DibiMsSqlReflector extends DibiObject implements IDibiReflector
{
/** @var IDibiDriver */
private $driver;
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query("
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
");
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
return $tables;
}
/**
* Returns count of rows in a table
* @param string
* @return integer
*/
public function getTableCount($table, $fallback=true)
{
if (empty($table)) {
return false;
}
$result = $this->driver->query("
SELECT MAX(rowcnt)
FROM sys.sysindexes
WHERE id=OBJECT_ID({$this->driver->escape($table, dibi::IDENTIFIER)})
");
$row = $result->fetch(FALSE);
if (!is_array($row) || count($row) < 1) {
if ($fallback) {
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escape($table, dibi::IDENTIFIER)}")->fetch(FALSE);
$count = intval($row[0]);
} else {
$count = false;
}
} else {
$count = intval($row[0]);
}
return $count;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = $this->driver->query("
SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
ORDER BY TABLE_NAME, ORDINAL_POSITION
");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = false;
$type = strtoupper($row['DATA_TYPE']);
$size_cols = array(
'DATETIME'=>'DATETIME_PRECISION',
'DECIMAL'=>'NUMERIC_PRECISION',
'CHAR'=>'CHARACTER_MAXIMUM_LENGTH',
'NCHAR'=>'CHARACTER_OCTET_LENGTH',
'NVARCHAR'=>'CHARACTER_OCTET_LENGTH',
'VARCHAR'=>'CHARACTER_OCTET_LENGTH'
);
if (isset($size_cols[$type])) {
if ($size_cols[$type]) {
$size = $row[$size_cols[$type]];
}
}
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $type,
'size' => $size,
'unsigned' => NULL,
'nullable' => $row['IS_NULLABLE'] === 'YES',
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => false,
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query(
"SELECT ind.name index_name, ind.index_id, ic.index_column_id,
col.name column_name, ind.is_unique, ind.is_primary_key
FROM sys.indexes ind
INNER JOIN sys.index_columns ic ON
(ind.object_id = ic.object_id AND ind.index_id = ic.index_id)
INNER JOIN sys.columns col ON
(ic.object_id = col.object_id and ic.column_id = col.column_id)
INNER JOIN sys.tables t ON
(ind.object_id = t.object_id)
WHERE t.name = {$this->driver->escape($table, dibi::TEXT)}
AND t.is_ms_shipped = 0
ORDER BY
t.name, ind.name, ind.index_id, ic.index_column_id
");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$index_name = $row['index_name'];
if (!isset($indexes[$index_name])) {
$indexes[$index_name] = array();
$indexes[$index_name]['name'] = $index_name;
$indexes[$index_name]['unique'] = (bool)$row['is_unique'];
$indexes[$index_name]['primary'] = (bool)$row['is_primary_key'];
$indexes[$index_name]['columns'] = array();
}
$indexes[$index_name]['columns'][] = $row['column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$res = $this->driver->query("
SELECT f.name AS foreign_key,
OBJECT_NAME(f.parent_object_id) AS table_name,
COL_NAME(fc.parent_object_id,
fc.parent_column_id) AS column_name,
OBJECT_NAME (f.referenced_object_id) AS reference_table_name,
COL_NAME(fc.referenced_object_id,
fc.referenced_column_id) AS reference_column_name,
fc.*
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escape($table, dibi::TEXT)}
");
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key_name = $row['foreign_key'];
if (!isset($keys[$key_name])) {
$keys[$key_name]['name'] = $row['foreign_key']; // foreign key name
$keys[$key_name]['local'] = array($row['column_name']); // local columns
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
$keys[$key_name]['foreign'] = array($row['reference_column_name']); // referenced columns
$keys[$key_name]['onDelete'] = false;
$keys[$key_name]['onUpdate'] = false;
} else {
$keys[$key_name]['local'][] = $row['column_name']; // local columns
$keys[$key_name]['foreign'][] = $row['reference_column_name']; // referenced columns
}
}
return array_values($keys);
}
}

View File

@@ -1,369 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* The dibi driver for MS SQL Driver 2005 database.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => connection options {@link http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
DibiConnection::alias($config, 'options|UID', 'username');
DibiConnection::alias($config, 'options|PWD', 'password');
DibiConnection::alias($config, 'options|Database', 'database');
DibiConnection::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
}
if (!is_resource($this->connection)) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code']);
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
sqlsrv_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = sqlsrv_query($this->connection, $sql);
if ($res === FALSE) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res);
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0];
}
return FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
sqlsrv_begin_transaction($this->connection);
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
sqlsrv_commit($this->connection);
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
sqlsrv_rollback($this->connection);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
throw new DibiNotSupportedException;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
sqlsrv_free_stmt($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$columns = array();
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = array(
'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'],
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -1,105 +1,109 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
require_once dirname(__FILE__) . '/mysql.reflector.php';
/** /**
* The dibi driver for MySQL database. * The dibi driver for MySQL database.
* *
* Driver options: * Connection options:
* - host => the MySQL server host name * - 'host' - the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server * - 'port' - the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe * - 'socket' - the socket or named pipe
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - database => the database name to select * - 'persistent' - try to find a persistent link?
* - flags (int) => driver specific constants (MYSQL_CLIENT_*) * - 'database' - the database name to select
* - charset => character encoding to set (default is utf8) * - 'charset' - character encoding to set
* - persistent (bool) => try to find a persistent link? * - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically? * - 'options' - driver specific constants (MYSQL_*)
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html * - 'sqlmode' - see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (resource) => existing connection resource * - 'lazy' - if TRUE, connection will be established only when required
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver class DibiMySqlDriver extends DibiObject implements IDibiDriver
{ {
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */ /** @var bool Is buffered (seekable and countable)? */
private $buffered; private $buffered;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('mysql')) { if (!extension_loaded('mysql')) {
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded."); throw new DibiDriverException("PHP extension 'mysql' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { DibiConnection::alias($config, 'username', 'user');
$this->connection = $config['resource']; DibiConnection::alias($config, 'password', 'pass');
DibiConnection::alias($config, 'host', 'hostname');
DibiConnection::alias($config, 'options');
// default values
if (!isset($config['username'])) $config['username'] = ini_get('mysql.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('mysql.default_password');
if (!isset($config['host'])) {
$host = ini_get('mysql.default_host');
if ($host) {
$config['host'] = $host;
$config['port'] = ini_get('mysql.default_port');
} else {
if (!isset($config['socket'])) $config['socket'] = ini_get('mysql.default_socket');
$config['host'] = NULL;
}
}
if (empty($config['socket'])) {
$host = $config['host'] . (empty($config['port']) ? '' : ':' . $config['port']);
} else { } else {
// default values $host = ':' . $config['socket'];
DibiConnection::alias($config, 'flags', 'options'); }
if (!isset($config['charset'])) $config['charset'] = 'utf8';
if (!isset($config['username'])) $config['username'] = ini_get('mysql.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('mysql.default_password');
if (!isset($config['host'])) {
$host = ini_get('mysql.default_host');
if ($host) {
$config['host'] = $host;
$config['port'] = ini_get('mysql.default_port');
} else {
if (!isset($config['socket'])) {
$config['socket'] = ini_get('mysql.default_socket');
}
$config['host'] = NULL;
}
}
if (empty($config['socket'])) { if (empty($config['persistent'])) {
$host = $config['host'] . (empty($config['port']) ? '' : ':' . $config['port']); $this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['options']); // intentionally @
} else { } else {
$host = ':' . $config['socket']; $this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['options']); // intentionally @
}
if (empty($config['persistent'])) {
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @
} else {
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
}
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -113,7 +117,10 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
$ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @ $ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
} }
if (!$ok) { if (!$ok) {
$this->query("SET NAMES '$config[charset]'"); $ok = @mysql_query("SET NAMES '$config[charset]'", $this->connection); // intentionally @
if (!$ok) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
} }
} }
@@ -124,15 +131,16 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
if (isset($config['sqlmode'])) { if (isset($config['sqlmode'])) {
$this->query("SET sql_mode='$config[sqlmode]'"); if (!@mysql_query("SET sql_mode='$config[sqlmode]'", $this->connection)) { // intentionally @
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
} }
$this->query("SET time_zone='" . date('P') . "'");
$this->buffered = empty($config['unbuffered']); $this->buffered = empty($config['unbuffered']);
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -143,254 +151,193 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
if ($this->buffered) { if ($this->buffered) {
$res = @mysql_query($sql, $this->connection); // intentionally @ $this->resultSet = @mysql_query($sql, $this->connection); // intentionally @
} else { } else {
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @ $this->resultSet = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
} }
if (mysql_errno($this->connection)) { if (mysql_errno($this->connection)) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql); throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
} }
return is_resource($this->resultSet) ? clone $this : NULL;
} }
/**
* Retrieves information about the most recently executed query.
* @return array
*/
public function getInfo()
{
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new DibiPcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
return $res;
}
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return mysql_affected_rows($this->connection); return mysql_affected_rows($this->connection);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
return mysql_insert_id($this->connection); return mysql_insert_id($this->connection);
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query('START TRANSACTION');
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query('COMMIT');
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query('ROLLBACK');
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return mixed
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
if (!is_resource($this->connection)) { case dibi::FIELD_BINARY:
throw new DibiException('Lost connection to server.'); return "'" . mysql_real_escape_string($value, $this->connection) . "'";
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::BINARY: case dibi::IDENTIFIER:
if (!is_resource($this->connection)) { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
throw new DibiException('Lost connection to server.'); $value = str_replace('`', '``', $value);
} return '`' . str_replace('.', '`.`', $value) . '`';
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::IDENTIFIER: case dibi::FIELD_BOOL:
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html return $value ? 1 : 0;
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL: case dibi::FIELD_DATE:
return $value ? 1 : 0; return date("'Y-m-d'", $value);
case dibi::DATE: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value); return date("'Y-m-d H:i:s'", $value);
case dibi::DATETIME: default:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value); throw new InvalidArgumentException('Unsupported type.');
default:
throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit >= 0 || $offset > 0) { if ($limit < 0 && $offset < 1) return;
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) // see http://dev.mysql.com/doc/refman/5.0/en/select.html
. ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
} . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiDriverException('Row count is not available for unbuffered queries.');
} }
return mysql_num_rows($this->resultSet); return mysql_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -398,6 +345,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
@@ -407,13 +355,14 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function seek($row) public function seek($row)
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiDriverException('Cannot seek an unbuffered result set.');
} }
return mysql_data_seek($this->resultSet, $row); return mysql_data_seek($this->resultSet, $row);
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -425,17 +374,18 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = mysql_num_fields($this->resultSet); $count = mysql_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i); $row = (array) mysql_fetch_field($this->resultSet, $i);
$columns[] = array( $res[] = array(
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['table'], 'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
@@ -443,18 +393,102 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
'vendor' => $row, 'vendor' => $row,
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return is_resource($this->resultSet) ? $this->resultSet : NULL; }
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$this->query("SHOW FULL TABLES");
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$this->query("SHOW COLUMNS FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$res[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$this->query("SHOW INDEX FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['Key_name']]['name'] = $row['Key_name'];
$res[$row['Key_name']]['unique'] = !$row['Non_unique'];
$res[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$res[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
$this->free();
return array_values($res);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
} }
} }

View File

@@ -1,120 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* The dibi reflector for MySQL databases.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class DibiMySqlReflector extends DibiObject implements IDibiReflector
{
/** @var IDibiDriver */
private $driver;
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
/*$this->query("
SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query("SHOW FULL TABLES");
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$columns[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'unsigned' => (bool) strstr($row['Type'], 'unsigned'),
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
AND REFERENCED_COLUMN_NAME IS NULL
");*/
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
}
}

View File

@@ -1,114 +1,107 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
require_once dirname(__FILE__) . '/mysql.reflector.php';
/** /**
* The dibi driver for MySQL database via improved extension. * The dibi driver for MySQL database via improved extension.
* *
* Driver options: * Connection options:
* - host => the MySQL server host name * - 'host' - the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server * - 'port' - the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe * - 'socket' - the socket or named pipe
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - database => the database name to select * - 'persistent' - try to find a persistent link?
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options} * - 'database' - the database name to select
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect} * - 'charset' - character encoding to set
* - charset => character encoding to set (default is utf8) * - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - persistent (bool) => try to find a persistent link? * - 'options' - driver specific constants (MYSQLI_*)
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically? * - 'sqlmode' - see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html * - 'lazy' - if TRUE, connection will be established only when required
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver class DibiMySqliDriver extends DibiObject implements IDibiDriver
{ {
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var mysqli Connection resource */ /** @var mysqli Connection resource */
private $connection; private $connection;
/** @var mysqli_result Resultset resource */ /** @var mysqli_result Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */ /** @var bool Is buffered (seekable and countable)? */
private $buffered; private $buffered;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('mysqli')) { if (!extension_loaded('mysqli')) {
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded."); throw new DibiDriverException("PHP extension 'mysqli' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
mysqli_report(MYSQLI_REPORT_OFF); DibiConnection::alias($config, 'username', 'user');
if (isset($config['resource'])) { DibiConnection::alias($config, 'password', 'pass');
$this->connection = $config['resource']; DibiConnection::alias($config, 'host', 'hostname');
DibiConnection::alias($config, 'options');
DibiConnection::alias($config, 'database');
} else { // default values
// default values if (!isset($config['username'])) $config['username'] = ini_get('mysqli.default_user');
if (!isset($config['charset'])) $config['charset'] = 'utf8'; if (!isset($config['password'])) $config['password'] = ini_get('mysqli.default_pw');
if (!isset($config['username'])) $config['username'] = ini_get('mysqli.default_user'); if (!isset($config['socket'])) $config['socket'] = ini_get('mysqli.default_socket');
if (!isset($config['password'])) $config['password'] = ini_get('mysqli.default_pw'); if (!isset($config['port'])) $config['port'] = NULL;
if (!isset($config['socket'])) $config['socket'] = ini_get('mysqli.default_socket'); if (!isset($config['host'])) {
if (!isset($config['port'])) $config['port'] = NULL; $host = ini_get('mysqli.default_host');
if (!isset($config['host'])) { if ($host) {
$host = ini_get('mysqli.default_host'); $config['host'] = $host;
if ($host) { $config['port'] = ini_get('mysqli.default_port');
$config['host'] = $host; } else {
$config['port'] = ini_get('mysqli.default_port'); $config['host'] = NULL;
} else { $config['port'] = NULL;
$config['host'] = NULL;
$config['port'] = NULL;
}
} }
}
$foo = & $config['flags']; $this->connection = mysqli_init();
$foo = & $config['database']; @mysqli_real_connect($this->connection, $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['options']); // intentionally @
$this->connection = mysqli_init(); if ($errno = mysqli_connect_errno()) {
if (isset($config['options'])) { throw new DibiDriverException(mysqli_connect_error(), $errno);
if (is_scalar($config['options'])) {
$config['flags'] = $config['options']; // back compatibility
trigger_error(__CLASS__ . ": configuration item 'options' must be array; for constants MYSQLI_CLIENT_* use 'flags'.", E_USER_NOTICE);
} else {
foreach ((array) $config['options'] as $key => $value) {
mysqli_options($this->connection, $key, $value);
}
}
}
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new DibiDriverException(mysqli_connect_error(), $errno);
}
} }
if (isset($config['charset'])) { if (isset($config['charset'])) {
@@ -118,20 +111,24 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
$ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @ $ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @
} }
if (!$ok) { if (!$ok) {
$this->query("SET NAMES '$config[charset]'"); $ok = @mysqli_query($this->connection, "SET NAMES '$config[charset]'"); // intentionally @
if (!$ok) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection));
}
} }
} }
if (isset($config['sqlmode'])) { if (isset($config['sqlmode'])) {
$this->query("SET sql_mode='$config[sqlmode]'"); if (!@mysqli_query($this->connection, "SET sql_mode='$config[sqlmode]'")) { // intentionally @
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection));
}
} }
$this->query("SET time_zone='" . date('P') . "'");
$this->buffered = empty($config['unbuffered']); $this->buffered = empty($config['unbuffered']);
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -142,243 +139,188 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @ $this->resultSet = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if (mysqli_errno($this->connection)) { if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql); throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
} }
return is_object($this->resultSet) ? clone $this : NULL;
} }
/**
* Retrieves information about the most recently executed query.
* @return array
*/
public function getInfo()
{
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new DibiPcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
return $res;
}
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return mysqli_affected_rows($this->connection) === -1 ? FALSE : mysqli_affected_rows($this->connection); return mysqli_affected_rows($this->connection);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
return mysqli_insert_id($this->connection); return mysqli_insert_id($this->connection);
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query('START TRANSACTION');
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query('COMMIT');
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query('ROLLBACK');
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mysqli * @return mysqli
*/ */
public function getResource() public function getResource()
{ {
return @$this->connection->thread_id ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return IDibiResultDriver
*/
public function createResultDriver(mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'"; case dibi::FIELD_BINARY:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::BINARY: case dibi::IDENTIFIER:
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'"; $value = str_replace('`', '``', $value);
return '`' . str_replace('.', '`.`', $value) . '`';
case dibi::IDENTIFIER: case dibi::FIELD_BOOL:
return '`' . str_replace('`', '``', $value) . '`'; return $value ? 1 : 0;
case dibi::BOOL: case dibi::FIELD_DATE:
return $value ? 1 : 0; return date("'Y-m-d'", $value);
case dibi::DATE: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value); return date("'Y-m-d H:i:s'", $value);
case dibi::DATETIME: default:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value); throw new InvalidArgumentException('Unsupported type.');
default:
throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit >= 0 || $offset > 0) { if ($limit < 0 && $offset < 1) return;
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit) // see http://dev.mysql.com/doc/refman/5.0/en/select.html
. ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
} . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && @$this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiDriverException('Row count is not available for unbuffered queries.');
} }
return mysqli_num_rows($this->resultSet); return mysqli_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -386,6 +328,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
@@ -395,12 +338,13 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function seek($row) public function seek($row)
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiDriverException('Cannot seek an unbuffered result set.');
} }
return mysqli_data_seek($this->resultSet, $row); return mysqli_data_seek($this->resultSet, $row);
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -412,11 +356,12 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
static $types; static $types;
if (empty($types)) { if (empty($types)) {
@@ -426,14 +371,13 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
$types[$value] = substr($key, 12); $types[$value] = substr($key, 12);
} }
} }
$types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
} }
$count = mysqli_num_fields($this->resultSet); $count = mysqli_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i); $row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$columns[] = array( $res[] = array(
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['orgtable'], 'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
@@ -441,18 +385,120 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
'vendor' => $row, 'vendor' => $row,
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mysqli_result * @return mysqli_result
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return $this->resultSet === NULL || $this->resultSet->type === NULL ? NULL : $this->resultSet; }
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
/*$this->query("
SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE()
");*/
$this->query("SHOW FULL TABLES");
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
/*$table = $this->escape($table, dibi::FIELD_TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
");*/
$this->query("SHOW COLUMNS FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$res[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
/*$table = $this->escape($table, dibi::FIELD_TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
AND REFERENCED_COLUMN_NAME IS NULL
");*/
$this->query("SHOW INDEX FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['Key_name']]['name'] = $row['Key_name'];
$res[$row['Key_name']]['unique'] = !$row['Non_unique'];
$res[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$res[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
$this->free();
return array_values($res);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
} }
} }

View File

@@ -1,74 +1,85 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* The dibi driver interacting with databases via ODBC connections. * The dibi driver interacting with databases via ODBC connections.
* *
* Driver options: * Connection options:
* - dsn => driver specific DSN * - 'dsn' - driver specific DSN
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - persistent (bool) => try to find a persistent link? * - 'persistent' - try to find a persistent link?
* - resource (resource) => existing connection resource * - 'lazy' - if TRUE, connection will be established only when required
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector class DibiOdbcDriver extends DibiObject implements IDibiDriver
{ {
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var int Cursor */ /** @var int Cursor */
private $row = 0; private $row = 0;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('odbc')) { if (!extension_loaded('odbc')) {
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded."); throw new DibiDriverException("PHP extension 'odbc' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { DibiConnection::alias($config, 'username', 'user');
$this->connection = $config['resource']; DibiConnection::alias($config, 'password', 'pass');
} else {
// default values
if (!isset($config['username'])) $config['username'] = ini_get('odbc.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('odbc.default_pw');
if (!isset($config['dsn'])) $config['dsn'] = ini_get('odbc.default_db');
if (empty($config['persistent'])) { // default values
$this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @ if (!isset($config['username'])) $config['username'] = ini_get('odbc.default_user');
} else { if (!isset($config['password'])) $config['password'] = ini_get('odbc.default_pw');
$this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @ if (!isset($config['dsn'])) $config['dsn'] = ini_get('odbc.default_db');
}
if (empty($config['persistent'])) {
$this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @
} else {
$this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -77,6 +88,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -87,54 +99,54 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = FALSE; $this->resultSet = @odbc_exec($this->connection, $sql); // intentionally @
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($this->resultSet === FALSE) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
return $this->createResultDriver($res);
} }
return is_resource($this->resultSet) ? clone $this : NULL;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return $this->affectedRows; return odbc_num_rows($this->resultSet);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
throw new DibiNotSupportedException('ODBC does not support autoincrementing.'); throw new NotSupportedException('ODBC does not support autoincrementing.');
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
if (!odbc_autocommit($this->connection, FALSE)) { if (!odbc_autocommit($this->connection, FALSE)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
@@ -142,13 +154,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
if (!odbc_commit($this->connection)) { if (!odbc_commit($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
@@ -157,13 +169,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
if (!odbc_rollback($this->connection)) { if (!odbc_rollback($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
@@ -172,15 +184,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !odbc_autocommit($this->connection);
}
/** /**
* Returns the connection resource. * Returns the connection resource.
@@ -188,142 +191,103 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
case dibi::BINARY: case dibi::FIELD_BINARY:
return "'" . str_replace("'", "''", $value) . "'"; return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']'; $value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
case dibi::BOOL: case dibi::FIELD_BOOL:
return $value ? 1 : 0; return $value ? -1 : 0;
case dibi::DATE: case dibi::FIELD_DATE:
return $value instanceof DateTime ? $value->format("#m/d/Y#") : date("#m/d/Y#", $value); return date("#m/d/Y#", $value);
case dibi::DATETIME: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format("#m/d/Y H:i:s#") : date("#m/d/Y H:i:s#", $value); return date("#m/d/Y H:i:s#", $value);
default: default:
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
// offset support is missing // offset suppot is missing...
if ($limit >= 0) { if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')'; $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
} }
if ($offset) { if ($offset) throw new InvalidArgumentException('Offset is not implemented in driver odbc.');
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
}
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
// will return -1 with many drivers :-( // will return -1 with many drivers :-(
return odbc_num_rows($this->resultSet); return odbc_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -331,9 +295,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
return odbc_fetch_array($this->resultSet, ++$this->row); return odbc_fetch_array($this->resultSet, ++$this->row);
} else { } else {
$set = $this->resultSet; $set = $this->resultSet;
if (!odbc_fetch_row($set, ++$this->row)) { if (!odbc_fetch_row($set, ++$this->row)) return FALSE;
return FALSE;
}
$count = odbc_num_fields($set); $count = odbc_num_fields($set);
$cols = array(); $cols = array();
for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i); for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i);
@@ -342,10 +304,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
@@ -354,6 +318,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -365,38 +330,41 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = odbc_num_fields($this->resultSet); $count = odbc_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$columns[] = array( $res[] = array(
'name' => odbc_field_name($this->resultSet, $i), 'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL, 'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i), 'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype'=> odbc_field_type($this->resultSet, $i), 'nativetype'=> odbc_field_type($this->resultSet, $i),
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
} }
/********************* IDibiReflector ****************d*g**/
/********************* reflection ****************d*g**/
/** /**
@@ -405,21 +373,22 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/ */
public function getTables() public function getTables()
{ {
$res = odbc_tables($this->connection); $result = odbc_tables($this->connection);
$tables = array(); $res = array();
while ($row = odbc_fetch_array($res)) { while ($row = odbc_fetch_array($result)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') { if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = array( $res[] = array(
'name' => $row['TABLE_NAME'], 'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW', 'view' => $row['TABLE_TYPE'] === 'VIEW',
); );
} }
} }
odbc_free_result($res); odbc_free_result($result);
return $tables; return $res;
} }
/** /**
* Returns metadata for all columns in a table. * Returns metadata for all columns in a table.
* @param string * @param string
@@ -427,11 +396,11 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/ */
public function getColumns($table) public function getColumns($table)
{ {
$res = odbc_columns($this->connection); $result = odbc_columns($this->connection);
$columns = array(); $res = array();
while ($row = odbc_fetch_array($res)) { while ($row = odbc_fetch_array($result)) {
if ($row['TABLE_NAME'] === $table) { if ($row['TABLE_NAME'] === $table) {
$columns[] = array( $res[] = array(
'name' => $row['COLUMN_NAME'], 'name' => $row['COLUMN_NAME'],
'table' => $table, 'table' => $table,
'nativetype' => $row['TYPE_NAME'], 'nativetype' => $row['TYPE_NAME'],
@@ -441,11 +410,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
); );
} }
} }
odbc_free_result($res); odbc_free_result($result);
return $columns; return $res;
} }
/** /**
* Returns metadata for all indexes in a table. * Returns metadata for all indexes in a table.
* @param string * @param string
@@ -453,10 +423,11 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
/** /**
* Returns metadata for all foreign keys in a table. * Returns metadata for all foreign keys in a table.
* @param string * @param string
@@ -464,7 +435,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
} }

View File

@@ -1,72 +1,79 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* The dibi driver for Oracle database. * The dibi driver for Oracle database.
* *
* Driver options: * Connection options:
* - database => the name of the local Oracle instance or the name of the entry in tnsnames.ora * - 'database' (or 'db') - the name of the local Oracle instance or the name of the entry in tnsnames.ora
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - charset => character encoding to set * - 'charset' - character encoding to set
* - formatDate => how to format date in SQL (@see date) * - 'lazy' - if TRUE, connection will be established only when required
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector class DibiOracleDriver extends DibiObject implements IDibiDriver
{ {
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool */ /** @var bool */
private $autocommit = TRUE; private $autocommit = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('oci8')) { if (!extension_loaded('oci8')) {
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded."); throw new DibiDriverException("PHP extension 'oci8' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
$foo = & $config['charset']; DibiConnection::alias($config, 'username', 'user');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U'; DibiConnection::alias($config, 'password', 'pass');
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U'; DibiConnection::alias($config, 'database', 'db');
DibiConnection::alias($config, 'charset');
if (isset($config['resource'])) { $this->connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
$this->connection = $config['resource'];
} else {
$this->connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
}
if (!$this->connection) { if (!$this->connection) {
$err = oci_error(); $err = oci_error();
@@ -75,6 +82,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -85,70 +93,73 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
$res = oci_parse($this->connection, $sql);
if ($res) { $this->resultSet = oci_parse($this->connection, $sql);
oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); if ($this->resultSet) {
$err = oci_error($res); oci_execute($this->resultSet, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($this->resultSet);
if ($err) { if ($err) {
throw new DibiDriverException($err['message'], $err['code'], $sql); throw new DibiDriverException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
} }
} else { } else {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code'], $sql); throw new DibiDriverException($err['message'], $err['code'], $sql);
} }
return is_resource($this->resultSet) ? clone $this : NULL;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
$row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(TRUE); throw new NotSupportedException('Oracle does not support autoincrementing.');
return isset($row['ID']) ? (int) $row['ID'] : FALSE;
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->autocommit = FALSE; $this->autocommit = FALSE;
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
if (!oci_commit($this->connection)) { if (!oci_commit($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
@@ -158,13 +169,13 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
if (!oci_rollback($this->connection)) { if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
@@ -174,148 +185,106 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return mixed
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
case dibi::BINARY: case dibi::FIELD_BINARY:
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm // @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"'; $value = str_replace('"', '""', $value);
return '"' . str_replace('.', '"."', $value) . '"';
case dibi::BOOL: case dibi::FIELD_BOOL:
return $value ? 1 : 0; return $value ? 1 : 0;
case dibi::DATE: case dibi::FIELD_DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value); return date("U", $value);
case dibi::DATETIME: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value); return date("U", $value);
default: default:
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($offset > 0) { if ($limit < 0 && $offset < 1) return;
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.'); return oci_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -323,17 +292,20 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -345,38 +317,41 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = oci_num_fields($this->resultSet); $count = oci_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$columns[] = array( $res[] = array(
'name' => oci_field_name($this->resultSet, $i), 'name' => oci_field_name($this->resultSet, $i),
'table' => NULL, 'table' => NULL,
'fullname' => oci_field_name($this->resultSet, $i), 'fullname' => oci_field_name($this->resultSet, $i),
'nativetype'=> oci_field_type($this->resultSet, $i), 'nativetype'=> oci_field_type($this->resultSet, $i),
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
} }
/********************* IDibiReflector ****************d*g**/
/********************* reflection ****************d*g**/
/** /**
@@ -385,20 +360,11 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/ */
public function getTables() public function getTables()
{ {
$res = $this->query('SELECT * FROM cat'); throw new NotImplementedException;
$tables = array();
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'VIEW',
);
}
}
return $tables;
} }
/** /**
* Returns metadata for all columns in a table. * Returns metadata for all columns in a table.
* @param string * @param string
@@ -406,10 +372,11 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/ */
public function getColumns($table) public function getColumns($table)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
/** /**
* Returns metadata for all indexes in a table. * Returns metadata for all indexes in a table.
* @param string * @param string
@@ -417,10 +384,11 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
/** /**
* Returns metadata for all foreign keys in a table. * Returns metadata for all foreign keys in a table.
* @param string * @param string
@@ -428,7 +396,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
} }

View File

@@ -1,68 +1,82 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
require_once dirname(__FILE__) . '/mysql.reflector.php';
require_once dirname(__FILE__) . '/sqlite.reflector.php';
/** /**
* The dibi driver for PDO. * The dibi driver for PDO.
* *
* Driver options: * Connection options:
* - dsn => driver specific DSN * - 'dsn' - driver specific DSN
* - username (or user) * - 'username' (or 'user')
* - password (or pass) * - 'password' (or 'pass')
* - options (array) => driver specific options {@see PDO::__construct} * - 'options' - driver specific options array
* - resource (PDO) => existing connection * - 'pdo' - PDO object (optional)
* - lazy, profiler, result, substitutes, ... => see DibiConnection options * - 'lazy' - if TRUE, connection will be established only when required
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver class DibiPdoDriver extends DibiObject implements IDibiDriver
{ {
/** @var PDO Connection resource */ /** @var PDO Connection resource */
private $connection; private $connection;
/** @var PDOStatement Resultset resource */ /** @var PDOStatement Resultset resource */
private $resultSet; private $resultSet;
/** @var int|FALSE Affected rows */ /** @var int|FALSE Affected rows */
private $affectedRows = FALSE; private $affectedRows = FALSE;
/** @var string */
private $driverName;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('pdo')) { if (!extension_loaded('pdo')) {
throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded."); throw new DibiDriverException("PHP extension 'pdo' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
$foo = & $config['dsn']; DibiConnection::alias($config, 'username', 'user');
$foo = & $config['options']; DibiConnection::alias($config, 'password', 'pass');
DibiConnection::alias($config, 'resource', 'pdo'); DibiConnection::alias($config, 'dsn');
DibiConnection::alias($config, 'pdo');
DibiConnection::alias($config, 'options');
if ($config['resource'] instanceof PDO) { if ($config['pdo'] instanceof PDO) {
$this->connection = $config['resource']; $this->connection = $config['pdo'];
} else try { } else try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']); $this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
@@ -74,11 +88,10 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
if (!$this->connection) { if (!$this->connection) {
throw new DibiDriverException('Connecting error.'); throw new DibiDriverException('Connecting error.');
} }
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -89,20 +102,21 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
// must detect if SQL returns result set or num of affected rows // must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6)); $cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1); $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
$this->affectedRows = FALSE;
if (isset($list[$cmd])) { if (isset($list[$cmd])) {
$this->resultSet = NULL;
$this->affectedRows = $this->connection->exec($sql); $this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows === FALSE) { if ($this->affectedRows === FALSE) {
@@ -110,46 +124,51 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql); throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} }
} else { return NULL;
$res = $this->connection->query($sql);
if ($res === FALSE) { } else {
$this->resultSet = $this->connection->query($sql);
$this->affectedRows = FALSE;
if ($this->resultSet === FALSE) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql); throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} else {
return $this->createResultDriver($res);
} }
return clone $this;
} }
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return $this->affectedRows; return $this->affectedRows;
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
return $this->connection->lastInsertId(); return $this->connection->lastInsertId();
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
if (!$this->connection->beginTransaction()) { if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -158,13 +177,13 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
if (!$this->connection->commit()) { if (!$this->connection->commit()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -173,13 +192,13 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
if (!$this->connection->rollBack()) { if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
@@ -188,6 +207,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return PDO * @return PDO
@@ -198,191 +218,118 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new DibiMySqlReflector($this);
case 'sqlite':
case 'sqlite2':
return new DibiSqliteReflector($this);
default:
throw new DibiNotSupportedException;
}
}
/**
* Result set driver factory.
* @param PDOStatement
* @return IDibiResultDriver
*/
public function createResultDriver(PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
return $this->connection->quote($value, PDO::PARAM_STR); return $this->connection->quote($value, PDO::PARAM_STR);
case dibi::BINARY: case dibi::FIELD_BINARY:
return $this->connection->quote($value, PDO::PARAM_LOB); return $this->connection->quote($value, PDO::PARAM_LOB);
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
switch ($this->driverName) { switch ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME)) {
case 'mysql': case 'mysql':
return '`' . str_replace('`', '``', $value) . '`'; $value = str_replace('`', '``', $value);
return '`' . str_replace('.', '`.`', $value) . '`';
case 'oci': case 'pgsql':
case 'pgsql': $a = strrpos($value, '.');
return '"' . str_replace('"', '""', $value) . '"'; if ($a === FALSE) {
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite': } else {
case 'sqlite2': return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"';
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
default:
return $value;
} }
case dibi::BOOL: case 'sqlite':
return $this->connection->quote($value, PDO::PARAM_BOOL); case 'sqlite2':
$value = strtr($value, '[]', ' ');
case dibi::DATE: case 'odbc':
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value); case 'oci': // TODO: not tested
case 'mssql':
case dibi::DATETIME: $value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value); return '[' . str_replace('.', '].[', $value) . ']';
default: default:
throw new InvalidArgumentException('Unsupported type.'); return $value;
}
case dibi::FIELD_BOOL:
return $this->connection->quote($value, PDO::PARAM_BOOL);
case dibi::FIELD_DATE:
return date("'Y-m-d'", $value);
case dibi::FIELD_DATETIME:
return date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new DibiNotImplementedException;
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit < 0 && $offset < 1) { throw new NotSupportedException('PDO does not support applying limit or offset.');
return;
}
switch ($this->driverName) {
case 'mysql':
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'pgsql':
if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
case 'sqlite2':
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'oci':
if ($offset > 0) {
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'odbc':
case 'mssql':
if ($offset < 1) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
break;
}
// intentionally break omitted
default:
throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
}
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
return $this->resultSet->rowCount(); throw new DibiDriverException('Row count is not available for unbuffered queries.');
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -390,17 +337,20 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiDriverException('Cannot seek an unbuffered result set.');
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -411,28 +361,22 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
* @throws DibiException * @throws DibiException
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = $this->resultSet->columnCount(); $count = $this->resultSet->columnCount();
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @ $row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) { if ($row === FALSE) {
throw new DibiNotSupportedException('Driver does not support meta data.'); throw new DibiDriverException('Driver does not support meta data.');
} }
// PHP < 5.2.3 compatibility $res[] = array(
// @see: http://php.net/manual/en/pdostatement.getcolumnmeta.php#pdostatement.getcolumnmeta.changelog
$row = $row + array(
'table' => NULL,
'native_type' => 'VAR_STRING',
);
$columns[] = array(
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['table'], 'table' => $row['table'],
'nativetype' => $row['native_type'], 'nativetype' => $row['native_type'],
@@ -440,10 +384,11 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
'vendor' => $row, 'vendor' => $row,
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return PDOStatement * @return PDOStatement
@@ -453,4 +398,55 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
return $this->resultSet; return $this->resultSet;
} }
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
throw new NotImplementedException;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
}
} }

View File

@@ -1,89 +1,91 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* The dibi driver for PostgreSQL database. * The dibi driver for PostgreSQL database.
* *
* Driver options: * Connection options:
* - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API * - 'host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service' - see PostgreSQL API
* - string => or use connection string * - 'string' - or use connection string
* - schema => the schema search path * - 'persistent' - try to find a persistent link?
* - charset => character encoding to set (default is utf8) * - 'charset' - character encoding to set
* - persistent (bool) => try to find a persistent link? * - 'schema' - the schema search path
* - resource (resource) => existing connection resource * - 'lazy' - if TRUE, connection will be established only when required
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector class DibiPostgreDriver extends DibiObject implements IDibiDriver
{ {
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var bool Escape method */ /** @var bool Escape method */
private $escMethod = FALSE; private $escMethod = FALSE;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('pgsql')) { if (!extension_loaded('pgsql')) {
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded."); throw new DibiDriverException("PHP extension 'pgsql' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
if (isset($config['resource'])) { if (isset($config['string'])) {
$this->connection = $config['resource']; $string = $config['string'];
} else { } else {
if (!isset($config['charset'])) $config['charset'] = 'utf8'; $string = '';
if (isset($config['string'])) { foreach (array('host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service') as $key) {
$string = $config['string']; if (isset($config[$key])) $string .= $key . '=' . $config[$key] . ' ';
} else {
$string = '';
DibiConnection::alias($config, 'user', 'username');
DibiConnection::alias($config, 'dbname', 'database');
foreach (array('host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service') as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
} }
}
DibiDriverException::tryError(); DibiDriverException::tryError();
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW); $this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else { } else {
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW); $this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
} }
if (DibiDriverException::catchError($msg)) { if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0); throw new DibiDriverException($msg, 0);
}
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
@@ -99,13 +101,14 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
} }
if (isset($config['schema'])) { if (isset($config['schema'])) {
$this->query('SET search_path TO "' . $config['schema'] . '"'); $this->query('SET search_path TO ' . $config['schema']);
} }
$this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>='); $this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>=');
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -116,106 +119,95 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @param bool update affected rows?
* @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
$this->affectedRows = FALSE; $this->resultSet = @pg_query($this->connection, $sql); // intentionally @
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($this->resultSet === FALSE) {
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql); throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
if (pg_num_fields($res)) {
return $this->createResultDriver($res);
}
} }
return is_resource($this->resultSet) && pg_num_fields($this->resultSet) ? clone $this : NULL;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return $this->affectedRows; return pg_affected_rows($this->resultSet);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
if ($sequence === NULL) { if ($sequence === NULL) {
// PostgreSQL 8.1 is needed // PostgreSQL 8.1 is needed
$res = $this->query("SELECT LASTVAL()"); $has = $this->query("SELECT LASTVAL()");
} else { } else {
$res = $this->query("SELECT CURRVAL('$sequence')"); $has = $this->query("SELECT CURRVAL('$sequence')");
} }
if (!$res) { if (!$has) return FALSE;
return FALSE;
}
$row = $res->fetch(FALSE); $row = $this->fetch(FALSE);
$this->free();
return is_array($row) ? $row[0] : FALSE; return is_array($row) ? $row[0] : FALSE;
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION'); $this->query('START TRANSACTION');
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT'); $this->query('COMMIT');
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK'); $this->query('ROLLBACK');
} }
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
}
/** /**
* Returns the connection resource. * Returns the connection resource.
@@ -223,163 +215,123 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
if ($this->escMethod) { if ($this->escMethod) {
if (!is_resource($this->connection)) { return "'" . pg_escape_string($this->connection, $value) . "'";
throw new DibiException('Lost connection to server.'); } else {
} return "'" . pg_escape_string($value) . "'";
return "'" . pg_escape_string($this->connection, $value) . "'"; }
} else {
return "'" . pg_escape_string($value) . "'";
}
case dibi::BINARY: case dibi::FIELD_BINARY:
if ($this->escMethod) { if ($this->escMethod) {
if (!is_resource($this->connection)) { return "'" . pg_escape_bytea($this->connection, $value) . "'";
throw new DibiException('Lost connection to server.'); } else {
} return "'" . pg_escape_bytea($value) . "'";
return "'" . pg_escape_bytea($this->connection, $value) . "'"; }
} else {
return "'" . pg_escape_bytea($value) . "'";
}
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS // @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
$a = strrpos($value, '.');
if ($a === FALSE) {
return '"' . str_replace('"', '""', $value) . '"'; return '"' . str_replace('"', '""', $value) . '"';
} else {
// table.col delimite as table."col"
return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"';
}
case dibi::BOOL: case dibi::FIELD_BOOL:
return $value ? 'TRUE' : 'FALSE'; return $value ? 'TRUE' : 'FALSE';
case dibi::DATE: case dibi::FIELD_DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value); return date("'Y-m-d'", $value);
case dibi::DATETIME: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value); return date("'Y-m-d H:i:s'", $value);
default: default:
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
if ($this->escMethod) {
$value = pg_escape_string($this->connection, $value);
} else {
$value = pg_escape_string($value);
}
$value = strtr($value, array( '%' => '\\\\%', '_' => '\\\\_'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) { switch ($type) {
case dibi::FIELD_BINARY:
return pg_unescape_bytea($value); return pg_unescape_bytea($value);
default:
throw new InvalidArgumentException('Unsupported type.');
} }
throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit >= 0) { if ($limit >= 0)
$sql .= ' LIMIT ' . (int) $limit; $sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) { if ($offset > 0)
$sql .= ' OFFSET ' . (int) $offset; $sql .= ' OFFSET ' . (int) $offset;
}
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
return pg_num_rows($this->resultSet); return pg_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
@@ -387,10 +339,12 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record * @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
@@ -398,6 +352,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -409,15 +364,16 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$hasTable = version_compare(PHP_VERSION , '5.2.0', '>='); $hasTable = version_compare(PHP_VERSION , '5.2.0', '>=');
$count = pg_num_fields($this->resultSet); $count = pg_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = array( $row = array(
'name' => pg_field_name($this->resultSet, $i), 'name' => pg_field_name($this->resultSet, $i),
@@ -425,24 +381,26 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
'nativetype'=> pg_field_type($this->resultSet, $i), 'nativetype'=> pg_field_type($this->resultSet, $i),
); );
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name']; $row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$columns[] = $row; $res[] = $row;
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
$this->autoFree = FALSE; return $this->resultSet;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
} }
/********************* IDibiReflector ****************d*g**/
/********************* reflection ****************d*g**/
/** /**
@@ -451,28 +409,23 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/ */
public function getTables() public function getTables()
{ {
$version = pg_parameter_status($this->resource, 'server_version'); $version = pg_version($this->connection);
if ($version < 7.4) { if ($version['server'] < 8) {
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.'); throw new NotSupportedException('Reflection requires PostgreSQL 8.');
} }
$res = $this->query(" $this->query("
SELECT SELECT table_name as name, CAST(table_type = 'VIEW' AS INTEGER) as view
table_name AS name, FROM information_schema.tables
CASE table_type WHERE table_schema = current_schema()
WHEN 'VIEW' THEN 1
ELSE 0
END AS view
FROM
information_schema.tables
WHERE
table_schema = current_schema()
"); ");
$tables = pg_fetch_all($res->resultSet); $res = pg_fetch_all($this->resultSet);
return $tables ? $tables : array(); $this->free();
return $res;
} }
/** /**
* Returns metadata for all columns in a table. * Returns metadata for all columns in a table.
* @param string * @param string
@@ -480,25 +433,25 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/ */
public function getColumns($table) public function getColumns($table)
{ {
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT); $_table = $this->escape($table, dibi::FIELD_TEXT);
$res = $this->query(" $this->query("
SELECT indkey SELECT indkey
FROM pg_class FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
WHERE pg_class.relname = $_table WHERE pg_class.relname = $_table
"); ");
$primary = (int) pg_fetch_object($res->resultSet)->indkey; $primary = (int) pg_fetch_object($this->resultSet)->indkey;
$res = $this->query(" $this->query("
SELECT * SELECT *
FROM information_schema.columns FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema() WHERE table_name = $_table AND table_schema = current_schema()
ORDER BY ordinal_position ORDER BY ordinal_position
"); ");
$columns = array(); $res = array();
while ($row = $res->fetch(TRUE)) { while ($row = $this->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']); $size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = array( $res[] = array(
'name' => $row['column_name'], 'name' => $row['column_name'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($row['udt_name']), 'nativetype' => strtoupper($row['udt_name']),
@@ -509,10 +462,12 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
'vendor' => $row, 'vendor' => $row,
); );
} }
return $columns; $this->free();
return $res;
} }
/** /**
* Returns metadata for all indexes in a table. * Returns metadata for all indexes in a table.
* @param string * @param string
@@ -520,8 +475,8 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT); $_table = $this->escape($table, dibi::FIELD_TEXT);
$res = $this->query(" $this->query("
SELECT ordinal_position, column_name SELECT ordinal_position, column_name
FROM information_schema.columns FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema() WHERE table_name = $_table AND table_schema = current_schema()
@@ -529,11 +484,11 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
"); ");
$columns = array(); $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $this->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name']; $columns[$row['ordinal_position']] = $row['column_name'];
} }
$res = $this->query(" $this->query("
SELECT pg_class2.relname, indisunique, indisprimary, indkey SELECT pg_class2.relname, indisunique, indisprimary, indkey
FROM pg_class FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
@@ -541,19 +496,21 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
WHERE pg_class.relname = $_table WHERE pg_class.relname = $_table
"); ");
$indexes = array(); $res = array();
while ($row = $res->fetch(TRUE)) { while ($row = $this->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname']; $res[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't'; $res[$row['relname']]['unique'] = $row['indisunique'] === 't';
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't'; $res[$row['relname']]['primary'] = $row['indisprimary'] === 't';
foreach (explode(' ', $row['indkey']) as $index) { foreach (explode(' ', $row['indkey']) as $index) {
$indexes[$row['relname']]['columns'][] = $columns[$index]; $res[$row['relname']]['columns'][] = $columns[$index];
} }
} }
return array_values($indexes); $this->free();
return array_values($res);
} }
/** /**
* Returns metadata for all foreign keys in a table. * Returns metadata for all foreign keys in a table.
* @param string * @param string
@@ -561,72 +518,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT); throw new NotImplementedException;
$res = $this->query("
SELECT
c.conname AS name,
lt.attname AS local,
c.confrelid::regclass AS table,
ft.attname AS foreign,
CASE c.confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onUpdate\",
CASE c.confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onDelete\",
c.conkey,
lt.attnum AS lnum,
c.confkey,
ft.attnum AS fnum
FROM
pg_constraint c
JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey)
JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey)
WHERE
c.contype = 'f'
AND
c.conrelid = $_table::regclass
");
$fKeys = $references = array();
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = array(
'name' => $row['name'],
'table' => $row['table'],
'local' => array(),
'foreign' => array(),
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
);
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));
$references[$row['name']] = array_combine($l, $f);
}
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
$fKeys[$row['name']]['local'][] = $row['local'];
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
}
}
return $fKeys;
} }
} }

View File

@@ -1,75 +1,84 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
require_once dirname(__FILE__) . '/sqlite.reflector.php';
/** /**
* The dibi driver for SQLite database. * The dibi driver for SQLite database.
* *
* Driver options: * Connection options:
* - database (or file) => the filename of the SQLite database * - 'database' (or 'file') - the filename of the SQLite database
* - persistent (bool) => try to find a persistent link? * - 'persistent' - try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically? * - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - formatDate => how to format date in SQL (@see date) * - 'lazy' - if TRUE, connection will be established only when required
* - formatDateTime => how to format datetime in SQL (@see date) * - 'formatDate' - how to format date in SQL (@see date)
* - dbcharset => database character encoding (will be converted to 'charset') * - 'formatDateTime' - how to format datetime in SQL (@see date)
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
* *
* @author David Grudl * @author David Grudl
* @package dibi\drivers * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/ */
class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDriver class DibiSqliteDriver extends DibiObject implements IDibiDriver
{ {
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
/** @var resource Resultset resource */ /** @var resource Resultset resource */
private $resultSet; private $resultSet;
/** @var bool Is buffered (seekable and countable)? */ /** @var bool Is buffered (seekable and countable)? */
private $buffered; private $buffered;
/** @var string Date and datetime format */ /** @var string Date and datetime format */
private $fmtDate, $fmtDateTime; private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/** /**
* @throws DibiNotSupportedException * @throws DibiException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('sqlite')) { if (!extension_loaded('sqlite')) {
throw new DibiNotSupportedException("PHP extension 'sqlite' is not loaded."); throw new DibiDriverException("PHP extension 'sqlite' is not loaded.");
} }
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array &$config)
{ {
DibiConnection::alias($config, 'database', 'file'); DibiConnection::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U'; $this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U'; $this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
$errorMsg = ''; $errorMsg = '';
if (isset($config['resource'])) { if (empty($config['persistent'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @ $this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @
} else { } else {
$this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @ $this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @
@@ -80,15 +89,10 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
$this->buffered = empty($config['unbuffered']); $this->buffered = empty($config['unbuffered']);
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL;
}
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
@@ -99,228 +103,195 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
if ($this->dbcharset !== NULL) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
}
DibiDriverException::tryError(); DibiDriverException::tryError();
if ($this->buffered) { if ($this->buffered) {
$res = sqlite_query($this->connection, $sql); $this->resultSet = sqlite_query($this->connection, $sql);
} else { } else {
$res = sqlite_unbuffered_query($this->connection, $sql); $this->resultSet = sqlite_unbuffered_query($this->connection, $sql);
} }
if (DibiDriverException::catchError($msg)) { if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql); throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
} }
return is_resource($this->resultSet) ? clone $this : NULL;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
public function getAffectedRows() public function affectedRows()
{ {
return sqlite_changes($this->connection); return sqlite_changes($this->connection);
} }
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
public function getInsertId($sequence) public function insertId($sequence)
{ {
return sqlite_last_insert_rowid($this->connection); return sqlite_last_insert_rowid($this->connection);
} }
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->query('BEGIN'); $this->query('BEGIN');
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->query('COMMIT'); $this->query('COMMIT');
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->query('ROLLBACK'); $this->query('ROLLBACK');
} }
/** /**
* Returns the connection resource. * Returns the connection resource.
* @return mixed * @return mixed
*/ */
public function getResource() public function getResource()
{ {
return is_resource($this->connection) ? $this->connection : NULL; return $this->connection;
} }
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/ /********************* SQL ****************d*g**/
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function escape($value, $type) public function escape($value, $type)
{ {
switch ($type) { switch ($type) {
case dibi::TEXT: case dibi::FIELD_TEXT:
case dibi::BINARY: case dibi::FIELD_BINARY:
return "'" . sqlite_escape_string($value) . "'"; return "'" . sqlite_escape_string($value) . "'";
case dibi::IDENTIFIER: case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']'; return '[' . str_replace('.', '].[', strtr($value, '[]', ' ')) . ']';
case dibi::BOOL: case dibi::FIELD_BOOL:
return $value ? 1 : 0; return $value ? 1 : 0;
case dibi::DATE: case dibi::FIELD_DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value); return date($this->fmtDate, $value);
case dibi::DATETIME: case dibi::FIELD_DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value); return date($this->fmtDateTime, $value);
default: default:
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
} }
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new DibiNotSupportedException;
}
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string value * @param string value
* @param string type (dibi::BINARY) * @param string type (dibi::FIELD_BINARY)
* @return string decoded value * @return string decoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
public function unescape($value, $type) public function unescape($value, $type)
{ {
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.'); throw new InvalidArgumentException('Unsupported type.');
} }
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(&$sql, $limit, $offset)
{ {
if ($limit >= 0 || $offset > 0) { if ($limit < 0 && $offset < 1) return;
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : ''); $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
} }
/********************* result set ****************d*g**/ /********************* result set ****************d*g**/
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
public function getRowCount() public function rowCount()
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiDriverException('Row count is not available for unbuffered queries.');
} }
return sqlite_num_rows($this->resultSet); return sqlite_num_rows($this->resultSet);
} }
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record * @return array array on success, nonarray if no next record
* @internal
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
$row = sqlite_fetch_array($this->resultSet, $assoc ? SQLITE_ASSOC : SQLITE_NUM); $row = sqlite_fetch_array($this->resultSet, $assoc ? SQLITE_ASSOC : SQLITE_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT'; if ($assoc && $row) {
if ($row && ($assoc || $charset)) {
$tmp = array(); $tmp = array();
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(array('[', ']'), '', $k)] = $v; $tmp[str_replace(array('[', ']'), '', $k)] = $v;
} }
return $tmp; return $tmp;
@@ -329,6 +300,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to * @param int the 0-based cursor pos to seek to
@@ -338,12 +310,13 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function seek($row) public function seek($row)
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiDriverException('Cannot seek an unbuffered result set.');
} }
return sqlite_seek($this->resultSet, $row); return sqlite_seek($this->resultSet, $row);
} }
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @return void * @return void
@@ -354,65 +327,96 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} }
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
*/ */
public function getResultColumns() public function getColumnsMeta()
{ {
$count = sqlite_num_fields($this->resultSet); $count = sqlite_num_fields($this->resultSet);
$columns = array(); $res = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i)); $name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i));
$pair = explode('.', $name); $pair = explode('.', $name);
$columns[] = array( $res[] = array(
'name' => isset($pair[1]) ? $pair[1] : $pair[0], 'name' => isset($pair[1]) ? $pair[1] : $pair[0],
'table' => isset($pair[1]) ? $pair[0] : NULL, 'table' => isset($pair[1]) ? $pair[0] : NULL,
'fullname' => $name, 'fullname' => $name,
'nativetype' => NULL, 'nativetype' => NULL,
); );
} }
return $columns; return $res;
} }
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return mixed * @return mixed
*/ */
public function getResultResource() public function getResultResource()
{ {
return is_resource($this->resultSet) ? $this->resultSet : NULL; return $this->resultSet;
} }
/********************* user defined functions ****************d*g**/
/********************* reflection ****************d*g**/
/** /**
* Registers an user defined function for use in SQL statements. * Returns list of tables.
* @param string function name * @return array
* @param mixed callback
* @param int num of arguments
* @return void
*/ */
public function registerFunction($name, $callback, $numArgs = -1) public function getTables()
{ {
sqlite_create_function($this->connection, $name, $callback, $numArgs); $this->query("
SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
UNION ALL
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$res = sqlite_fetch_all($this->resultSet, SQLITE_ASSOC);
$this->free();
return $res;
} }
/** /**
* Registers an aggregating user defined function for use in SQL statements. * Returns metadata for all columns in a table.
* @param string function name * @param string
* @param mixed callback called for each row of the result set * @return array
* @param mixed callback called to aggregate the "stepped" data from each row
* @param int num of arguments
* @return void
*/ */
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1) public function getColumns($table)
{ {
sqlite_create_aggregate($this->connection, $name, $rowCallback, $agrCallback, $numArgs); throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
} }
} }

View File

@@ -1,161 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* The dibi reflector for SQLite database.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class DibiSqliteReflector extends DibiObject implements IDibiReflector
{
/** @var IDibiDriver */
private $driver;
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query("
SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
UNION ALL
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = array();
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$meta = $this->driver->query("
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
UNION ALL
SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
")->fetch(TRUE);
$res = $this->driver->query("PRAGMA table_info({$this->driver->escape($table, dibi::IDENTIFIER)})");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$column = $row['name'];
$pattern = "/(\"$column\"|\[$column\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui";
$type = explode('(', $row['type']);
$columns[] = array(
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escape($index, dibi::IDENTIFIER)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
}
$columns = $this->getColumns($table);
foreach ($indexes as $index => $values) {
$column = $indexes[$index]['columns'][0];
$primary = FALSE;
foreach ($columns as $info) {
if ($column == $info['name']) {
$primary = $info['vendor']['pk'];
break;
}
}
$indexes[$index]['primary'] = (bool) $primary;
}
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = array(
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => array($column['name']),
);
break;
}
}
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
if (!($this->driver instanceof DibiSqlite3Driver)) {
// throw new DibiNotSupportedException; // @see http://www.sqlite.org/foreignkeys.html
}
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$keys = array();
while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns
$keys[$row['id']]['table'] = $row['table']; // referenced table
$keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns
$keys[$row['id']]['onDelete'] = $row['on_delete'];
$keys[$row['id']]['onUpdate'] = $row['on_update'];
if ($keys[$row['id']]['foreign'][0] == NULL) {
$keys[$row['id']]['foreign'] = NULL;
}
}
return array_values($keys);
}
}

View File

@@ -1,420 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/sqlite.reflector.php';
/**
* The dibi driver for SQLite3 database.
*
* Driver options:
* - database (or file) => the filename of the SQLite3 database
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var SQLite3 Connection resource */
private $connection;
/** @var SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new DibiNotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
DibiConnection::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else try {
$this->connection = new SQLite3($config['database']);
} catch (Exception $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
}
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL;
}
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query("PRAGMA foreign_keys = ON");
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection->close();
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
if ($this->dbcharset !== NULL) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
}
$res = @$this->connection->query($sql); // intentionally @
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
} elseif ($res instanceof SQLite3Result) {
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->connection->changes();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertRowID();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param SQLite3Result
* @return IDibiResultDriver
*/
public function createResultDriver(SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
return "'" . $this->connection->escapeString($value) . "'";
case dibi::BINARY:
return "X'" . bin2hex((string) $value) . "'";
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit >= 0 || $offset > 0) {
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->resultSet && @$this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
* @throws DibiNotSupportedException
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = array();
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
}
return $tmp;
}
return $row;
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiNotSupportedException
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet->finalize();
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = array();
static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
for ($i = 0; $i < $count; $i++) {
$columns[] = array(
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
* @param string function name
* @param mixed callback
* @param int num of arguments
* @return void
*/
public function registerFunction($name, $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
* @param string function name
* @param mixed callback called for each row of the result set
* @param mixed callback called to aggregate the "stepped" data from each row
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -1,89 +1,79 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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 connection. * dibi connection.
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*
* @property-read bool $connected
* @property-read mixed $config
* @property-read IDibiDriver $driver
* @property-read int $affectedRows
* @property-read int $insertId
* @property-read DibiDatabaseInfo $databaseInfo
*/ */
class DibiConnection extends DibiObject class DibiConnection extends DibiObject
{ {
/** @var array of function(DibiEvent $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */ /** @var array Current connection configuration */
private $config; private $config;
/** @var IDibiDriver */ /** @var IDibiDriver Driver */
private $driver; private $driver;
/** @var DibiTranslator */ /** @var IDibiProfiler Profiler */
private $translator; private $profiler;
/** @var bool Is connected? */ /** @var bool Is connected? */
private $connected = FALSE; private $connected = FALSE;
/** @var DibiHashMap Substitutes for identifiers */ /** @var bool Is in transaction? */
private $substitutes; private $inTxn = FALSE;
/** /**
* Connection options: (see driver-specific options too) * Creates object and (optionally) connects to a database.
* - lazy (bool) => if TRUE, connection will be established only when required * @param array|string|ArrayObject connection parameters
* - result (array) => result set options * @param string connection name
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
* - profiler (array or bool)
* - run (bool) => enable profiler?
* - file => file to log
* - substitutes (array) => map of driver specific substitutes (under development)
* @param mixed connection parameters
* @param string connection name
* @throws DibiException * @throws DibiException
*/ */
public function __construct($config, $name = NULL) public function __construct($config, $name = NULL)
{ {
class_exists('dibi'); // ensure class dibi is loaded if (class_exists(/*Nette::*/'Debug', FALSE)) {
/*Nette::*/Debug::addColophon(array('dibi', 'getColophon'));
}
// DSN string // DSN string
if (is_string($config)) { if (is_string($config)) {
parse_str($config, $config); parse_str($config, $config);
} elseif ($config instanceof Traversable) { } elseif ($config instanceof ArrayObject) {
$tmp = array(); $config = (array) $config;
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} elseif (!is_array($config)) { } elseif (!is_array($config)) {
throw new InvalidArgumentException('Configuration must be array, string or object.'); throw new InvalidArgumentException('Configuration must be array, string or ArrayObject.');
} }
self::alias($config, 'username', 'user');
self::alias($config, 'password', 'pass');
self::alias($config, 'host', 'hostname');
self::alias($config, 'result|formatDate', 'resultDate');
self::alias($config, 'result|formatDateTime', 'resultDateTime');
if (!isset($config['driver'])) { if (!isset($config['driver'])) {
$config['driver'] = dibi::$defaultDriver; $config['driver'] = dibi::$defaultDriver;
} }
$driver = preg_replace('#[^a-z0-9_]#', '_', strtolower($config['driver'])); $driver = preg_replace('#[^a-z0-9_]#', '_', $config['driver']);
$class = "Dibi" . $driver . "Driver"; $class = "Dibi" . $driver . "Driver";
if (!class_exists($class, FALSE)) { if (!class_exists($class, FALSE)) {
include_once dirname(__FILE__) . "/../drivers/$driver.php"; include_once dirname(__FILE__) . "/../drivers/$driver.php";
@@ -96,35 +86,16 @@ class DibiConnection extends DibiObject
$config['name'] = $name; $config['name'] = $name;
$this->config = $config; $this->config = $config;
$this->driver = new $class; $this->driver = new $class;
$this->translator = new DibiTranslator($this);
// profiler if (!empty($config['profiler'])) {
$profilerCfg = & $config['profiler']; $class = $config['profiler'];
if (is_scalar($profilerCfg)) { if (is_numeric($class) || is_bool($class)) {
$profilerCfg = array('run' => (bool) $profilerCfg); $class = 'DibiProfiler';
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
} }
if (!class_exists($class)) {
if (DibiFirePhpLogger::isAvailable()) { throw new DibiException("Unable to create instance of dibi profiler '$class'.");
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
}
if (class_exists('DibiNettePanel', FALSE)) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
}
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
} }
$this->setProfiler(new $class);
} }
if (empty($config['lazy'])) { if (empty($config['lazy'])) {
@@ -133,6 +104,7 @@ class DibiConnection extends DibiObject
} }
/** /**
* Automatically frees the resources allocated for this result set. * Automatically frees the resources allocated for this result set.
* @return void * @return void
@@ -140,40 +112,48 @@ class DibiConnection extends DibiObject
public function __destruct() public function __destruct()
{ {
// disconnects and rolls back transaction - do not rely on auto-disconnect and rollback! // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
$this->connected && $this->driver->getResource() && $this->disconnect(); $this->disconnect();
} }
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
*/ */
final public function connect() final protected function connect()
{ {
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL; if (!$this->connected) {
try { if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::CONNECT);
}
$this->driver->connect($this->config); $this->driver->connect($this->config);
$this->connected = TRUE; $this->connected = TRUE;
$event && $this->onEvent($event->done()); if (isset($ticket)) {
$this->profiler->after($ticket);
} catch (DibiException $e) { }
$event && $this->onEvent($event->done($e));
throw $e;
} }
} }
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
*/ */
final public function disconnect() final public function disconnect()
{ {
$this->driver->disconnect(); if ($this->connected) {
$this->connected = FALSE; if ($this->inTxn) {
$this->rollback();
}
$this->driver->disconnect();
$this->connected = FALSE;
}
} }
/** /**
* Returns TRUE when connection was established. * Returns TRUE when connection was established.
* @return bool * @return bool
@@ -184,6 +164,7 @@ class DibiConnection extends DibiObject
} }
/** /**
* Returns configuration variable. If no $key is passed, returns the entire array. * Returns configuration variable. If no $key is passed, returns the entire array.
* @see self::__construct * @see self::__construct
@@ -205,6 +186,7 @@ class DibiConnection extends DibiObject
} }
/** /**
* Apply configuration alias or default values. * Apply configuration alias or default values.
* @param array connect configuration * @param array connect configuration
@@ -212,56 +194,50 @@ class DibiConnection extends DibiObject
* @param string alias key * @param string alias key
* @return void * @return void
*/ */
public static function alias(& $config, $key, $alias) public static function alias(&$config, $key, $alias=NULL)
{ {
$foo = & $config; if (isset($config[$key])) return;
foreach (explode('|', $key) as $key) {
$foo = & $foo[$key];
}
if (!isset($foo) && isset($config[$alias])) { if ($alias !== NULL && isset($config[$alias])) {
$foo = $config[$alias]; $config[$key] = $config[$alias];
unset($config[$alias]); unset($config[$alias]);
} else {
$config[$key] = NULL;
} }
} }
/** /**
* Returns the driver and connects to a database in lazy mode. * Returns the connection resource.
* @return IDibiDriver * @return resource
*/ */
final public function getDriver() final public function getResource()
{ {
$this->connected || $this->connect(); return $this->driver->getResource();
return $this->driver;
} }
/** /**
* Generates (translates) and executes SQL query. * Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any) * @return DibiResult|NULL result set object (if any)
* @throws DibiException * @throws DibiException
*/ */
final public function query($args) final public function query($args)
{ {
$args = func_get_args(); $args = func_get_args();
return $this->nativeQuery($this->translateArgs($args)); $this->connect();
$trans = new DibiTranslator($this->driver);
if ($trans->translate($args)) {
return $this->nativeQuery($trans->sql);
} else {
throw new DibiException('SQL translate error: ' . $trans->sql);
}
} }
/**
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
final public function translate($args)
{
$args = func_get_args();
return $this->translateArgs($args);
}
/** /**
* Generates and prints SQL query. * Generates and prints SQL query.
@@ -271,103 +247,71 @@ class DibiConnection extends DibiObject
final public function test($args) final public function test($args)
{ {
$args = func_get_args(); $args = func_get_args();
try { $this->connect();
dibi::dump($this->translateArgs($args)); $trans = new DibiTranslator($this->driver);
return TRUE; $ok = $trans->translate($args);
dibi::dump($trans->sql);
} catch (DibiException $e) { return $ok;
if ($e->getSql()) {
dibi::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
return FALSE;
}
} }
/**
* Generates (translates) and returns SQL query as DibiDataSource.
* @param array|mixed one or more arguments
* @return DibiDataSource
* @throws DibiException
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DibiDataSource($this->translateArgs($args), $this);
}
/**
* Generates SQL query.
* @param array
* @return string
*/
private function translateArgs($args)
{
$this->connected || $this->connect();
return $this->translator->translate($args);
}
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return DibiResult|int result set object (if any) * @return DibiResult|NULL result set object (if any)
* @throws DibiException * @throws DibiException
*/ */
final public function nativeQuery($sql) final public function nativeQuery($sql)
{ {
$this->connected || $this->connect(); $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::$sql = $sql;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL; dibi::$elapsedTime = FALSE;
try { $time = -microtime(TRUE);
$res = $this->driver->query($sql);
} catch (DibiException $e) { if ($res = $this->driver->query($sql)) { // intentionally =
$event && $this->onEvent($event->done($e)); $res = new DibiResult($res, $this->config);
throw $e;
} }
if ($res) { $time += microtime(TRUE);
$res = $this->createResultSet($res); dibi::$elapsedTime = $time;
} else { dibi::$totalTime += $time;
$res = $this->driver->getAffectedRows();
}
$event && $this->onEvent($event->done($res)); if (isset($ticket)) {
$this->profiler->after($ticket, $res);
}
return $res; return $res;
} }
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows * @return int number of rows
* @throws DibiException * @throws DibiException
*/ */
public function getAffectedRows() public function affectedRows()
{ {
$this->connected || $this->connect(); $rows = $this->driver->affectedRows();
$rows = $this->driver->getAffectedRows(); if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.');
if (!is_int($rows) || $rows < 0) {
throw new DibiException('Cannot retrieve number of affected rows.');
}
return $rows; return $rows;
} }
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws DibiException
*/
public function affectedRows()
{
return $this->getAffectedRows();
}
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
@@ -375,105 +319,136 @@ class DibiConnection extends DibiObject
* @return int * @return int
* @throws DibiException * @throws DibiException
*/ */
public function getInsertId($sequence = NULL) public function insertId($sequence = NULL)
{ {
$this->connected || $this->connect(); $id = $this->driver->insertId($sequence);
$id = $this->driver->getInsertId($sequence); if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.');
if ($id < 1) {
throw new DibiException('Cannot retrieve last generated ID.');
}
return (int) $id; return (int) $id;
} }
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public function insertId($sequence = NULL)
{
return $this->getInsertId($sequence);
}
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
*/ */
public function begin($savepoint = NULL) public function begin()
{ {
$this->connected || $this->connect(); $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL; if ($this->inTxn) {
try { throw new DibiException('There is already an active transaction.');
$this->driver->begin($savepoint); }
$event && $this->onEvent($event->done()); if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::BEGIN);
} catch (DibiException $e) { }
$event && $this->onEvent($event->done($e)); $this->driver->begin();
throw $e; $this->inTxn = TRUE;
if (isset($ticket)) {
$this->profiler->after($ticket);
} }
} }
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
*/ */
public function commit($savepoint = NULL) public function commit()
{ {
$this->connected || $this->connect(); if (!$this->inTxn) {
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL; throw new DibiException('There is no active transaction.');
try { }
$this->driver->commit($savepoint); if ($this->profiler !== NULL) {
$event && $this->onEvent($event->done()); $ticket = $this->profiler->before($this, IDibiProfiler::COMMIT);
}
} catch (DibiException $e) { $this->driver->commit();
$event && $this->onEvent($event->done($e)); $this->inTxn = FALSE;
throw $e; if (isset($ticket)) {
$this->profiler->after($ticket);
} }
} }
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
*/ */
public function rollback($savepoint = NULL) public function rollback()
{ {
$this->connected || $this->connect(); if (!$this->inTxn) {
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL; throw new DibiException('There is no active transaction.');
try { }
$this->driver->rollback($savepoint); if ($this->profiler !== NULL) {
$event && $this->onEvent($event->done()); $ticket = $this->profiler->before($this, IDibiProfiler::ROLLBACK);
}
} catch (DibiException $e) { $this->driver->rollback();
$event && $this->onEvent($event->done($e)); $this->inTxn = FALSE;
throw $e; if (isset($ticket)) {
$this->profiler->after($ticket);
} }
} }
/** /**
* Result set factory. * Encodes data for use in an SQL statement.
* @param IDibiResultDriver * @param string unescaped string
* @return DibiResult * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string escaped and quoted string
*/ */
public function createResultSet(IDibiResultDriver $resultDriver) public function escape($value, $type = dibi::FIELD_TEXT)
{ {
$res = new DibiResult($resultDriver); $this->connect(); // MySQL & PDO require connection
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate']) return $this->driver->escape($value, $type);
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
} }
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::FIELD_BINARY)
* @return string decoded value
*/
public function unescape($value, $type = dibi::FIELD_BINARY)
{
return $this->driver->unescape($value, $type);
}
/**
* Delimites identifier (table's or column's name, etc.).
* @param string identifier
* @return string delimited identifier
*/
public function delimite($value)
{
return $this->driver->escape($value, dibi::IDENTIFIER);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
{
$this->driver->applyLimit($sql, $limit, $offset);
}
/********************* fluent SQL builders ****************d*g**/ /********************* fluent SQL builders ****************d*g**/
/** /**
* @return DibiFluent * @return DibiFluent
*/ */
@@ -483,6 +458,7 @@ class DibiConnection extends DibiObject
} }
/** /**
* @param string column name * @param string column name
* @return DibiFluent * @return DibiFluent
@@ -494,37 +470,32 @@ class DibiConnection extends DibiObject
} }
/** /**
* @param string table * @param string table
* @param array * @param array
* @return DibiFluent * @return DibiFluent
*/ */
public function update($table, $args) public function update($table, array $args)
{ {
if (!(is_array($args) || $args instanceof Traversable)) {
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->update('%n', $table)->set($args); return $this->command()->update('%n', $table)->set($args);
} }
/** /**
* @param string table * @param string table
* @param array * @param array
* @return DibiFluent * @return DibiFluent
*/ */
public function insert($table, $args) public function insert($table, array $args)
{ {
if ($args instanceof Traversable) {
$args = iterator_to_array($args);
} elseif (!is_array($args)) {
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->insert() return $this->command()->insert()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args); ->into('%n', $table, '(%n)', array_keys($args))->values('%l', array_values($args));
} }
/** /**
* @param string table * @param string table
* @return DibiFluent * @return DibiFluent
@@ -535,96 +506,36 @@ class DibiConnection extends DibiObject
} }
/********************* substitutions ****************d*g**/
/********************* profiler ****************d*g**/
/** /**
* Returns substitution hashmap. * @param IDibiProfiler
* @return DibiHashMap * @return void
*/ */
public function getSubstitutes() public function setProfiler(IDibiProfiler $profiler = NULL)
{ {
return $this->substitutes; $this->profiler = $profiler;
} }
/** /**
* Provides substitution. * @return IDibiProfiler
* @return string
*/ */
public function substitute($value) public function getProfiler()
{ {
return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value); return $this->profiler;
} }
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
}
/********************* shortcuts ****************d*g**/
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return DibiRow
* @throws DibiException
*/
public function fetch($args)
{
$args = func_get_args();
return $this->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return DibiRow[]
* @throws DibiException
*/
public function fetchAll($args)
{
$args = func_get_args();
return $this->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public function fetchSingle($args)
{
$args = func_get_args();
return $this->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public function fetchPairs($args)
{
$args = func_get_args();
return $this->query($args)->fetchPairs();
}
/********************* misc ****************d*g**/ /********************* misc ****************d*g**/
/** /**
* Import SQL dump from file - extreme fast! * Import SQL dump from file - extreme fast!
* @param string filename * @param string filename
@@ -632,12 +543,13 @@ class DibiConnection extends DibiObject
*/ */
public function loadFile($file) public function loadFile($file)
{ {
$this->connected || $this->connect(); $this->connect();
@set_time_limit(0); // intentionally @ @set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @ $handle = @fopen($file, 'r'); // intentionally @
if (!$handle) { if (!$handle) {
throw new RuntimeException("Cannot open file '$file'."); throw new FileNotFoundException("Cannot open file '$file'.");
} }
$count = 0; $count = 0;
@@ -651,41 +563,39 @@ class DibiConnection extends DibiObject
$count++; $count++;
} }
} }
if (trim($sql) !== '') {
$this->driver->query($sql);
$count++;
}
fclose($handle); fclose($handle);
return $count; return $count;
} }
/** /**
* Gets a information about the current database. * Gets a information about the current database.
* @return DibiDatabaseInfo * @return DibiDatabaseInfo
*/ */
public function getDatabaseInfo() public function getDatabaseInfo()
{ {
$this->connected || $this->connect(); return new DibiDatabaseInfo($this->driver, isset($this->config['database']) ? $this->config['database'] : NULL);
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
} }
/** /**
* Prevents unserialization. * Prevents unserialization.
*/ */
public function __wakeup() public function __wakeup()
{ {
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
} }
/** /**
* Prevents serialization. * Prevents serialization.
*/ */
public function __sleep() public function __sleep()
{ {
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
} }
} }

View File

@@ -1,21 +1,31 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* Default implementation of IDataSource for dibi. * Default implementation of IDataSource for dibi.
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*
* @property-read DibiConnection $connection
* @property-read DibiResult $result
* @property-read DibiResultIterator $iterator
* @property-read int $totalCount
*/ */
class DibiDataSource extends DibiObject implements IDataSource class DibiDataSource extends DibiObject implements IDataSource
{ {
@@ -25,288 +35,58 @@ class DibiDataSource extends DibiObject implements IDataSource
/** @var string */ /** @var string */
private $sql; private $sql;
/** @var DibiResult */
private $result;
/** @var int */ /** @var int */
private $count; private $count;
/** @var int */
private $totalCount;
/** @var array */
private $cols = array();
/** @var array */
private $sorting = array();
/** @var array */
private $conds = array();
/** @var int */
private $offset;
/** @var int */
private $limit;
/** /**
* @param string SQL command or table or view name, as data source * @param string SQL command or table name, as data source
* @param DibiConnection connection * @param DibiConnection connection
*/ */
public function __construct($sql, DibiConnection $connection) public function __construct($sql, DibiConnection $connection = NULL)
{ {
if (strpbrk($sql, " \t\r\n") === FALSE) { if (strpos($sql, ' ') === FALSE) {
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name // table name
$this->sql = $sql;
} else { } else {
$this->sql = '(' . $sql . ') t'; // SQL command // SQL command
$this->sql = '(' . $sql . ') AS [source]';
} }
$this->connection = $connection;
$this->connection = $connection === NULL ? dibi::getConnection() : $connection;
} }
/** /**
* Selects columns to query. * @param int offset
* @param string|array column name or array of column names * @param int limit
* @param string column alias * @param array columns
* @return self * @return ArrayIterator
*/ */
public function select($col, $as = NULL) public function getIterator($offset = NULL, $limit = NULL)
{ {
if (is_array($col)) { return $this->connection->query('
$this->cols = $col; SELECT *
} else { FROM', $this->sql, '
$this->cols[$col] = $as; %ofs %lmt', $offset, $limit
} );
$this->result = NULL;
return $this;
} }
/**
* Adds conditions to query.
* @param mixed conditions
* @return self
*/
public function where($cond)
{
if (is_array($cond)) {
// TODO: not consistent with select and orderBy
$this->conds[] = $cond;
} else {
$this->conds[] = func_get_args();
}
$this->result = $this->count = NULL;
return $this;
}
/** /**
* Selects columns to order by.
* @param string|array column name or array of column names
* @param string sorting direction
* @return self
*/
public function orderBy($row, $sorting = 'ASC')
{
if (is_array($row)) {
$this->sorting = $row;
} else {
$this->sorting[$row] = $sorting;
}
$this->result = NULL;
return $this;
}
/**
* Limits number of rows.
* @param int limit
* @param int offset
* @return self
*/
public function applyLimit($limit, $offset = NULL)
{
$this->limit = $limit;
$this->offset = $offset;
$this->result = $this->count = NULL;
return $this;
}
/**
* Returns the dibi connection.
* @return DibiConnection
*/
final public function getConnection()
{
return $this->connection;
}
/********************* executing ****************d*g**/
/**
* Returns (and queries) DibiResult.
* @return DibiResult
*/
public function getResult()
{
if ($this->result === NULL) {
$this->result = $this->connection->nativeQuery($this->__toString());
}
return $this->result;
}
/**
* @return DibiResultIterator
*/
public function getIterator()
{
return $this->getResult()->getIterator();
}
/**
* Generates, executes SQL query and fetches the single row.
* @return DibiRow|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
return $this->getResult()->fetch();
}
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
*/
public function fetchSingle()
{
return $this->getResult()->fetchSingle();
}
/**
* Fetches all records from table.
* @return array
*/
public function fetchAll()
{
return $this->getResult()->fetchAll();
}
/**
* Fetches all records from table and returns associative tree.
* @param string associative descriptor
* @return array
*/
public function fetchAssoc($assoc)
{
return $this->getResult()->fetchAssoc($assoc);
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
* @param string value
* @return array
*/
public function fetchPairs($key = NULL, $value = NULL)
{
return $this->getResult()->fetchPairs($key, $value);
}
/**
* Discards the internal cache.
* @return void
*/
public function release()
{
$this->result = $this->count = $this->totalCount = NULL;
}
/********************* exporting ****************d*g**/
/**
* Returns this data source wrapped in DibiFluent object.
* @return DibiFluent
*/
public function toFluent()
{
return $this->connection->select('*')->from('(%SQL) t', $this->__toString());
}
/**
* Returns this data source wrapped in DibiDataSource object.
* @return DibiDataSource
*/
public function toDataSource()
{
return new self($this->__toString(), $this->connection);
}
/**
* Returns SQL query.
* @return string
*/
public function __toString()
{
try {
return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, '
%ex', $this->conds ? array('WHERE %and', $this->conds) : NULL, '
%ex', $this->sorting ? array('ORDER BY %by', $this->sorting) : NULL, '
%ofs %lmt', $this->offset, $this->limit
);
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/********************* counting ****************d*g**/
/**
* Returns the number of rows in a given data source.
* @return int * @return int
*/ */
public function count() public function count()
{ {
if ($this->count === NULL) { if ($this->count === NULL) {
$this->count = $this->conds || $this->offset || $this->limit $this->count = $this->connection->query('
? (int) $this->connection->nativeQuery( SELECT COUNT(*) FROM', $this->sql
'SELECT COUNT(*) FROM (' . $this->__toString() . ') t' )->fetchSingle();
)->fetchSingle()
: $this->getTotalCount();
} }
return $this->count; return $this->count;
} }
/**
* Returns the number of rows in a given data source.
* @return int
*/
public function getTotalCount()
{
if ($this->totalCount === NULL) {
$this->totalCount = (int) $this->connection->nativeQuery(
'SELECT COUNT(*) FROM ' . $this->sql
)->fetchSingle();
}
return $this->totalCount;
}
} }

View File

@@ -1,25 +1,36 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* Reflection metadata class for a database. * Reflection metadata class for a database.
* *
* @author David Grudl * @author David Grudl
* @package dibi\reflection * @copyright Copyright (c) 2005, 2008 David Grudl
* * @package dibi
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/ */
class DibiDatabaseInfo extends DibiObject class DibiDatabaseInfo extends DibiObject
{ {
/** @var IDibiReflector */ /** @var IDibiDriver */
private $reflector; private $driver;
/** @var string */ /** @var string */
private $name; private $name;
@@ -28,13 +39,15 @@ class DibiDatabaseInfo extends DibiObject
private $tables; private $tables;
public function __construct(IDibiReflector $reflector, $name)
public function __construct(IDibiDriver $driver, $name)
{ {
$this->reflector = $reflector; $this->driver = $driver;
$this->name = $name; $this->name = $name;
} }
/** /**
* @return string * @return string
*/ */
@@ -44,8 +57,9 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* @return DibiTableInfo[] * @return array of DibiTableInfo
*/ */
public function getTables() public function getTables()
{ {
@@ -54,8 +68,9 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* @return string[] * @return array of string
*/ */
public function getTableNames() public function getTableNames()
{ {
@@ -68,8 +83,9 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* @param string * @param string
* @return DibiTableInfo * @return DibiTableInfo
*/ */
public function getTable($name) public function getTable($name)
@@ -85,8 +101,9 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* @param string * @param string
* @return bool * @return bool
*/ */
public function hasTable($name) public function hasTable($name)
@@ -96,6 +113,7 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* @return void * @return void
*/ */
@@ -103,8 +121,8 @@ class DibiDatabaseInfo extends DibiObject
{ {
if ($this->tables === NULL) { if ($this->tables === NULL) {
$this->tables = array(); $this->tables = array();
foreach ($this->reflector->getTables() as $info) { foreach ($this->driver->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new DibiTableInfo($this->reflector, $info); $this->tables[strtolower($info['name'])] = new DibiTableInfo($this->driver, $info);
} }
} }
} }
@@ -112,24 +130,19 @@ class DibiDatabaseInfo extends DibiObject
} }
/** /**
* Reflection metadata class for a database table. * Reflection metadata class for a database table.
* *
* @author David Grudl * @author David Grudl
* @package dibi\reflection * @copyright Copyright (c) 2005, 2008 David Grudl
* * @package dibi
* @property-read string $name
* @property-read bool $view
* @property-read array $columns
* @property-read array $columnNames
* @property-read array $foreignKeys
* @property-read array $indexes
* @property-read DibiIndexInfo $primaryKey
*/ */
class DibiTableInfo extends DibiObject class DibiTableInfo extends DibiObject
{ {
/** @var IDibiReflector */ /** @var IDibiDriver */
private $reflector; private $driver;
/** @var string */ /** @var string */
private $name; private $name;
@@ -150,14 +163,16 @@ class DibiTableInfo extends DibiObject
private $primaryKey; private $primaryKey;
public function __construct(IDibiReflector $reflector, array $info)
public function __construct(IDibiDriver $driver, array $info)
{ {
$this->reflector = $reflector; $this->driver = $driver;
$this->name = $info['name']; $this->name = $info['name'];
$this->view = !empty($info['view']); $this->view = !empty($info['view']);
} }
/** /**
* @return string * @return string
*/ */
@@ -167,6 +182,7 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return bool * @return bool
*/ */
@@ -176,8 +192,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return DibiColumnInfo[] * @return array of DibiColumnInfo
*/ */
public function getColumns() public function getColumns()
{ {
@@ -186,8 +203,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return string[] * @return array of string
*/ */
public function getColumnNames() public function getColumnNames()
{ {
@@ -200,8 +218,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @param string * @param string
* @return DibiColumnInfo * @return DibiColumnInfo
*/ */
public function getColumn($name) public function getColumn($name)
@@ -217,8 +236,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @param string * @param string
* @return bool * @return bool
*/ */
public function hasColumn($name) public function hasColumn($name)
@@ -228,8 +248,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return DibiForeignKeyInfo[] * @return array of DibiForeignKeyInfo
*/ */
public function getForeignKeys() public function getForeignKeys()
{ {
@@ -238,8 +259,9 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return DibiIndexInfo[] * @return array of DibiIndexInfo
*/ */
public function getIndexes() public function getIndexes()
{ {
@@ -248,6 +270,7 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return DibiIndexInfo * @return DibiIndexInfo
*/ */
@@ -258,6 +281,7 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return void * @return void
*/ */
@@ -265,13 +289,14 @@ class DibiTableInfo extends DibiObject
{ {
if ($this->columns === NULL) { if ($this->columns === NULL) {
$this->columns = array(); $this->columns = array();
foreach ($this->reflector->getColumns($this->name) as $info) { foreach ($this->driver->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->reflector, $info); $this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->driver, $info);
} }
} }
} }
/** /**
* @return void * @return void
*/ */
@@ -280,7 +305,7 @@ class DibiTableInfo extends DibiObject
if ($this->indexes === NULL) { if ($this->indexes === NULL) {
$this->initColumns(); $this->initColumns();
$this->indexes = array(); $this->indexes = array();
foreach ($this->reflector->getIndexes($this->name) as $info) { foreach ($this->driver->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) { foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)]; $info['columns'][$key] = $this->columns[strtolower($name)];
} }
@@ -293,150 +318,52 @@ class DibiTableInfo extends DibiObject
} }
/** /**
* @return void * @return void
*/ */
protected function initForeignKeys() protected function initForeignKeys()
{ {
throw new DibiNotImplementedException; throw new NotImplementedException;
} }
} }
/**
* Reflection metadata class for a result set.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read array $columns
* @property-read array $columnNames
*/
class DibiResultInfo extends DibiObject
{
/** @var IDibiResultDriver */
private $driver;
/** @var array */
private $columns;
/** @var array */
private $names;
public function __construct(IDibiResultDriver $driver)
{
$this->driver = $driver;
}
/**
* @return DibiColumnInfo[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @param bool
* @return string[]
*/
public function getColumnNames($fullNames = FALSE)
{
$this->initColumns();
$res = array();
foreach ($this->columns as $column) {
$res[] = $fullNames ? $column->getFullName() : $column->getName();
}
return $res;
}
/**
* @param string
* @return DibiColumnInfo
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->names[$l])) {
return $this->names[$l];
} else {
throw new DibiException("Result set has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->names[strtolower($name)]);
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = array();
$reflector = $this->driver instanceof IDibiReflector ? $this->driver : NULL;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[$info['name']] = new DibiColumnInfo($reflector, $info);
}
}
}
}
/** /**
* Reflection metadata class for a table or result set column. * Reflection metadata class for a table column.
* *
* @author David Grudl * @author David Grudl
* @package dibi\reflection * @copyright Copyright (c) 2005, 2008 David Grudl
* * @package dibi
* @property-read string $name
* @property-read string $fullName
* @property-read DibiTableInfo $table
* @property-read string $type
* @property-read mixed $nativeType
* @property-read int $size
* @property-read bool $unsigned
* @property-read bool $nullable
* @property-read bool $autoIncrement
* @property-read mixed $default
*/ */
class DibiColumnInfo extends DibiObject class DibiColumnInfo extends DibiObject
{ {
/** @var array */ /** @var array */
private static $types; private static $types;
/** @var IDibiReflector|NULL when created by DibiResultInfo */ /** @var IDibiDriver */
private $reflector; private $driver;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */ /** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info; private $info;
/** @var string */
private $type;
public function __construct(IDibiReflector $reflector = NULL, array $info)
public function __construct(IDibiDriver $driver, array $info)
{ {
$this->reflector = $reflector; $this->driver = $driver;
$this->info = $info; $this->info = $info;
$this->type = self::detectType($this->info['nativetype']);
} }
/** /**
* @return string * @return string
*/ */
@@ -446,14 +373,6 @@ class DibiColumnInfo extends DibiObject
} }
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/** /**
* @return bool * @return bool
@@ -464,36 +383,30 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* @return DibiTableInfo * @return DibiTableInfo
*/ */
public function getTable() public function getTable()
{ {
if (empty($this->info['table']) || !$this->reflector) { if (empty($this->info['table'])) {
throw new DibiException("Table is unknown or not available."); throw new DibiException("Table name is unknown.");
} }
return new DibiTableInfo($this->reflector, array('name' => $this->info['table'])); return new DibiTableInfo($this->driver, array('name' => $this->info['table']));
} }
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) ? $this->info['table'] : NULL;
}
/** /**
* @return string * @return string
*/ */
public function getType() public function getType()
{ {
return self::getTypeCache()->{$this->info['nativetype']}; return $this->type;
} }
/** /**
* @return mixed * @return mixed
*/ */
@@ -503,6 +416,7 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* @return int * @return int
*/ */
@@ -512,14 +426,6 @@ class DibiColumnInfo extends DibiObject
} }
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/** /**
* @return bool * @return bool
@@ -530,6 +436,7 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* @return bool * @return bool
*/ */
@@ -539,6 +446,7 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* @return mixed * @return mixed
*/ */
@@ -548,6 +456,7 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* @param string * @param string
* @return mixed * @return mixed
@@ -558,58 +467,47 @@ class DibiColumnInfo extends DibiObject
} }
/** /**
* Heuristic type detection. * Heuristic type detection.
* @param string * @param string
* @return string * @return string
* @internal
*/ */
public static function detectType($type) public static function detectType($type)
{ {
static $patterns = array( static $patterns = array(
'^_' => dibi::TEXT, // PostgreSQL arrays 'BYTE|COUNTER|SERIAL|INT|LONG' => dibi::FIELD_INTEGER,
'BYTEA|BLOB|BIN' => dibi::BINARY, 'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC' => dibi::FIELD_FLOAT,
'TEXT|CHAR' => dibi::TEXT, '^TIME$' => dibi::FIELD_TIME,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG' => dibi::INTEGER, 'TIME' => dibi::FIELD_DATETIME, // DATETIME, TIMESTAMP
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => dibi::FLOAT, 'YEAR|DATE' => dibi::FIELD_DATE,
'^TIME$' => dibi::TIME, 'BYTEA|BLOB|BIN' => dibi::FIELD_BINARY,
'TIME' => dibi::DATETIME, // DATETIME, TIMESTAMP 'BOOL|BIT' => dibi::FIELD_BOOL,
'DATE' => dibi::DATE,
'BOOL|BIT' => dibi::BOOL,
); );
foreach ($patterns as $s => $val) { if (!isset(self::$types[$type])) {
if (preg_match("#$s#i", $type)) { self::$types[$type] = dibi::FIELD_TEXT;
return $val; foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return self::$types[$type] = $val;
}
} }
} }
return dibi::TEXT; return self::$types[$type];
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new DibiHashMap(array(__CLASS__, 'detectType'));
}
return self::$types;
} }
} }
/** /**
* Reflection metadata class for a foreign key. * Reflection metadata class for a foreign key.
* *
* @author David Grudl * @author David Grudl
* @package dibi\reflection * @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
* @todo * @todo
*
* @property-read string $name
* @property-read array $references
*/ */
class DibiForeignKeyInfo extends DibiObject class DibiForeignKeyInfo extends DibiObject
{ {
@@ -620,6 +518,7 @@ class DibiForeignKeyInfo extends DibiObject
private $references; private $references;
public function __construct($name, array $references) public function __construct($name, array $references)
{ {
$this->name = $name; $this->name = $name;
@@ -627,6 +526,7 @@ class DibiForeignKeyInfo extends DibiObject
} }
/** /**
* @return string * @return string
*/ */
@@ -636,6 +536,7 @@ class DibiForeignKeyInfo extends DibiObject
} }
/** /**
* @return array * @return array
*/ */
@@ -647,16 +548,14 @@ class DibiForeignKeyInfo extends DibiObject
} }
/** /**
* Reflection metadata class for a index or primary key. * Reflection metadata class for a index or primary key
* *
* @author David Grudl * @author David Grudl
* @package dibi\reflection * @copyright Copyright (c) 2005, 2008 David Grudl
* * @package dibi
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/ */
class DibiIndexInfo extends DibiObject class DibiIndexInfo extends DibiObject
{ {
@@ -670,6 +569,7 @@ class DibiIndexInfo extends DibiObject
} }
/** /**
* @return string * @return string
*/ */
@@ -679,6 +579,7 @@ class DibiIndexInfo extends DibiObject
} }
/** /**
* @return array * @return array
*/ */
@@ -688,6 +589,7 @@ class DibiIndexInfo extends DibiObject
} }
/** /**
* @return bool * @return bool
*/ */
@@ -697,6 +599,8 @@ class DibiIndexInfo extends DibiObject
} }
/** /**
* @return bool * @return bool
*/ */

View File

@@ -1,76 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* DateTime with serialization and timestamp support for PHP 5.2.
*
* @author David Grudl
* @package dibi
*/
class DibiDateTime extends DateTime
{
public function __construct($time = 'now', DateTimeZone $timezone = NULL)
{
if (is_numeric($time)) {
$time = date('Y-m-d H:i:s', $time);
}
if ($timezone === NULL) {
parent::__construct($time);
} else {
parent::__construct($time, $timezone);
}
}
public function modifyClone($modify = '')
{
$dolly = clone($this);
return $modify ? $dolly->modify($modify) : $dolly;
}
public function modify($modify)
{
parent::modify($modify);
return $this;
}
public function __sleep()
{
$this->fix = array($this->format('Y-m-d H:i:s'), $this->getTimezone()->getName());
return array('fix');
}
public function __wakeup()
{
$this->__construct($this->fix[0], new DateTimeZone($this->fix[1]));
unset($this->fix);
}
public function getTimestamp()
{
return (int) $this->format('U');
}
public function setTimestamp($timestamp)
{
return $this->__construct(date('Y-m-d H:i:s', $timestamp), new DateTimeZone($this->getTimezone()->getName())); // getTimeZone() crashes in PHP 5.2.6
}
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
}

View File

@@ -1,97 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* Profiler & logger event.
*
* @author David Grudl
* @package dibi
*/
class DibiEvent
{
/** event type */
const CONNECT = 1,
SELECT = 4,
INSERT = 8,
DELETE = 16,
UPDATE = 32,
QUERY = 60, // SELECT | INSERT | DELETE | UPDATE
BEGIN = 64,
COMMIT = 128,
ROLLBACK = 256,
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var DibiConnection */
public $connection;
/** @var int */
public $type;
/** @var string */
public $sql;
/** @var DibiResult|DibiDriverException|NULL */
public $result;
/** @var float */
public $time;
/** @var int */
public $count;
/** @var array */
public $source;
public function __construct(DibiConnection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
$this->sql = trim($sql);
$this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = array(
'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
);
$this->type = $types[strtoupper($matches[1])];
}
$rc = new ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = array($row['file'], (int) $row['line']);
break;
}
}
dibi::$elapsedTime = FALSE;
dibi::$numOfQueries++;
dibi::$sql = $sql;
}
public function done($result = NULL)
{
$this->result = $result;
try {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = NULL;
}
$this->time += microtime(TRUE);
dibi::$elapsedTime = $this->time;
dibi::$totalTime += $this->time;
return $this;
}
}

View File

@@ -1,36 +1,70 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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 common exception. * dibi common exception.
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*/ */
class DibiException extends Exception class DibiException extends Exception
{ {
}
/**
* database server exception.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/
class DibiDriverException extends DibiException implements /*Nette::*/IDebuggable
{
/** @var string */
private static $errorMsg;
/** @var string */ /** @var string */
private $sql; private $sql;
/** /**
* Construct a dibi exception. * Construct an dibi driver exception.
* @param string Message describing the exception * @param string Message describing the exception
* @param int Some code * @param int Some code
* @param string SQL command * @param string SQL command
*/ */
public function __construct($message = NULL, $code = 0, $sql = NULL) public function __construct($message = NULL, $code = 0, $sql = NULL)
{ {
parent::__construct($message, (int) $code); parent::__construct($message, (int) $code);
$this->sql = $sql; $this->sql = $sql;
// TODO: add $profiler->exception($this);
} }
/** /**
* @return string The SQL passed to the constructor * @return string The SQL passed to the constructor
*/ */
@@ -40,6 +74,7 @@ class DibiException extends Exception
} }
/** /**
* @return string string represenation of exception with SQL command * @return string string represenation of exception with SQL command
*/ */
@@ -48,27 +83,35 @@ class DibiException extends Exception
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : ''); return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
} }
}
/** /********************* interface Nette::IDebuggable ****************d*g**/
* database server exception.
*
* @author David Grudl /**
* @package dibi * Returns custom panels.
*/ * @return array
class DibiDriverException extends DibiException */
{ public function getPanels()
{
$panels = array();
if ($this->sql !== NULL) {
$panels['SQL'] = array(
'expanded' => TRUE,
'content' => dibi::dump($this->sql, TRUE),
);
}
return $panels;
}
/********************* error catching ****************d*g**/ /********************* error catching ****************d*g**/
/** @var string */
private static $errorMsg;
/** /**
* Starts catching potential errors/warnings. * Starts catching potential errors/warnings
* @return void * @return void
*/ */
public static function tryError() public static function tryError()
@@ -78,6 +121,7 @@ class DibiDriverException extends DibiException
} }
/** /**
* Returns catched error/warning message. * Returns catched error/warning message.
* @param string catched message * @param string catched message
@@ -92,6 +136,7 @@ class DibiDriverException extends DibiException
} }
/** /**
* Internal error handler. Do not call directly. * Internal error handler. Do not call directly.
* @internal * @internal
@@ -108,41 +153,4 @@ class DibiDriverException extends DibiException
self::$errorMsg = $message; self::$errorMsg = $message;
} }
} }
/**
* PCRE exception.
*
* @author David Grudl
* @package dibi
*/
class DibiPcreException extends Exception {
public function __construct($message = '%msg.')
{
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
);
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
/**
* @package dibi
*/
class DibiNotImplementedException extends DibiException
{}
/**
* @package dibi
*/
class DibiNotSupportedException extends DibiException
{}

View File

@@ -1,73 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* dibi file logger.
*
* @author David Grudl
* @package dibi
*/
class DibiFileLogger extends DibiObject
{
/** @var string Name of the file where SQL errors should be logged */
public $file;
/** @var int */
public $filter;
public function __construct($file, $filter = NULL)
{
$this->file = $file;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$handle = fopen($this->file, 'a');
if (!$handle) {
return; // or throw exception?
}
flock($handle, LOCK_EX);
if ($event->result instanceof Exception) {
$message = $event->result->getMessage();
if ($code = $event->result->getCode()) {
$message = "[$code] $message";
}
fwrite($handle,
"ERROR: $message"
. "\n-- SQL: " . $event->sql
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. ";\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
} else {
fwrite($handle,
"OK: " . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f ms', $event->time * 1000)
. "\n-- source: " . implode(':', $event->source)
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. "\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
}
fclose($handle);
}
}

View File

@@ -1,88 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* dibi FirePHP logger.
*
* @author David Grudl
* @package dibi
*/
class DibiFirePhpLogger extends DibiObject
{
/** maximum number of rows */
static public $maxQueries = 30;
/** maximum SQL length */
static public $maxLength = 1000;
/** @var int */
public $filter;
/** @var int Elapsed time for all queries */
public $totalTime = 0;
/** @var int Number of all queries */
public $numOfQueries = 0;
/** @var array */
private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
/**
* @return bool
*/
public static function isAvailable()
{
return isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
}
public function __construct($filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
{
if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) {
return;
}
$this->totalTime += $event->time;
$this->numOfQueries++;
self::$fireTable[] = array(
sprintf('%0.3f', $event->time * 1000),
strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql,
$event->result instanceof Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
);
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
$payload = json_encode(array(
array(
'Type' => 'TABLE',
'Label' => 'dibi profiler (' . $this->numOfQueries . ' SQL queries took ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms)',
),
self::$fireTable,
));
foreach (str_split($payload, 4990) as $num => $s) {
$num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
}
header("X-Wf-dibi-1-1-d$num: |$s|");
}
}

View File

@@ -1,48 +1,47 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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 SQL builder via fluent interfaces. EXPERIMENTAL! * dibi SQL builder via fluent interfaces. EXPERIMENTAL!
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*
* @property-read string $command
* @property-read DibiConnection $connection
* @property-read DibiResultIterator $iterator
* @method DibiFluent select($field)
* @method DibiFluent distinct()
* @method DibiFluent from($table)
* @method DibiFluent where($cond)
* @method DibiFluent groupBy($field)
* @method DibiFluent having($cond)
* @method DibiFluent orderBy($field)
* @method DibiFluent limit(int $limit)
* @method DibiFluent offset(int $offset)
*/ */
class DibiFluent extends DibiObject implements IDataSource class DibiFluent extends DibiObject
{ {
const REMOVE = FALSE;
/** @var array */ /** @var array */
public static $masks = array( public static $masks = array(
'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY', 'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'), 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET', '%end'),
'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'), 'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT', '%end'),
'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT'), 'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT', '%end'),
'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'), 'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT', '%end'),
); );
/** @var array default modifiers for arrays */ /** @var array */
public static $modifiers = array( public static $modifiers = array(
'SELECT' => '%n', 'SELECT' => '%n',
'FROM' => '%n', 'IN' => '%l',
'IN' => '%in',
'VALUES' => '%l', 'VALUES' => '%l',
'SET' => '%a', 'SET' => '%a',
'WHERE' => '%and', 'WHERE' => '%and',
@@ -51,10 +50,10 @@ class DibiFluent extends DibiObject implements IDataSource
'GROUP BY' => '%by', 'GROUP BY' => '%by',
); );
/** @var array clauses separators */ /** @var array */
public static $separators = array( public static $separators = array(
'SELECT' => ',', 'SELECT' => ',',
'FROM' => ',', 'FROM' => FALSE,
'WHERE' => 'AND', 'WHERE' => 'AND',
'GROUP BY' => ',', 'GROUP BY' => ',',
'HAVING' => 'AND', 'HAVING' => 'AND',
@@ -66,20 +65,9 @@ class DibiFluent extends DibiObject implements IDataSource
'INTO' => FALSE, 'INTO' => FALSE,
); );
/** @var array clauses */
public static $clauseSwitches = array(
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
);
/** @var DibiConnection */ /** @var DibiConnection */
private $connection; private $connection;
/** @var array */
private $setups = array();
/** @var string */ /** @var string */
private $command; private $command;
@@ -92,8 +80,6 @@ class DibiFluent extends DibiObject implements IDataSource
/** @var array */ /** @var array */
private $cursor; private $cursor;
/** @var DibiHashMap normalized clauses */
private static $normalizer;
/** /**
@@ -102,22 +88,19 @@ class DibiFluent extends DibiObject implements IDataSource
public function __construct(DibiConnection $connection) public function __construct(DibiConnection $connection)
{ {
$this->connection = $connection; $this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
}
} }
/** /**
* Appends new argument to the clause. * Appends new argument to the clause.
* @param string clause name * @param string clause name
* @param array arguments * @param array arguments
* @return self * @return DibiFluent provides a fluent interface
*/ */
public function __call($clause, $args) public function __call($clause, $args)
{ {
$clause = self::$normalizer->$clause; $clause = self::_formatClause($clause);
// lazy initialization // lazy initialization
if ($this->command === NULL) { if ($this->command === NULL) {
@@ -129,9 +112,24 @@ class DibiFluent extends DibiObject implements IDataSource
$this->command = $clause; $this->command = $clause;
} }
// auto-switch to a clause // special types or argument
if (isset(self::$clauseSwitches[$clause])) { if (count($args) === 1) {
$this->cursor = & $this->clauses[self::$clauseSwitches[$clause]]; $arg = $args[0];
// TODO: really ignore TRUE?
if ($arg === TRUE) { // flag
$args = array();
} elseif (is_string($arg) && preg_match('#^[a-z][a-z0-9_.]*$#i', $arg)) { // identifier
$args = array('%n', $arg);
} elseif (is_array($arg)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
}
}
} }
if (array_key_exists($clause, $this->clauses)) { if (array_key_exists($clause, $this->clauses)) {
@@ -139,14 +137,14 @@ class DibiFluent extends DibiObject implements IDataSource
$this->cursor = & $this->clauses[$clause]; $this->cursor = & $this->clauses[$clause];
// TODO: really delete? // TODO: really delete?
if ($args === array(self::REMOVE)) { if ($args === array(FALSE)) {
$this->cursor = NULL; $this->cursor = NULL;
return $this; return $this;
} }
if (isset(self::$separators[$clause])) { if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause]; $sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace if ($sep === FALSE) {
$this->cursor = array(); $this->cursor = array();
} elseif (!empty($this->cursor)) { } elseif (!empty($this->cursor)) {
@@ -156,7 +154,7 @@ class DibiFluent extends DibiObject implements IDataSource
} else { } else {
// append to currect flow // append to currect flow
if ($args === array(self::REMOVE)) { if ($args === array(FALSE)) {
return $this; return $this;
} }
@@ -167,48 +165,22 @@ class DibiFluent extends DibiObject implements IDataSource
$this->cursor = array(); $this->cursor = array();
} }
// special types or argument array_splice($this->cursor, count($this->cursor), 0, $args);
if (count($args) === 1) {
$arg = $args[0];
// TODO: really ignore TRUE?
if ($arg === TRUE) { // flag
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = array('%n', $arg);
} elseif (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = "($arg)";
}
$this->cursor[] = $arg;
}
return $this; return $this;
} }
/** /**
* Switch to a clause. * Switch to a clause.
* @param string clause name * @param string clause name
* @return self * @return DibiFluent provides a fluent interface
*/ */
public function clause($clause, $remove = FALSE) public function clause($clause, $remove = FALSE)
{ {
$this->cursor = & $this->clauses[self::$normalizer->$clause]; $this->cursor = & $this->clauses[self::_formatClause($clause)];
if ($remove) { // deprecated, use removeClause if ($remove) {
trigger_error(__METHOD__ . '(..., TRUE) is deprecated; use removeClause() instead.', E_USER_NOTICE);
$this->cursor = NULL; $this->cursor = NULL;
} elseif ($this->cursor === NULL) { } elseif ($this->cursor === NULL) {
@@ -219,23 +191,12 @@ class DibiFluent extends DibiObject implements IDataSource
} }
/**
* Removes a clause.
* @param string clause name
* @return self
*/
public function removeClause($clause)
{
$this->clauses[self::$normalizer->$clause] = NULL;
return $this;
}
/** /**
* Change a SQL flag. * Change a SQL flag.
* @param string flag name * @param string flag name
* @param bool value * @param bool value
* @return self * @return DibiFluent provides a fluent interface
*/ */
public function setFlag($flag, $value = TRUE) public function setFlag($flag, $value = TRUE)
{ {
@@ -249,6 +210,7 @@ class DibiFluent extends DibiObject implements IDataSource
} }
/** /**
* Is a flag set? * Is a flag set?
* @param string flag name * @param string flag name
@@ -260,6 +222,7 @@ class DibiFluent extends DibiObject implements IDataSource
} }
/** /**
* Returns SQL command. * Returns SQL command.
* @return string * @return string
@@ -270,59 +233,34 @@ class DibiFluent extends DibiObject implements IDataSource
} }
/**
* Returns the dibi connection.
* @return DibiConnection
*/
final public function getConnection()
{
return $this->connection;
}
/**
* Adds DibiResult setup.
* @param string method
* @param mixed args
* @return self
*/
public function setupResult($method)
{
$this->setups[] = func_get_args();
return $this;
}
/********************* executing ****************d*g**/
/** /**
* Generates and executes SQL query. * Generates and executes SQL query.
* @param mixed what to return? * @return DibiResult|NULL result set object (if any)
* @return DibiResult|int result set object (if any)
* @throws DibiException * @throws DibiException
*/ */
public function execute($return = NULL) public function execute()
{ {
$res = $this->query($this->_export()); return $this->connection->query($this->_export());
return $return === dibi::IDENTIFIER ? $this->connection->getInsertId() : $res;
} }
/** /**
* Generates, executes SQL query and fetches the single row. * Generates, executes SQL query and fetches the single row.
* @return DibiRow|FALSE array on success, FALSE if no next record * @return DibiRow|FALSE array on success, FALSE if no next record
* @throws DibiException
*/ */
public function fetch() public function fetch()
{ {
if ($this->command === 'SELECT') { if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch(); $this->clauses['LIMIT'] = array(1);
} else {
return $this->query($this->_export())->fetch();
} }
return $this->execute()->fetch();
} }
/** /**
* Like fetch(), but returns only first field. * Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record * @return mixed value on success, FALSE if no next record
@@ -330,13 +268,13 @@ class DibiFluent extends DibiObject implements IDataSource
public function fetchSingle() public function fetchSingle()
{ {
if ($this->command === 'SELECT') { if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle(); $this->clauses['LIMIT'] = array(1);
} else {
return $this->query($this->_export())->fetchSingle();
} }
return $this->execute()->fetchSingle();
} }
/** /**
* Fetches all records from table. * Fetches all records from table.
* @param int offset * @param int offset
@@ -345,44 +283,39 @@ class DibiFluent extends DibiObject implements IDataSource
*/ */
public function fetchAll($offset = NULL, $limit = NULL) public function fetchAll($offset = NULL, $limit = NULL)
{ {
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll(); return $this->execute()->fetchAll($offset, $limit);
} }
/** /**
* Fetches all records from table and returns associative tree. * Fetches all records from table and returns associative tree.
* Associative descriptor: assoc1,#,assoc2,=,assoc3,@
* builds a tree: $data[assoc1][index][assoc2]['assoc3']->value = {record}
* @param string associative descriptor * @param string associative descriptor
* @return array * @return array
* @throws InvalidArgumentException
*/ */
public function fetchAssoc($assoc) public function fetchAssoc($assoc)
{ {
return $this->query($this->_export())->fetchAssoc($assoc); return $this->execute()->fetchAssoc($assoc);
} }
/** /**
* Fetches all records from table like $key => $value pairs. * Fetches all records from table like $key => $value pairs.
* @param string associative key * @param string associative key
* @param string value * @param string value
* @return array * @return array
* @throws InvalidArgumentException
*/ */
public function fetchPairs($key = NULL, $value = NULL) public function fetchPairs($key = NULL, $value = NULL)
{ {
return $this->query($this->_export())->fetchPairs($key, $value); return $this->execute()->fetchPairs($key, $value);
} }
/**
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return DibiResultIterator
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
}
/** /**
* Generates and prints SQL query or it's part. * Generates and prints SQL query or it's part.
@@ -395,68 +328,19 @@ class DibiFluent extends DibiObject implements IDataSource
} }
/**
* @return int
*/
public function count()
{
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]'
))->fetchSingle();
}
/**
* @return DibiResult
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array(array($res, array_shift($setup)), $setup);
}
return $res;
}
/********************* exporting ****************d*g**/
/**
* @return DibiDataSource
*/
public function toDataSource()
{
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
}
/**
* Returns SQL query.
* @return string
*/
final public function __toString()
{
try {
return $this->connection->translate($this->_export());
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/** /**
* Generates parameters for DibiTranslator. * Generates parameters for DibiTranslator.
* @param string clause name * @param string clause name
* @return array * @return array
*/ */
protected function _export($clause = NULL, $args = array()) protected function _export($clause = NULL)
{ {
if ($clause === NULL) { if ($clause === NULL) {
$data = $this->clauses; $data = $this->clauses;
} else { } else {
$clause = self::$normalizer->$clause; $clause = self::_formatClause($clause);
if (array_key_exists($clause, $this->clauses)) { if (array_key_exists($clause, $this->clauses)) {
$data = array($clause => $this->clauses[$clause]); $data = array($clause => $this->clauses[$clause]);
} else { } else {
@@ -464,46 +348,58 @@ class DibiFluent extends DibiObject implements IDataSource
} }
} }
$args = array();
foreach ($data as $clause => $statement) { foreach ($data as $clause => $statement) {
if ($statement !== NULL) { if ($statement !== NULL) {
$args[] = $clause; if ($clause[0] !== '%') {
if ($clause === $this->command && $this->flags) { $args[] = $clause;
$args[] = implode(' ', array_keys($this->flags)); if ($clause === $this->command) {
} $args[] = implode(' ', array_keys($this->flags));
foreach ($statement as $arg) { }
$args[] = $arg;
} }
array_splice($args, count($args), 0, $statement);
} }
} }
return $args; return $args;
} }
/** /**
* Format camelCase clause name to UPPER CASE. * Format camelCase clause name to UPPER CASE.
* @param string * @param string
* @return string * @return string
* @internal
*/ */
public static function _formatClause($s) private static function _formatClause($s)
{ {
if ($s === 'order' || $s === 'group') { if ($s === 'order' || $s === 'group') {
$s .= 'By'; $s .= 'By';
trigger_error("Did you mean '$s'?", E_USER_NOTICE); trigger_error("Did you mean '$s'?", E_USER_NOTICE);
} }
return strtoupper(preg_replace('#[a-z](?=[A-Z])#', '$0 ', $s)); return strtoupper(preg_replace('#[A-Z]#', ' $0', $s));
} }
public function __clone()
/**
* Returns (highlighted) SQL query.
* @return string
*/
final public function __toString()
{ {
// remove references ob_start();
foreach ($this->clauses as $clause => $val) { $this->test();
$this->clauses[$clause] = & $val; return ob_get_clean();
unset($val);
}
$this->cursor = & $foo;
} }
} }
// PHP < 5.2 compatibility
if (!function_exists('array_fill_keys')) {
function array_fill_keys($keys, $value)
{
return array_combine($keys, array_fill(0, count($keys), $value));
}
}

View File

@@ -1,73 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* Lazy cached storage.
*
* @author David Grudl
* @package dibi
* @internal
*/
abstract class DibiHashMapBase
{
private $callback;
public function __construct($callback)
{
$this->setCallback($callback);
}
public function setCallback($callback)
{
if (!is_callable($callback)) {
$able = is_callable($callback, TRUE, $textual);
throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
}
$this->callback = $callback;
}
public function getCallback()
{
return $this->callback;
}
}
/**
* Lazy cached storage.
*
* @author David Grudl
* @internal
*/
final class DibiHashMap extends DibiHashMapBase
{
public function __set($nm, $val)
{
if ($nm == '') {
$nm = "\xFF";
}
$this->$nm = $val;
}
public function __get($nm)
{
if ($nm == '') {
$nm = "\xFF";
return isset($this->$nm) ? $this->$nm : $this->$nm = call_user_func($this->getCallback(), '');
} else {
return $this->$nm = call_user_func($this->getCallback(), $nm);
}
}
}

View File

@@ -1,35 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* SQL literal value.
*
* @author David Grudl
* @package dibi
*/
class DibiLiteral extends DibiObject
{
/** @var string */
private $value;
public function __construct($value)
{
$this->value = (string) $value;
}
/**
* @return string
*/
public function __toString()
{
return $this->value;
}
}

View File

@@ -1,15 +1,29 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/** /**
* DibiObject is the ultimate ancestor of all instantiable classes. * DibiObject is the ultimate ancestor of all instantiable classes.
* *
* DibiObject is copy of Nette\Object from Nette Framework (http://nette.org). * DibiObject is copy of Nette::Object from Nette Framework (http://nettephp.com).
* *
* It defines some handful methods and enhances object core of PHP: * It defines some handful methods and enhances object core of PHP:
* - access to undeclared members throws exceptions * - access to undeclared members throws exceptions
@@ -45,6 +59,7 @@
* </code> * </code>
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*/ */
abstract class DibiObject abstract class DibiObject
@@ -53,6 +68,7 @@ abstract class DibiObject
private static $extMethods; private static $extMethods;
/** /**
* Returns the name of the class of this object. * Returns the name of the class of this object.
* @return string * @return string
@@ -63,9 +79,10 @@ abstract class DibiObject
} }
/** /**
* Access to reflection. * Access to reflection.
* @return \ReflectionObject * @return ReflectionObject
*/ */
final public function getReflection() final public function getReflection()
{ {
@@ -73,19 +90,20 @@ abstract class DibiObject
} }
/** /**
* Call to undefined method. * Call to undefined method.
* @param string method name * @param string method name
* @param array arguments * @param array arguments
* @return mixed * @return mixed
* @throws \LogicException * @throws ::MemberAccessException
*/ */
public function __call($name, $args) public function __call($name, $args)
{ {
$class = get_class($this); $class = get_class($this);
if ($name === '') { if ($name === '') {
throw new LogicException("Call to class '$class' method without name."); throw new /*::*/MemberAccessException("Call to class '$class' method without name.");
} }
// event functionality // event functionality
@@ -113,24 +131,26 @@ abstract class DibiObject
return call_user_func_array($cb, $args); return call_user_func_array($cb, $args);
} }
throw new LogicException("Call to undefined method $class::$name()."); throw new /*::*/MemberAccessException("Call to undefined method $class::$name().");
} }
/** /**
* Call to undefined static method. * Call to undefined static method.
* @param string method name (in lower case!) * @param string method name (in lower case!)
* @param array arguments * @param array arguments
* @return mixed * @return mixed
* @throws \LogicException * @throws ::MemberAccessException
*/ */
public static function __callStatic($name, $args) public static function __callStatic($name, $args)
{ {
$class = get_called_class(); $class = get_called_class();
throw new LogicException("Call to undefined static method $class::$name()."); throw new /*::*/MemberAccessException("Call to undefined static method $class::$name().");
} }
/** /**
* Adding method to class. * Adding method to class.
* @param string method name * @param string method name
@@ -148,9 +168,7 @@ abstract class DibiObject
self::$extMethods[$pair[1]][''] = NULL; self::$extMethods[$pair[1]][''] = NULL;
} }
} }
if ($name === NULL) { if ($name === NULL) return NULL;
return NULL;
}
} }
$name = strtolower($name); $name = strtolower($name);
@@ -194,18 +212,19 @@ abstract class DibiObject
} }
/** /**
* Returns property value. Do not call directly. * Returns property value. Do not call directly.
* @param string property name * @param string property name
* @return mixed property value * @return mixed property value
* @throws \LogicException if the property is not defined. * @throws ::MemberAccessException if the property is not defined.
*/ */
public function & __get($name) public function &__get($name)
{ {
$class = get_class($this); $class = get_class($this);
if ($name === '') { if ($name === '') {
throw new LogicException("Cannot read a class '$class' property without name."); throw new /*::*/MemberAccessException("Cannot read an class '$class' property without name.");
} }
// property getter support // property getter support
@@ -213,8 +232,8 @@ abstract class DibiObject
$m = 'get' . $name; $m = 'get' . $name;
if (self::hasAccessor($class, $m)) { if (self::hasAccessor($class, $m)) {
// ampersands: // ampersands:
// - uses & __get() because declaration should be forward compatible (e.g. with Nette\Web\Html) // - uses &__get() because declaration should be forward compatible (e.g. with Nette::Web::Html)
// - doesn't call & $this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value'; // - doesn't call &$this->$m because user could bypass property setter by: $x = & $obj->property; $x = 'new value';
$val = $this->$m(); $val = $this->$m();
return $val; return $val;
} }
@@ -226,23 +245,24 @@ abstract class DibiObject
} }
$name = func_get_arg(0); $name = func_get_arg(0);
throw new LogicException("Cannot read an undeclared property $class::\$$name."); throw new /*::*/MemberAccessException("Cannot read an undeclared property $class::\$$name.");
} }
/** /**
* Sets value of a property. Do not call directly. * Sets value of a property. Do not call directly.
* @param string property name * @param string property name
* @param mixed property value * @param mixed property value
* @return void * @return void
* @throws \LogicException if the property is not defined or is read-only * @throws ::MemberAccessException if the property is not defined or is read-only
*/ */
public function __set($name, $value) public function __set($name, $value)
{ {
$class = get_class($this); $class = get_class($this);
if ($name === '') { if ($name === '') {
throw new LogicException("Cannot assign to a class '$class' property without name."); throw new /*::*/MemberAccessException("Cannot assign to an class '$class' property without name.");
} }
// property setter support // property setter support
@@ -255,15 +275,16 @@ abstract class DibiObject
} else { } else {
$name = func_get_arg(0); $name = func_get_arg(0);
throw new LogicException("Cannot assign to a read-only property $class::\$$name."); throw new /*::*/MemberAccessException("Cannot assign to a read-only property $class::\$$name.");
} }
} }
$name = func_get_arg(0); $name = func_get_arg(0);
throw new LogicException("Cannot assign to an undeclared property $class::\$$name."); throw new /*::*/MemberAccessException("Cannot assign to an undeclared property $class::\$$name.");
} }
/** /**
* Is property defined? * Is property defined?
* @param string property name * @param string property name
@@ -276,19 +297,21 @@ abstract class DibiObject
} }
/** /**
* Access to undeclared property. * Access to undeclared property.
* @param string property name * @param string property name
* @return void * @return void
* @throws \LogicException * @throws ::MemberAccessException
*/ */
public function __unset($name) public function __unset($name)
{ {
$class = get_class($this); $class = get_class($this);
throw new LogicException("Cannot unset the property $class::\$$name."); throw new /*::*/MemberAccessException("Cannot unset an property $class::\$$name.");
} }
/** /**
* Has property an accessor? * Has property an accessor?
* @param string class name * @param string class name

229
dibi/libs/DibiProfiler.php Normal file
View File

@@ -0,0 +1,229 @@
<?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($sql),
$res instanceof DibiResult ? count($res) : '-',
$connection->getConfig('driver') . '/' . $connection->getConfig('name')
);
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
$payload = array(
array(
'Type' => 'TABLE',
'Label' => 'dibi profiler (' . dibi::$numOfQueries . ' SQL queries took ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms)',
),
self::$table,
);
$payload = function_exists('json_encode') ? json_encode($payload) : self::json_encode($payload);
foreach (str_split($payload, 4990) as $num => $s) {
$num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
}
header("X-Wf-dibi-1-1-d$num: |$s|");
header("X-Wf-dibi-Index: d$num");
}
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);
}
public static function json_encode($val)
{
// indexed array
if (is_array($val) && (!$val
|| array_keys($val) === range(0, count($val) - 1))) {
return '[' . implode(',', array_map(array(__CLASS__, 'json_encode'), $val)) . ']';
}
// associative array
if (is_array($val) || is_object($val)) {
$tmp = array();
foreach ($val as $k => $v) {
$tmp[] = self::json_encode((string) $k) . ':' . self::json_encode($v);
}
return '{' . implode(',', $tmp) . '}';
}
if (is_string($val)) {
$val = str_replace(array("\\", "\x00"), array("\\\\", "\\u0000"), $val); // due to bug #40915
return '"' . addcslashes($val, "\x8\x9\xA\xC\xD/\"") . '"';
}
if (is_int($val) || is_float($val)) {
return rtrim(rtrim(number_format($val, 5, '.', ''), '0'), '.');
}
if (is_bool($val)) {
return $val ? 'true' : 'false';
}
return 'null';
}
}

View File

@@ -1,13 +1,27 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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 result set. * dibi result-set.
* *
* <code> * <code>
* $result = dibi::query('SELECT * FROM [table]'); * $result = dibi::query('SELECT * FROM [table]');
@@ -23,85 +37,64 @@
* </code> * </code>
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*
* @property-read mixed $resource
* @property-read IDibiResultDriver $driver
* @property-read int $rowCount
* @property-read DibiResultIterator $iterator
* @property string $rowClass
* @property-read DibiResultInfo $info
*/ */
class DibiResult extends DibiObject implements IDataSource class DibiResult extends DibiObject implements IDataSource
{ {
/** @var array IDibiResultDriver */ /** @var array IDibiDriver */
private $driver; private $driver;
/** @var array Translate table */ /** @var array Translate table */
private $types = array(); private $xlat;
/** @var DibiResultInfo */ /** @var array Cache for $driver->getColumnsMeta() */
private $meta; private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */ /** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE; private $fetched = FALSE;
/** @var string returned object class */ /** @var array|FALSE Qualifiy each column name with the table name? */
private $rowClass = 'DibiRow'; private $withTables = FALSE;
/** @var array format */
private $formats = array();
/** /**
* @param IDibiResultDriver * @param IDibiDriver
* @param array
*/ */
public function __construct($driver) public function __construct($driver, $config)
{ {
$this->driver = $driver; $this->driver = $driver;
$this->detectTypes();
if (!empty($config[dibi::RESULT_WITH_TABLES])) {
$this->setWithTables(TRUE);
}
} }
/** /**
* @deprecated * Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
@$this->free(); // intentionally @
}
/**
* Returns the result set resource.
* @return mixed
*/ */
final public function getResource() final public function getResource()
{ {
return $this->getResultDriver()->getResultResource(); return $this->getDriver()->getResultResource();
} }
/**
* Frees the resources allocated for this result set.
* @return void
*/
final public function free()
{
if ($this->driver !== NULL) {
$this->driver->free();
$this->driver = $this->meta = NULL;
}
}
/**
* Safe access to property $driver.
* @return IDibiResultDriver
* @throws RuntimeException
*/
final public function getResultDriver()
{
if ($this->driver === NULL) {
throw new RuntimeException('Result-set was released from memory.');
}
return $this->driver;
}
/********************* rows ****************d*g**/
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
@@ -111,79 +104,75 @@ class DibiResult extends DibiObject implements IDataSource
*/ */
final public function seek($row) final public function seek($row)
{ {
return ($row !== 0 || $this->fetched) ? (bool) $this->getResultDriver()->seek($row) : TRUE; return ($row !== 0 || $this->fetched) ? (bool) $this->getDriver()->seek($row) : TRUE;
} }
/**
* Required by the Countable interface.
* @return int
*/
final public function count()
{
return $this->getResultDriver()->getRowCount();
}
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
final public function getRowCount()
{
return $this->getResultDriver()->getRowCount();
}
/**
* Returns the number of rows in a result set. Alias for getRowCount().
* @deprecated
*/
final public function rowCount() final public function rowCount()
{ {
trigger_error(__METHOD__ . '() is deprecated; use count($res) or $res->getRowCount() instead.', E_USER_WARNING); return $this->getDriver()->rowCount();
return $this->getResultDriver()->getRowCount();
} }
/** /**
* Required by the IteratorAggregate interface. * Frees the resources allocated for this result set.
* @return DibiResultIterator * @return void
*/ */
final public function getIterator() final public function free()
{ {
if (func_num_args()) { if ($this->driver !== NULL) {
trigger_error(__METHOD__ . ' arguments $offset & $limit have been dropped; use SQL clauses instead.', E_USER_WARNING); $this->driver->free();
$this->driver = NULL;
} }
return new DibiResultIterator($this);
} }
/********************* fetching rows ****************d*g**/
/** /**
* Set fetched object class. This class should extend the DibiRow class. * Qualifiy each column name with the table name?
* @param string * @param bool
* @return self * @return void
* @throws DibiException
*/ */
public function setRowClass($class) final public function setWithTables($val)
{ {
$this->rowClass = $class; if ($val) {
return $this; $cols = array();
foreach ($this->getMeta() as $info) {
$name = $info['fullname'];
if (isset($cols[$name])) {
$fix = 1;
while (isset($cols[$name . '#' . $fix])) $fix++;
$name .= '#' . $fix;
}
$cols[$name] = TRUE;
}
$this->withTables = array_keys($cols);
} else {
$this->withTables = FALSE;
}
} }
/** /**
* Returns fetched object class name. * Qualifiy each key with the table name?
* @return string * @return bool
*/ */
public function getRowClass() final public function getWithTables()
{ {
return $this->rowClass; return (bool) $this->withTables;
} }
/** /**
* Fetches the row at current position, process optional type conversion. * Fetches the row at current position, process optional type conversion.
* and moves the internal cursor to the next position * and moves the internal cursor to the next position
@@ -191,55 +180,71 @@ class DibiResult extends DibiObject implements IDataSource
*/ */
final public function fetch() final public function fetch()
{ {
$row = $this->getResultDriver()->fetch(TRUE); if ($this->withTables === FALSE) {
if (!is_array($row)) { $row = $this->getDriver()->fetch(TRUE);
return FALSE; if (!is_array($row)) return FALSE;
} else {
$row = $this->getDriver()->fetch(FALSE);
if (!is_array($row)) return FALSE;
$row = array_combine($this->withTables, $row);
} }
$this->fetched = TRUE; $this->fetched = TRUE;
$this->normalize($row);
if ($this->rowClass) { // types-converting?
$row = new $this->rowClass($row); if ($this->xlat !== NULL) {
foreach ($this->xlat as $col => $type) {
if (isset($row[$col])) {
$row[$col] = $this->convert($row[$col], $type['type'], $type['format']);
}
}
} }
return $row;
return new DibiRow($row, 2);
} }
/** /**
* Like fetch(), but returns only first field. * Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record * @return mixed value on success, FALSE if no next record
*/ */
final public function fetchSingle() final public function fetchSingle()
{ {
$row = $this->getResultDriver()->fetch(TRUE); $row = $this->getDriver()->fetch(TRUE);
if (!is_array($row)) { if (!is_array($row)) return FALSE;
return FALSE;
}
$this->fetched = TRUE; $this->fetched = TRUE;
$this->normalize($row); $value = reset($row);
return reset($row);
// types-converting?
$key = key($row);
if (isset($this->xlat[$key])) {
$type = $this->xlat[$key];
return $this->convert($value, $type['type'], $type['format']);
}
return $value;
} }
/** /**
* Fetches all records from table. * Fetches all records from table.
* @param int offset * @param int offset
* @param int limit * @param int limit
* @return DibiRow[] * @return array of DibiRow
*/ */
final public function fetchAll($offset = NULL, $limit = NULL) final public function fetchAll($offset = NULL, $limit = NULL)
{ {
$limit = $limit === NULL ? -1 : (int) $limit; $limit = $limit === NULL ? -1 : (int) $limit;
$this->seek((int) $offset); $this->seek((int) $offset);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) return array(); // empty result set
return array(); // empty result set
}
$data = array(); $data = array();
do { do {
if ($limit === 0) { if ($limit === 0) break;
break;
}
$limit--; $limit--;
$data[] = $row; $data[] = $row;
} while ($row = $this->fetch()); } while ($row = $this->fetch());
@@ -248,100 +253,32 @@ class DibiResult extends DibiObject implements IDataSource
} }
/** /**
* Fetches all records from table and returns associative tree. * Fetches all records from table and returns associative tree.
* Examples: * Associative descriptor: assoc1,#,assoc2,=,assoc3,@
* - associative descriptor: col1[]col2->col3 * builds a tree: $data[assoc1][index][assoc2]['assoc3']->value = {record}
* builds a tree: $tree[$val1][$index][$val2]->col3[$val3] = {record}
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor * @param string associative descriptor
* @return DibiRow * @return DibiRow
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
final public function fetchAssoc($assoc) final public function fetchAssoc($assoc)
{ {
if (strpos($assoc, ',') !== FALSE) {
return $this->oldFetchAssoc($assoc);
}
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) return array(); // empty result set
return array(); // empty result set
}
$data = NULL; $data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); $assoc = explode(',', $assoc);
// check columns // check columns
foreach ($assoc as $as) { foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts // offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) { if ($as !== '#' && $as !== '=' && $as !== '@' && !isset($row[$as])) {
throw new InvalidArgumentException("Unknown column '$as' in associative descriptor."); throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
} }
} }
if ($as === '->') { // must not be last
array_pop($assoc);
}
if (empty($assoc)) {
$assoc[] = '[]';
}
// make associative tree
do {
$x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node
$x = & $x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i+1]};
continue 2;
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i+1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i+1]};
}
} elseif ($as !== '|') { // associative-array node
$x = & $x[$row->$as];
}
}
if ($x === NULL) { // build leaf
$x = $row;
}
} while ($row = $this->fetch());
unset($x);
return $data;
}
/**
* @deprecated
*/
private function oldFetchAssoc($assoc)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return array(); // empty result set
}
$data = NULL;
$assoc = explode(',', $assoc);
// strip leading = and @ // strip leading = and @
$leaf = '@'; // gap $leaf = '@'; // gap
$last = count($assoc) - 1; $last = count($assoc) - 1;
@@ -356,16 +293,19 @@ class DibiResult extends DibiObject implements IDataSource
} }
} }
// make associative tree
do { do {
$arr = (array) $row;
$x = & $data; $x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) { foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node if ($as === '#') { // indexed-array node
$x = & $x[]; $x = & $x[];
} elseif ($as === '=') { // "record" node } elseif ($as === '=') { // "record" node
if ($x === NULL) { if ($x === NULL) {
$x = $row->toArray(); $x = $arr;
$x = & $x[ $assoc[$i+1] ]; $x = & $x[ $assoc[$i+1] ];
$x = NULL; // prepare child node $x = NULL; // prepare child node
} else { } else {
@@ -383,13 +323,13 @@ class DibiResult extends DibiObject implements IDataSource
} else { // associative-array node } else { // associative-array node
$x = & $x[$row->$as]; $x = & $x[ $arr[ $as ] ];
} }
} }
if ($x === NULL) { // build leaf if ($x === NULL) { // build leaf
if ($leaf === '=') { if ($leaf === '=') {
$x = $row->toArray(); $x = $arr;
} else { } else {
$x = $row; $x = $row;
} }
@@ -402,6 +342,7 @@ class DibiResult extends DibiObject implements IDataSource
} }
/** /**
* Fetches all records from table like $key => $value pairs. * Fetches all records from table like $key => $value pairs.
* @param string associative key * @param string associative key
@@ -413,9 +354,7 @@ class DibiResult extends DibiObject implements IDataSource
{ {
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) return array(); // empty result set
return array(); // empty result set
}
$data = array(); $data = array();
@@ -425,7 +364,7 @@ class DibiResult extends DibiObject implements IDataSource
} }
// autodetect // autodetect
$tmp = array_keys($row->toArray()); $tmp = array_keys((array) $row);
$key = $tmp[0]; $key = $tmp[0];
if (count($row) < 2) { // indexed-array if (count($row) < 2) { // indexed-array
do { do {
@@ -437,7 +376,7 @@ class DibiResult extends DibiObject implements IDataSource
$value = $tmp[1]; $value = $tmp[1];
} else { } else {
if (!property_exists($row, $value)) { if (!isset($row[$value])) {
throw new InvalidArgumentException("Unknown value column '$value'."); throw new InvalidArgumentException("Unknown value column '$value'.");
} }
@@ -448,7 +387,7 @@ class DibiResult extends DibiObject implements IDataSource
return $data; return $data;
} }
if (!property_exists($row, $key)) { if (!isset($row[$key])) {
throw new InvalidArgumentException("Unknown key column '$key'."); throw new InvalidArgumentException("Unknown key column '$key'.");
} }
} }
@@ -461,155 +400,128 @@ class DibiResult extends DibiObject implements IDataSource
} }
/********************* column types ****************d*g**/
/**
* Define column type.
* @param string column
* @param string type (use constant Dibi::FIELD_*)
* @param string optional format
* @return void
*/
final public function setType($col, $type, $format = NULL)
{
$this->xlat[$col] = array('type' => $type, 'format' => $format);
}
/** /**
* Autodetect column types. * Autodetect column types.
* @return void * @return void
*/ */
private function detectTypes() final public function detectTypes()
{ {
$cache = DibiColumnInfo::getTypeCache(); foreach ($this->getMeta() as $info) {
try { $this->xlat[$info['name']] = array('type' => $info['type'], 'format' => NULL);
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = $cache->{$col['nativetype']};
}
} catch (DibiNotSupportedException $e) {}
}
/**
* Converts values to specified type and format.
* @param array
* @return void
*/
private function normalize(array & $row)
{
foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL
continue;
}
$value = $row[$key];
if ($value === FALSE || $type === dibi::TEXT) {
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
} elseif ($type === dibi::FLOAT) {
$row[$key] = ltrim((string) ($tmp = (float) $value), '0') === ltrim(rtrim(rtrim($value, '0'), '.'), '0') ? $tmp : $value;
} elseif ($type === dibi::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
if ((int) $value === 0 && substr((string) $value, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
} elseif (empty($this->formats[$type])) { // return DateTime object (default)
$row[$key] = new DibiDateTime(is_numeric($value) ? date('Y-m-d H:i:s', $value) : $value);
} elseif ($this->formats[$type] === 'U') { // return timestamp
$row[$key] = is_numeric($value) ? (int) $value : strtotime($value);
} elseif (is_numeric($value)) { // formatted date
$row[$key] = date($this->formats[$type], $value);
} else {
$value = new DibiDateTime($value);
$row[$key] = $value->format($this->formats[$type]);
}
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
}
} }
} }
/** /**
* Define column type. * Define multiple columns types.
* @param string column * @param array
* @param string type (use constant Dibi::*) * @return void
* @return self * @internal
*/ */
final public function setType($col, $type) final public function setTypes(array $types)
{ {
$this->types[$col] = $type; $this->xlat = $types;
return $this;
} }
/** /**
* Returns column type. * Returns column type.
* @return string * @return array ($type, $format)
*/ */
final public function getType($col) final public function getType($col)
{ {
return isset($this->types[$col]) ? $this->types[$col] : NULL; return isset($this->xlat[$col]) ? $this->xlat[$col] : NULL;
} }
/**
* Sets data format.
* @param string type (use constant Dibi::*)
* @param string format
* @return self
*/
final public function setFormat($type, $format)
{
$this->formats[$type] = $format;
return $this;
}
/** /**
* Returns data format. * Converts value to specified type and format
* @return string * @return array ($type, $format)
*/ */
final public function getFormat($type) final public function convert($value, $type, $format = NULL)
{ {
return isset($this->formats[$type]) ? $this->formats[$type] : NULL; if ($value === NULL || $value === FALSE) {
} return $value;
}
/********************* meta info ****************d*g**/ switch ($type) {
case dibi::FIELD_TEXT:
return (string) $value;
/**
* Returns a meta information about the current result set. case dibi::FIELD_BINARY:
* @return DibiResultInfo return $this->getDriver()->unescape($value, $type);
*/
public function getInfo() case dibi::FIELD_INTEGER:
{ return (int) $value;
if ($this->meta === NULL) {
$this->meta = new DibiResultInfo($this->getResultDriver()); case dibi::FIELD_FLOAT:
return (float) $value;
case dibi::FIELD_DATE:
case dibi::FIELD_DATETIME:
$value = strtotime($value);
return $format === NULL ? $value : date($format, $value);
case dibi::FIELD_BOOL:
return ((bool) $value) && $value !== 'f' && $value !== 'F';
default:
return $value;
} }
return $this->meta;
} }
/** /**
* @deprecated * Gets an array of meta informations about columns.
* @return array of DibiColumnInfo
*/ */
final public function getColumns() final public function getColumns()
{ {
return $this->getInfo()->getColumns(); $cols = array();
foreach ($this->getMeta() as $info) {
$cols[] = new DibiColumnInfo($this->driver, $info);
}
return $cols;
} }
/** @deprecated */
public function getColumnNames($fullNames = FALSE)
{
trigger_error(__METHOD__ . '() is deprecated; use $res->getInfo()->getColumnNames() instead.', E_USER_WARNING);
return $this->getInfo()->getColumnNames($fullNames);
}
/********************* misc tools ****************d*g**/
/** /**
* Displays complete result set as HTML table for debug purposes. * @param bool
* @return array of string
*/
public function getColumnNames($withTables = FALSE)
{
$cols = array();
foreach ($this->getMeta() as $info) {
$cols[] = $info[$withTables ? 'fullname' : 'name'];
}
return $cols;
}
/**
* Displays complete result-set as HTML table for debug purposes.
* @return void * @return void
*/ */
final public function dump() final public function dump()
@@ -643,4 +555,75 @@ class DibiResult extends DibiObject implements IDataSource
} }
} }
/**
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return ArrayIterator
*/
final public function getIterator($offset = NULL, $limit = NULL)
{
return new ArrayIterator($this->fetchAll($offset, $limit));
}
/**
* Required by the Countable interface.
* @return int
*/
final public function count()
{
return $this->rowCount();
}
/**
* Safe access to property $driver.
* @return IDibiDriver
* @throws InvalidStateException
*/
private function getDriver()
{
if ($this->driver === NULL) {
throw new InvalidStateException('Resultset was released from memory.');
}
return $this->driver;
}
/**
* Meta lazy initialization.
* @return array
*/
private function getMeta()
{
if ($this->meta === NULL) {
$this->meta = $this->getDriver()->getColumnsMeta();
foreach ($this->meta as & $row) {
$row['type'] = DibiColumnInfo::detectType($row['nativetype']);
}
}
return $this->meta;
}
}
/**
* dibi result-set row
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/
class DibiRow extends ArrayObject
{
} }

View File

@@ -1,107 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* External result set iterator.
*
* This can be returned by DibiResult::getIterator() method or using foreach
* <code>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $row) {
* print_r($row);
* }
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*/
class DibiResultIterator implements Iterator, Countable
{
/** @var DibiResult */
private $result;
/** @var int */
private $row;
/** @var int */
private $pointer;
/**
* @param DibiResult
*/
public function __construct(DibiResult $result)
{
$this->result = $result;
}
/**
* Rewinds the iterator to the first element.
* @return void
*/
public function rewind()
{
$this->pointer = 0;
$this->result->seek(0);
$this->row = $this->result->fetch();
}
/**
* Returns the key of the current element.
* @return mixed
*/
public function key()
{
return $this->pointer;
}
/**
* Returns the current element.
* @return mixed
*/
public function current()
{
return $this->row;
}
/**
* Moves forward to next element.
* @return void
*/
public function next()
{
$this->row = $this->result->fetch();
$this->pointer++;
}
/**
* Checks if there is a current element after calls to rewind() or next().
* @return bool
*/
public function valid()
{
return !empty($this->row);
}
/**
* Required by the Countable interface.
* @return int
*/
public function count()
{
return $this->result->getRowCount();
}
}

View File

@@ -1,125 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*/
/**
* Result set single row.
*
* @author David Grudl
* @package dibi
*/
class DibiRow implements ArrayAccess, IteratorAggregate, Countable
{
public function __construct($arr)
{
foreach ($arr as $k => $v) {
$this->$k = $v;
}
}
public function toArray()
{
return (array) $this;
}
/**
* Converts value to DateTime object.
* @param string key
* @param string format
* @return DateTime
*/
public function asDateTime($key, $format = NULL)
{
$time = $this[$key];
if (!$time instanceof DibiDateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
return NULL;
}
$time = new DibiDateTime(is_numeric($time) ? date('Y-m-d H:i:s', $time) : $time);
}
return $format === NULL ? $time : $time->format($format);
}
/**
* Converts value to UNIX timestamp.
* @param string key
* @return int
*/
public function asTimestamp($key)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
return $this->asDateTime($key, 'U');
}
/**
* Converts value to boolean.
* @param string key
* @return mixed
*/
public function asBool($key)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
return $this[$key];
}
/** @deprecated */
public function asDate($key, $format = NULL)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_WARNING);
if ($format === NULL) {
return $this->asTimestamp($key);
} else {
return $this->asDateTime($key, $format === TRUE ? NULL : $format);
}
}
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
final public function count()
{
return count((array) $this);
}
final public function getIterator()
{
return new ArrayIterator($this);
}
final public function offsetSet($nm, $val)
{
$this->$nm = $val;
}
final public function offsetGet($nm)
{
return $this->$nm;
}
final public function offsetExists($nm)
{
return isset($this->$nm);
}
final public function offsetUnset($nm)
{
unset($this->$nm);
}
}

420
dibi/libs/DibiTableX.php Normal file
View File

@@ -0,0 +1,420 @@
<?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$
*/
/**
* Experimental object-oriented interface to database tables.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi
*/
abstract class DibiTableX extends DibiObject
{
/** @var string primary key mask */
public static $primaryMask = 'id';
/** @var bool */
public static $lowerCase = TRUE;
/** @var DibiConnection */
private $connection;
/** @var string table name */
protected $name;
/** @var string primary key name */
protected $primary;
/** @var string primary key type */
protected $primaryModifier = '%i';
/** @var bool primary key is auto increment */
protected $primaryAutoIncrement = TRUE;
/** @var array */
protected $blankRow = array();
/** @var mixed; TRUE means autodetect, or array of pairs [type, format] */
protected $types = array();
/**
* Table constructor.
* @param DibiConnection
* @return void
*/
public function __construct(DibiConnection $connection = NULL)
{
$this->connection = $connection === NULL ? dibi::getConnection() : $connection;
$this->setup();
}
/**
* Returns the table name.
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns the primary key name.
* @return string
*/
public function getPrimary()
{
return $this->primary;
}
/**
* Returns the dibi connection.
* @return DibiConnection
*/
public function getConnection()
{
return $this->connection;
}
/**
* Setup object.
* @return void
*/
protected function setup()
{
// autodetect table name
if ($this->name === NULL) {
$name = $this->getClass();
if (FALSE !== ($pos = strrpos($name, ':'))) {
$name = substr($name, $pos + 1);
}
if (self::$lowerCase) {
$name = strtolower($name);
}
$this->name = $name;
}
// autodetect primary key name
if ($this->primary === NULL) {
$this->primary = str_replace(
array('%p', '%s'),
array($this->name, trim($this->name, 's')), // the simplest inflector in the world :-))
self::$primaryMask
);
}
}
/********************* basic commands ****************d*g**/
/**
* Inserts row into a table.
* @param array|object
* @return int new primary key
*/
public function insert($data)
{
$this->connection->query(
'INSERT INTO %n', $this->name, '%v', $this->prepare($data)
);
return $this->primaryAutoIncrement ? $this->connection->insertId() : NULL;
}
/**
* Updates rows in a table.
* @param mixed primary key value(s)
* @param array|object
* @return int number of updated rows
*/
public function update($where, $data)
{
$data = $this->prepare($data);
if ($where === NULL && isset($data[$this->primary])) {;
$where = $data[$this->primary];
unset($data[$this->primary]);
}
$this->connection->query(
'UPDATE %n', $this->name,
'SET %a', $data,
'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')'
);
return $this->connection->affectedRows();
}
/**
* Inserts or updates rows in a table.
* @param array|object
* @return int (new) primary key
*/
public function insertOrUpdate($data)
{
$data = $this->prepare($data);
if (!isset($data[$this->primary])) {
throw new InvalidArgumentException("Missing primary key '$this->primary' in dataset.");
}
try {
$this->connection->query(
'INSERT INTO %n', $this->name, '%v', $data
);
} catch (DibiDriverException $e) {
$where = $data[$this->primary];
unset($data[$this->primary]);
$this->connection->query(
'UPDATE %n', $this->name,
'SET %a', $data,
'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')'
);
}
}
/**
* Deletes rows from a table by primary key.
* @param mixed primary key value(s)
* @return int number of deleted rows
*/
public function delete($where)
{
$this->connection->query(
'DELETE FROM %n', $this->name,
'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $where, ')'
);
return $this->connection->affectedRows();
}
/**
* Finds rows by primary key.
* @param mixed primary key value(s)
* @return DibiResult
*/
public function find($what)
{
if (!is_array($what)) {
$what = func_get_args();
}
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'WHERE %n', $this->primary, 'IN (' . $this->primaryModifier, $what, ')'
));
}
/**
* Selects all rows.
* @param array conditions
* @param string|array column to order by
* @return DibiResult
*/
public function findAll($conditions = NULL, $order = NULL)
{
if (!is_array($order)) {
$order = func_get_args();
if (is_array($conditions)) {
array_shift($order);
} else {
$conditions = NULL;
}
}
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'%ex', $conditions ? array('WHERE %and', $conditions) : NULL,
'%ex', $order ? array('ORDER BY %by', $order) : NULL
));
}
/**
* Fetches single row.
* @param scalar|array primary key value
* @return DibiRow
*/
public function fetch($conditions)
{
if (is_array($conditions)) {
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'WHERE %and', $conditions
))->fetch();
}
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'WHERE %n=' . $this->primaryModifier, $this->primary, $conditions
))->fetch();
}
/**
* Returns a blank row (not fetched from database).
* @return DibiRow
*/
public function createBlank()
{
$row = new DibiRow($this->blankRow, 2);
$row[$this->primary] = NULL;
return $row;
}
/**
* User data pre-processing.
* @param array|object
* @return array
*/
protected function prepare($data)
{
if (is_object($data)) {
return (array) $data;
} elseif (is_array($data)) {
return $data;
}
throw new InvalidArgumentException('Dataset must be array or anonymous object.');
}
/**
* User DibiResult post-processing.
* @param DibiResult
* @return DibiResult
*/
protected function complete($res)
{
if (is_array($this->types)) {
$res->setTypes($this->types);
} elseif ($this->types === TRUE) {
$res->detectTypes();
}
return $res;
}
/********************* fluent SQL builders ****************d*g**/
/**
* Creates fluent SQL builder.
* @return DibiFluent
*/
public function command()
{
return new DibiFluent($this->connection);
}
/**
* @param string column name
* @return DibiFluent
*/
public function select($args)
{
$args = func_get_args();
return $this->command()->__call('select', $args)->from($this->name);
}
/********************* magic fetching ****************d*g**/
/**
* Magic fetch.
* - $row = $model->fetchByUrl('about-us');
* - $arr = $model->fetchAllByCategoryIdAndVisibility(5, TRUE);
*
* @param string
* @param array
* @return DibiRow|array
*/
public function __call($name, $args)
{
if (strncmp($name, 'fetchBy', 7) === 0) { // single row
$single = TRUE;
$name = substr($name, 7);
} elseif (strncmp($name, 'fetchAllBy', 10) === 0) { // multi row
$name = substr($name, 10);
} else {
parent::__call($name, $args);
}
// ProductIdAndTitle -> array('product', 'title')
$parts = explode('_and_', strtolower(preg_replace('#(.)(?=[A-Z])#', '$1_', $name)));
if (count($parts) !== count($args)) {
throw new InvalidArgumentException("Magic fetch expects " . count($parts) . " parameters, but " . count($args) . " was given.");
}
if (isset($single)) {
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'WHERE %and', array_combine($parts, $args),
'LIMIT 1'
))->fetch();
} else {
return $this->complete($this->connection->query(
'SELECT * FROM %n', $this->name,
'WHERE %and', array_combine($parts, $args)
))->fetchAll();
}
}
}
abstract class DibiTable extends DibiTableX
{
}

View File

@@ -1,21 +1,36 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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 SQL translator. * dibi SQL translator.
* *
* @author David Grudl * @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*/ */
final class DibiTranslator extends DibiObject final class DibiTranslator extends DibiObject
{ {
/** @var DibiConnection */ /** @var string */
private $connection; public $sql;
/** @var IDibiDriver */ /** @var IDibiDriver */
private $driver; private $driver;
@@ -44,33 +59,32 @@ final class DibiTranslator extends DibiObject
/** @var int */ /** @var int */
private $offset; private $offset;
/** @var DibiHashMap */
private $identifiers;
public function __construct(DibiConnection $connection) public function __construct(IDibiDriver $driver)
{ {
$this->connection = $connection; $this->driver = $driver;
} }
/**
* return IDibiDriver.
*/
public function getDriver()
{
return $this->driver;
}
/** /**
* Generates SQL. * Generates SQL.
* @param array * @param array
* @return string * @return bool
* @throws DibiException
*/ */
public function translate(array $args) public function translate(array $args)
{ {
$this->identifiers = new DibiHashMap(array($this, 'delimite'));
$this->driver = $this->connection->getDriver();
$args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]);
}
$this->args = $args;
$this->limit = -1; $this->limit = -1;
$this->offset = 0; $this->offset = 0;
$this->hasError = FALSE; $this->hasError = FALSE;
@@ -79,6 +93,8 @@ final class DibiTranslator extends DibiObject
// shortcuts // shortcuts
$cursor = & $this->cursor; $cursor = & $this->cursor;
$cursor = 0; $cursor = 0;
$this->args = array_values($args);
$args = & $this->args;
// conditional sql // conditional sql
$this->ifLevel = $this->ifLevelStart = 0; $this->ifLevel = $this->ifLevelStart = 0;
@@ -87,40 +103,37 @@ final class DibiTranslator extends DibiObject
// iterate // iterate
$sql = array(); $sql = array();
while ($cursor < count($this->args)) { while ($cursor < count($args))
$arg = $this->args[$cursor]; {
$arg = $args[$cursor];
$cursor++; $cursor++;
// simple string means SQL // simple string means SQL
if (is_string($arg)) { if (is_string($arg)) {
// speed-up - is regexp required? // speed-up - is regexp required?
$toSkip = strcspn($arg, '`[\'":%?'); $toSkip = strcspn($arg, '`[\'"%');
if (strlen($arg) === $toSkip) { // needn't be translated if (strlen($arg) === $toSkip) { // needn't be translated
$sql[] = $arg; $sql[] = $arg;
} else { } else {
$sql[] = substr($arg, 0, $toSkip) $sql[] = substr($arg, 0, $toSkip)
/* /*
. preg_replace_callback('/ preg_replace_callback('/
(?=[`[\'":%?]) ## speed-up (?=`|\[|\'|"|%) ## speed-up
(?: (?:
`(.+?)`| ## 1) `identifier` `(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier] \[(.+?)\]| ## 2) [identifier]
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string' (\')((?:\'\'|[^\'])*)\'| ## 3,4) string
(")((?:""|[^"])*)"| ## 5,6) "string" (")((?:""|[^"])*)"| ## 5,6) "string"
(\'|")| ## 7) lone quote (\'|") ## 7) lone-quote
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution: %([a-zA-Z]{1,4})(?![a-zA-Z])|## 8) modifier
%([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
(\?) ## 11) placeholder
)/xs', )/xs',
*/ // note: this can change $this->args & $this->cursor & ... */ // note: this can change $this->args & $this->cursor & ...
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s', . preg_replace_callback('/(?=`|\[|\'|"|%)(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|%([a-zA-Z]{1,4})(?![a-zA-Z]))/s',
array($this, 'cb'), array($this, 'cb'),
substr($arg, $toSkip) substr($arg, $toSkip)
); );
if (preg_last_error()) {
throw new DibiPcreException;
}
} }
continue; continue;
} }
@@ -130,25 +143,25 @@ final class DibiTranslator extends DibiObject
continue; continue;
} }
if ($arg instanceof Traversable) {
$arg = iterator_to_array($arg);
}
if (is_array($arg)) { if (is_array($arg)) {
if (is_string(key($arg))) { if (is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST // associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) { if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6)); $commandIns = strtoupper(substr(ltrim($args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC'; $commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a'); $sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else { } else {
if ($lastArr === $cursor - 1) { if ($lastArr === $cursor - 1) $sql[] = ',';
$sql[] = ',';
}
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a'); $sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
} }
$lastArr = $cursor; $lastArr = $cursor;
continue; continue;
} elseif ($cursor === 1) {
// implicit array expansion
$cursor = 0;
array_splice($args, 0, 1, $arg);
continue;
} }
} }
@@ -157,25 +170,21 @@ final class DibiTranslator extends DibiObject
} // while } // while
if ($comment) { if ($comment) $sql[] = "*/";
$sql[] = "*/";
}
$sql = implode(' ', $sql); $sql = implode(' ', $sql);
if ($this->hasError) {
throw new DibiException('SQL translate error', 0, $sql);
}
// apply limit // apply limit
if ($this->limit > -1 || $this->offset > 0) { if ($this->limit > -1 || $this->offset > 0) {
$this->driver->applyLimit($sql, $this->limit, $this->offset); $this->driver->applyLimit($sql, $this->limit, $this->offset);
} }
return $sql; $this->sql = $sql;
return !$this->hasError;
} }
/** /**
* Apply modifier to single value. * Apply modifier to single value.
* @param mixed * @param mixed
@@ -184,280 +193,177 @@ final class DibiTranslator extends DibiObject
*/ */
public function formatValue($value, $modifier) public function formatValue($value, $modifier)
{ {
if ($this->comment) {
return "...";
}
// array processing (with or without modifier) // array processing (with or without modifier)
if ($value instanceof Traversable) { if (is_array($value) || $value instanceof ArrayObject) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
$vx = $kx = array(); $vx = $kx = array();
$operator = ', ';
switch ($modifier) { switch ($modifier) {
case 'and': case 'and':
case 'or': // key=val AND key IS NULL AND ... case 'or': // key=val AND key IS NULL AND ...
if (empty($value)) { $operator = ' ' . strtoupper($modifier) . ' ';
return '1=1'; if (empty($value)) {
} return '1';
foreach ($value as $k => $v) { } elseif (!is_string(key($value))) {
if (is_string($k)) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$k = $this->identifiers->{$pair[0]} . ' ';
if (!isset($pair[1])) {
$v = $this->formatValue($v, FALSE);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
} elseif ($pair[1] === 'ex') { // TODO: this will be removed
$vx[] = $k . $this->formatValue($v, 'ex');
} else {
$v = $this->formatValue($v, $pair[1]);
if ($pair[1] === 'l' || $pair[1] === 'in') {
$op = 'IN ';
} elseif (strpos($pair[1], 'like') !== FALSE) {
$op = 'LIKE ';
} elseif ($v === 'NULL') {
$op = 'IS ';
} else {
$op = '= ';
}
$vx[] = $k . $op . $v;
}
} else {
$vx[] = $this->formatValue($v, 'ex');
}
}
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) {
if (is_string($k)) {
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]};
}
}
return implode(', ', $vx);
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]} . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return implode(', ', $vx);
case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
case 'v': // (key, key, ...) VALUES (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
foreach ($value as $k => $v) {
if (is_array($v)) {
if (isset($proto)) {
if ($proto !== array_keys($v)) {
$this->hasError = TRUE;
return '**Multi-insert array "' . $k . '" is different.**';
}
} else {
$proto = array_keys($v);
}
} else {
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($v) . '**';
}
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
foreach ($v as $k2 => $v2) {
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE));
}
}
foreach ($vx as $k => $v) {
$vx[$k] = '(' . implode(', ', $v) . ')';
}
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
case 'by': // key ASC, key DESC
foreach ($value as $k => $v) {
if (is_array($v)) {
$vx[] = $this->formatValue($v, 'ex');
} elseif (is_string($k)) {
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
$vx[] = $this->identifiers->$k . ' ' . $v;
} else {
$vx[] = $this->identifiers->$v;
}
}
return implode(', ', $vx);
case 'ex':
case 'sql':
$translator = new self($this->connection);
return $translator->translate($value);
default: // value, value, value - all with the same modifier
foreach ($value as $v) { foreach ($value as $v) {
$vx[] = $this->formatValue($v, $modifier); $vx[] = $this->formatValue($v, 'sql');
} }
return implode(', ', $vx);
} else {
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$k = $this->delimite($pair[0]);
$v = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE);
$op = isset($pair[1]) && $pair[1] === 'l' ? 'IN' : ($v === 'NULL' ? 'IS' : '=');
$vx[] = $k . ' ' . $op . ' ' . $v;
}
}
return implode($operator, $vx);
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->delimite($pair[0]) . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE);
}
return implode($operator, $vx);
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE);
}
return '(' . implode(', ', $vx) . ')';
case 'v': // (key, key, ...) VALUES (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->delimite($pair[0]);
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : FALSE);
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
case 'by': // key ASC, key DESC
foreach ($value as $k => $v) {
if (is_string($k)) {
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
$vx[] = $this->delimite($k) . ' ' . $v;
} else {
$vx[] = $this->delimite($v);
}
}
return implode(', ', $vx);
default: // value, value, value - all with the same modifier
foreach ($value as $v) {
$vx[] = $this->formatValue($v, $modifier);
}
return implode(', ', $vx);
} }
} }
// with modifier procession // with modifier procession
if ($modifier) { if ($modifier) {
if ($value !== NULL && !is_scalar($value) && !($value instanceof DateTime)) { // array is already processed if ($value === NULL) {
return 'NULL';
}
if ($value instanceof IDibiVariable) {
return $value->toSql($this, $modifier);
}
if (!is_scalar($value)) { // array is already processed
$this->hasError = TRUE; $this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**'; return '**Unexpected type ' . gettype($value) . '**';
} }
switch ($modifier) { switch ($modifier) {
case 's': // string case 's': // string
case 'bin':// binary case 'bin':// binary
case 'b': // boolean case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier); return $this->driver->escape($value, $modifier);
case 'sN': // string or NULL case 'sn': // string or NULL
case 'sn': return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::FIELD_TEXT); // notice two equal signs
return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
case 'iN': // signed int or NULL case 'i': // signed int
case 'in': // deprecated case 'u': // unsigned int, ignored
if ($value == '') { // support for long numbers - keep them unchanged
$value = NULL; if (is_string($value) && preg_match('#[+-]?\d+(e\d+)?$#A', $value)) {
} return $value;
// intentionally break omitted }
return (string) (int) ($value + 0);
case 'i': // signed int case 'f': // float
case 'u': // unsigned int, ignored // support for extreme numbers - keep them unchanged
// support for long numbers - keep them unchanged if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) {
if (is_string($value) && preg_match('#[+-]?\d++(e\d+)?\z#A', $value)) { return $value; // something like -9E-005 is accepted by SQL, HEX values are not
return $value; }
} else { return rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
}
case 'f': // float case 'd': // date
// support for extreme numbers - keep them unchanged case 't': // datetime
if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) { return $this->driver->escape(is_string($value) ? strtotime($value) : $value, $modifier);
return $value; // something like -9E-005 is accepted by SQL, HEX values are not
} else {
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
}
case 'd': // date case 'by':
case 't': // datetime case 'n': // identifier name
if ($value === NULL) { return $this->delimite($value);
return 'NULL';
} else {
if (is_numeric($value)) {
$value = (int) $value; // timestamp
} elseif (is_string($value)) { case 'sql':// preserve as SQL
$value = new DateTime($value); $value = (string) $value;
} // speed-up - is regexp required?
return $this->driver->escape($value, $modifier); $toSkip = strcspn($value, '`[\'"');
} if (strlen($value) === $toSkip) { // needn't be translated
return $value;
case 'by': } else {
case 'n': // identifier name return substr($value, 0, $toSkip)
return $this->identifiers->$value; . preg_replace_callback('/(?=`|\[|\'|")(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"(\'|"))/s',
case 'ex':
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
$value = (string) $value;
// speed-up - is regexp required?
$toSkip = strcspn($value, '`[\'":');
if (strlen($value) !== $toSkip) {
$value = substr($value, 0, $toSkip)
. preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
array($this, 'cb'), array($this, 'cb'),
substr($value, $toSkip) substr($value, $toSkip)
); );
if (preg_last_error()) { }
throw new DibiPcreException;
}
}
return $value;
case 'SQL': // preserve as real SQL (TODO: rename to %sql) case 'and':
return (string) $value; case 'or':
case 'a':
case 'l':
case 'v':
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
case 'like~': // LIKE string% default:
return $this->driver->escapeLike($value, 1); $this->hasError = TRUE;
return "**Unknown or invalid modifier %$modifier**";
case '~like': // LIKE %string
return $this->driver->escapeLike($value, -1);
case '~like~': // LIKE %string%
return $this->driver->escapeLike($value, 0);
case 'and':
case 'or':
case 'a':
case 'l':
case 'v':
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
default:
$this->hasError = TRUE;
return "**Unknown or invalid modifier %$modifier**";
} }
} }
// without modifier procession // without modifier procession
if (is_string($value)) { if (is_string($value))
return $this->driver->escape($value, dibi::TEXT); return $this->driver->escape($value, dibi::FIELD_TEXT);
} elseif (is_int($value)) { if (is_int($value) || is_float($value))
return (string) $value; return rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
} elseif (is_float($value)) { if (is_bool($value))
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); return $this->driver->escape($value, dibi::FIELD_BOOL);
} elseif (is_bool($value)) { if ($value === NULL)
return $this->driver->escape($value, dibi::BOOL);
} elseif ($value === NULL) {
return 'NULL'; return 'NULL';
} elseif ($value instanceof DateTime) { if ($value instanceof IDibiVariable)
return $this->driver->escape($value, dibi::DATETIME); return $value->toSql($this, NULL);
} elseif ($value instanceof DibiLiteral) { $this->hasError = TRUE;
return (string) $value; return '**Unexpected ' . gettype($value) . '**';
} else {
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
}
} }
/** /**
* PREG callback from translate() or formatValue(). * PREG callback from translate() or formatValue().
* @param array * @param array
@@ -472,26 +378,10 @@ final class DibiTranslator extends DibiObject
// [5] => " // [5] => "
// [6] => string // [6] => string
// [7] => lone-quote // [7] => lone-quote
// [8] => substitution // [8] => modifier (when called from self::translate())
// [9] => substitution flag
// [10] => modifier (when called from self::translate())
// [11] => placeholder (when called from self::translate())
if (!empty($matches[8])) { // modifier
if (!empty($matches[11])) { // placeholder $mod = $matches[8];
$cursor = & $this->cursor;
if ($cursor >= count($this->args)) {
$this->hasError = TRUE;
return "**Extra placeholder**";
}
$cursor++;
return $this->formatValue($this->args[$cursor - 1], FALSE);
}
if (!empty($matches[10])) { // modifier
$mod = $matches[10];
$cursor = & $this->cursor; $cursor = & $this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') { if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
@@ -536,16 +426,12 @@ final class DibiTranslator extends DibiObject
return ''; return '';
} elseif ($mod === 'lmt') { // apply limit } elseif ($mod === 'lmt') { // apply limit
if ($this->args[$cursor] !== NULL) { if ($this->args[$cursor] !== NULL) $this->limit = (int) $this->args[$cursor];
$this->limit = (int) $this->args[$cursor];
}
$cursor++; $cursor++;
return ''; return '';
} elseif ($mod === 'ofs') { // apply offset } elseif ($mod === 'ofs') { // apply offset
if ($this->args[$cursor] !== NULL) { if ($this->args[$cursor] !== NULL) $this->offset = (int) $this->args[$cursor];
$this->offset = (int) $this->args[$cursor];
}
$cursor++; $cursor++;
return ''; return '';
@@ -555,53 +441,39 @@ final class DibiTranslator extends DibiObject
} }
} }
if ($this->comment) { if ($this->comment) return '...';
return '...';
}
if ($matches[1]) { // SQL identifiers: `ident` if ($matches[1]) // SQL identifiers: `ident`
return $this->identifiers->{$matches[1]}; return $this->delimite($matches[1]);
} elseif ($matches[2]) { // SQL identifiers: [ident] if ($matches[2]) // SQL identifiers: [ident]
return $this->identifiers->{$matches[2]}; return $this->delimite($matches[2]);
} elseif ($matches[3]) { // SQL strings: '...' if ($matches[3]) // SQL strings: '...'
return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::TEXT); return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::FIELD_TEXT);
} elseif ($matches[5]) { // SQL strings: "..." if ($matches[5]) // SQL strings: "..."
return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::TEXT); return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::FIELD_TEXT);
} elseif ($matches[7]) { // string quote if ($matches[7]) { // string quote
$this->hasError = TRUE; $this->hasError = TRUE;
return '**Alone quote**'; return '**Alone quote**';
} }
if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1);
$m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
}
die('this should be never executed'); die('this should be never executed');
} }
/** /**
* Apply substitutions to indentifier and delimites it. * Apply substitutions to indentifier and delimites it.
* @param string indentifier * @param string indentifier
* @return string * @return string
* @internal
*/ */
public function delimite($value) private function delimite($value)
{ {
$value = $this->connection->substitute($value); return $this->driver->escape(dibi::substitute($value), dibi::IDENTIFIER);
$parts = explode('.', $value);
foreach ($parts as & $v) {
if ($v !== '*') {
$v = $this->driver->escape($v, dibi::IDENTIFIER);
}
}
return implode('.', $parts);
} }
}
} // class DibiTranslator

View File

@@ -0,0 +1,49 @@
<?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$
*/
/**
* Default implemenation of IDibiVariable.
* @package dibi
*/
class DibiVariable extends DibiObject implements IDibiVariable
{
/** @var mixed */
public $value;
/** @var string */
public $modifier;
public function __construct($value, $modifier)
{
$this->value = $value;
$this->modifier = $modifier;
}
public function toSql(DibiTranslator $translator, $modifier)
{
return $translator->formatValue($this->value, $this->modifier);
}
}

View File

@@ -1,14 +1,47 @@
<?php <?php
/** /**
* This file is part of the "dibi" - smart database abstraction layer. * dibi - tiny'n'smart database abstraction layer
* Copyright (c) 2005 David Grudl (http://davidgrudl.com) * ----------------------------------------------
*
* 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$
*/ */
/**
* Interface for user variable, used for generating SQL.
* @package dibi
*/
interface IDibiVariable
{
/**
* Format for SQL.
* @param DibiTranslator
* @param string optional modifier
* @return string SQL code
*/
function toSql(DibiTranslator $translator, $modifier);
}
/** /**
* Provides an interface between a dataset and data-aware components. * Provides an interface between a dataset and data-aware components.
* @package dibi * @package dibi
*/ */
interface IDataSource extends Countable, IteratorAggregate interface IDataSource extends Countable, IteratorAggregate
{ {
@@ -17,8 +50,65 @@ 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. * dibi driver interface.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2008 David Grudl
* @package dibi * @package dibi
*/ */
interface IDibiDriver interface IDibiDriver
@@ -30,7 +120,9 @@ interface IDibiDriver
* @return void * @return void
* @throws DibiException * @throws DibiException
*/ */
function connect(array & $config); function connect(array &$config);
/** /**
* Disconnects from a database. * Disconnects from a database.
@@ -39,49 +131,60 @@ interface IDibiDriver
*/ */
function disconnect(); function disconnect();
/** /**
* Internal: Executes the SQL query. * Internal: Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return IDibiResultDriver|NULL * @return IDibiDriver|NULL
* @throws DibiDriverException * @throws DibiDriverException
*/ */
function query($sql); function query($sql);
/** /**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error * @return int|FALSE number of rows or FALSE on error
*/ */
function getAffectedRows(); function affectedRows();
/** /**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure * @return int|FALSE int on success or FALSE on failure
*/ */
function getInsertId($sequence); function insertId($sequence);
/** /**
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
function begin($savepoint = NULL); function begin();
/** /**
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
function commit($savepoint = NULL); function commit();
/** /**
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name
* @return void * @return void
* @throws DibiDriverException * @throws DibiDriverException
*/ */
function rollback($savepoint = NULL); function rollback();
/** /**
* Returns the connection resource. * Returns the connection resource.
@@ -89,50 +192,56 @@ interface IDibiDriver
*/ */
function getResource(); function getResource();
/**
* Returns the connection reflector.
* @return IDibiReflector /********************* SQL ****************d*g**/
*/
function getReflector();
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in an SQL statement.
* @param string value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...) * @param string type (dibi::FIELD_TEXT, dibi::FIELD_BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException * @throws InvalidArgumentException
*/ */
function escape($value, $type); function escape($value, $type);
/** /**
* Encodes string for use in a LIKE statement. * Decodes data from result set.
* @param string * @param string value
* @param int * @param string type (dibi::FIELD_BINARY)
* @return string * @return string decoded value
* @throws InvalidArgumentException
*/ */
function escapeLike($value, $pos); function unescape($value, $type);
/** /**
* Injects LIMIT/OFFSET to the SQL query. * Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void * @return void
*/ */
function applyLimit(& $sql, $limit, $offset); function applyLimit(&$sql, $limit, $offset);
}
/********************* result set ****************d*g**/
/**
* dibi result set driver interface.
* @package dibi
*/
interface IDibiResultDriver
{
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
*/ */
function getRowCount(); function rowCount();
/** /**
* Moves cursor position without fetching row. * Moves cursor position without fetching row.
@@ -142,6 +251,8 @@ interface IDibiResultDriver
*/ */
function seek($row); function seek($row);
/** /**
* Fetches the row at current position and moves the internal cursor to the next position. * Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric * @param bool TRUE for associative array, FALSE for numeric
@@ -150,6 +261,8 @@ interface IDibiResultDriver
*/ */
function fetch($type); function fetch($type);
/** /**
* Frees the resources allocated for this result set. * Frees the resources allocated for this result set.
* @param resource result set resource * @param resource result set resource
@@ -157,11 +270,16 @@ interface IDibiResultDriver
*/ */
function free(); function free();
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]} * @return array
* @throws DibiException
*/ */
function getResultColumns(); function getColumnsMeta();
/** /**
* Returns the result set resource. * Returns the result set resource.
@@ -169,47 +287,38 @@ interface IDibiResultDriver
*/ */
function getResultResource(); function getResultResource();
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
function unescape($value, $type);
}
/** /********************* reflection ****************d*g**/
* dibi driver reflection.
*
* @author David Grudl
* @package dibi
*/
interface IDibiReflector
{
/** /**
* Returns list of tables. * Returns list of tables.
* @return array of {name [, (bool) view ]} * @return array
*/ */
function getTables(); function getTables();
/** /**
* Returns metadata for all columns in a table. * Returns metadata for all columns in a table.
* @param string * @param string
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]} * @return array
*/ */
function getColumns($table); function getColumns($table);
/** /**
* Returns metadata for all indexes in a table. * Returns metadata for all indexes in a table.
* @param string * @param string
* @return array of {name, (array of names) columns [, (bool) unique, (bool) primary ]} * @return array
*/ */
function getIndexes($table); function getIndexes($table);
/** /**
* Returns metadata for all foreign keys in a table. * Returns metadata for all foreign keys in a table.
* @param string * @param string

2
dibi/netterobots.txt Normal file
View File

@@ -0,0 +1,2 @@
Disallow: /drivers
Disallow: /Nette

3
examples/.gitignore vendored
View File

@@ -1,3 +0,0 @@
_test.bat
ref
output

608
examples/Nette/Debug.php Normal file
View File

@@ -0,0 +1,608 @@
<?php
/**
* Nette Framework
*
* Copyright (c) 2004, 2008 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "Nette license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://nettephp.com
*
* @copyright Copyright (c) 2004, 2008 David Grudl
* @license http://nettephp.com/license Nette license
* @link http://nettephp.com
* @category Nette
* @package Nette
* @version $Id$
*/
class
ArgumentOutOfRangeException
extends
InvalidArgumentException{}class
InvalidStateException
extends
RuntimeException{}class
NotImplementedException
extends
LogicException{}class
NotSupportedException
extends
LogicException{}class
MemberAccessException
extends
LogicException{}class
IOException
extends
RuntimeException{}class
FileNotFoundException
extends
IOException{}class
DirectoryNotFoundException
extends
IOException{}class
FatalErrorException
extends
Exception{public
function
__construct($message,$code,$severity,$file,$line,$context){parent::__construct($message,$code);$this->file=$file;$this->line=$line;$this->context=$context;}}final
class
Framework{const
VERSION='0.8';const
REVISION='107 released on 2008/10/29 20:40:23';final
public
function
__construct(){throw
new
LogicException("Cannot instantiate static class ".get_class($this));}public
static
function
compareVersion($version){return
version_compare($version,self::VERSION);}public
static
function
promo($xhtml=TRUE){echo'<a href="http://nettephp.com/" title="Nette Framework - The Most Innovative PHP Framework"><img ','src="http://nettephp.com/images/nette-powered.gif" alt="Powered by Nette Framework" width="80" height="15"',($xhtml?' />':'>'),'</a>';}}final
class
Debug{public
static$counters=array();public
static$html;public
static$maxDepth=3;public
static$maxLen=150;private
static$enabled=FALSE;public
static$useFirebug;private
static$logFile;private
static$logHandle;private
static$sendEmails;private
static$emailHeaders=array('To'=>'','From'=>'noreply@%host%','X-Mailer'=>'Nette Framework','Subject'=>'PHP: An error occurred on the server %host%','Body'=>'[%date%]');public
static$mailer=array(__CLASS__,'sendEmail');public
static$emailProbability=0.01;public
static$keysToHide=array('password','passwd','pass','pwd','creditcard','credit card','cc','pin');private
static$colophons=array(array(__CLASS__,'getDefaultColophons'));private
static$keyFilter=array();public
static$time;const
LOG='LOG';const
INFO='INFO';const
WARN='WARN';const
ERROR='ERROR';final
public
function
__construct(){throw
new
LogicException("Cannot instantiate static class ".get_class($this));}public
static
function
dump($var,$return=FALSE){self::$keyFilter=FALSE;$output="<pre class=\"dump\">".self::_dump($var,0)."</pre>\n";if(!self::$html){$output=htmlspecialchars_decode(strip_tags($output),ENT_NOQUOTES);}if($return){return$output;}else{echo$output;return$var;}}private
static
function
_dump(&$var,$level){if(is_bool($var)){return"<span>bool</span>(".($var?'TRUE':'FALSE').")\n";}elseif($var===NULL){return"<span>NULL</span>\n";}elseif(is_int($var)){return"<span>int</span>($var)\n";}elseif(is_float($var)){return"<span>float</span>($var)\n";}elseif(is_string($var)){if(self::$maxLen&&strlen($var)>self::$maxLen){$s=htmlSpecialChars(substr($var,0,self::$maxLen),ENT_NOQUOTES).' ... ';}else{$s=htmlSpecialChars($var,ENT_NOQUOTES);}return"<span>string</span>(".strlen($var).") \"$s\"\n";}elseif(is_array($var)){$s="<span>array</span>(".count($var).") {\n";$space=str_repeat(' ',$level);static$marker;if($marker===NULL)$marker=uniqid("\x00",TRUE);if(isset($var[$marker])){$s.="$space *RECURSION*\n";}elseif($level<self::$maxDepth||!self::$maxDepth){$var[$marker]=0;foreach($var
as$k=>&$v){if($k===$marker)continue;$s.="$space ".(is_int($k)?$k:"\"$k\"")." => ";if(self::$keyFilter&&is_string($v)&&isset(self::$keyFilter[strtolower($k)])){$s.="<span>string</span>(?) <i>*** hidden ***</i>\n";}else{$s.=self::_dump($v,$level+1);}}unset($var[$marker]);}else{$s.="$space ...\n";}return$s."$space}\n";}elseif(is_object($var)){$arr=(array)$var;$s="<span>object</span>(".get_class($var).") (".count($arr).") {\n";$space=str_repeat(' ',$level);static$list=array();if(in_array($var,$list,TRUE)){$s.="$space *RECURSION*\n";}elseif($level<self::$maxDepth||!self::$maxDepth){$list[]=$var;foreach($arr
as$k=>&$v){$m='';if($k[0]==="\x00"){$m=$k[1]==='*'?' <span>protected</span>':' <span>private</span>';$k=substr($k,strrpos($k,"\x00")+1);}$s.="$space \"$k\"$m => ";if(self::$keyFilter&&is_string($v)&&isset(self::$keyFilter[strtolower($k)])){$s.="<span>string</span>(?) <i>*** hidden ***</i>\n";}else{$s.=self::_dump($v,$level+1);}}array_pop($list);}else{$s.="$space ...\n";}return$s."$space}\n";}elseif(is_resource($var)){return"<span>resource of type</span>(".get_resource_type($var).")\n";}else{return"<span>unknown type</span>\n";}}public
static
function
timer(){static$time=0;$now=microtime(TRUE);$delta=$now-$time;$time=$now;return$delta;}public
static
function
enable($level=E_ALL,$logErrors=NULL,$sendEmails=FALSE){if(version_compare(PHP_VERSION,'5.2.1')===0){throw
new
NotSupportedException(__METHOD__.' is not supported in PHP 5.2.1');}if($logErrors===NULL&&class_exists('Environment')){$logErrors=Environment::isLive();}if(self::$useFirebug===NULL){self::$useFirebug=function_exists('json_encode')&&!$logErrors&&isset($_SERVER['HTTP_USER_AGENT'])&&strpos($_SERVER['HTTP_USER_AGENT'],'FirePHP/');}if($level!==NULL){error_reporting($level);}if(function_exists('ini_set')){ini_set('display_startup_errors',!$logErrors);ini_set('display_errors',!$logErrors);ini_set('html_errors',self::$html);ini_set('log_errors',(bool)$logErrors);}elseif($logErrors){throw
new
NotSupportedException('Function ini_set() is not enabled.');}if($logErrors){if(is_string($logErrors)){self::$logFile=strpos($logErrors,'%')===FALSE?$logErrors:Environment::expand($logErrors);}else{try{self::$logFile=Environment::expand('%logDir%/php_error.log');}catch(InvalidStateException$e){self::$logFile='php_error.log';}}ini_set('error_log',self::$logFile);}self::$sendEmails=$logErrors&&$sendEmails;if(self::$sendEmails){if(is_string($sendEmails)){self::$emailHeaders['To']=$sendEmails;}elseif(is_array($sendEmails)){self::$emailHeaders=$sendEmails+self::$emailHeaders;}if(mt_rand()/mt_getrandmax()<self::$emailProbability){self::observeErrorLog();}}if(!defined('E_RECOVERABLE_ERROR')){define('E_RECOVERABLE_ERROR',4096);}if(!defined('E_DEPRECATED')){define('E_DEPRECATED',8192);}set_exception_handler(array(__CLASS__,'exceptionHandler'));set_error_handler(array(__CLASS__,'errorHandler'));self::$enabled=TRUE;}public
static
function
isEnabled(){return
self::$enabled;}public
static
function
exceptionHandler(Exception$exception){if(!headers_sent()){header('HTTP/1.1 500 Internal Server Error');}if(self::$logFile){error_log("PHP Fatal error: Uncaught $exception");$file=@strftime('%d-%b-%Y %H-%M-%S ',Debug::$time).strstr(number_format(Debug::$time,4,'~',''),'~');$file=dirname(self::$logFile)."/exception $file.html";self::$logHandle=@fopen($file,'x');if(self::$logHandle){ob_start(array(__CLASS__,'writeFile'),1);self::paintBlueScreen($exception);ob_end_flush();fclose(self::$logHandle);}self::observeErrorLog();}elseif(!self::$html||isset($_SERVER['HTTP_X_REQUESTED_WITH'])&&$_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest'){if(self::$useFirebug&&!headers_sent()){self::fireLog($exception);}else{echo"$exception\n";foreach(self::$colophons
as$callback){foreach((array)call_user_func($callback,'bluescreen')as$line)echo
strip_tags($line)."\n";}}}else{while(ob_get_level()&&@ob_end_clean());self::paintBlueScreen($exception);exit;}}public
static
function
errorHandler($severity,$message,$file,$line,$context){static$fatals=array(E_ERROR=>1,E_CORE_ERROR=>1,E_COMPILE_ERROR=>1,E_USER_ERROR=>1,E_PARSE=>1,E_RECOVERABLE_ERROR=>1);if(isset($fatals[$severity])){throw
new
FatalErrorException($message,0,$severity,$file,$line,$context);}elseif(($severity&error_reporting())!==$severity){return
NULL;}elseif(self::$useFirebug&&!headers_sent()){$types=array(E_WARNING=>'Warning',E_USER_WARNING=>'Warning',E_NOTICE=>'Notice',E_USER_NOTICE=>'Notice',E_STRICT=>'Strict standards',E_DEPRECATED=>'Deprecated');$type=isset($types[$severity])?$types[$severity]:'Unknown error';$message=strip_tags($message);self::fireLog("$type: $message in $file on line $line",'WARN');return
NULL;}return
FALSE;}public
static
function
paintBlueScreen(Exception$exception){$colophons=self::$colophons;function
_netteDebugPrintCode($file,$line,$count=15){if(function_exists('ini_set')){ini_set('highlight.comment','#999; font-style: italic');ini_set('highlight.default','#000');ini_set('highlight.html','#06b');ini_set('highlight.keyword','#d24; font-weight: bold');ini_set('highlight.string','#080');}$start=max(1,$line-floor($count/2));$source=explode("\n",@highlight_file($file,TRUE));echo$source[0];$source=explode('<br />',$source[1]);array_unshift($source,NULL);$i=$start;while(--$i>=1){if(preg_match('#.*(</?span[^>]*>)#',$source[$i],$m)){if($m[1]!=='</span>')echo$m[1];break;}}$source=array_slice($source,$start,$count,TRUE);end($source);$numWidth=strlen((string)key($source));foreach($source
as$n=>$s){$s=str_replace(array("\r","\n"),array('',''),$s);if($n===$line){printf("<span class='highlight'>Line %{$numWidth}s: %s\n</span>%s",$n,strip_tags($s),preg_replace('#[^>]*(<[^>]+>)[^<]*#','$1',$s));}else{printf("<span class='line'>Line %{$numWidth}s:</span> %s\n",$n,$s);}}echo'</span></span></code>';}function
_netteOpenPanel($name,$collaped){static$id;$id++;?>
<div class="panel">
<h2><a href="#" onclick="return !toggle(this, 'pnl<?php echo$id?>')"><?php echo
htmlSpecialChars($name)?> <span><?php echo$collaped?'&#x25b6;':'&#x25bc;'?></span></a></h2>
<div id="pnl<?php echo$id?>" class="<?php echo$collaped?'collapsed ':''?>inner">
<?php
}function
_netteClosePanel(){?>
</div>
</div>
<?php
}if(headers_sent()){echo'</pre></xmp></table>';}?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex,noarchive">
<meta name="generator" content="Nette Framework">
<title><?php echo
htmlspecialchars(get_class($exception))?></title>
<style type="text/css">
/* <![CDATA[ */
body {
font: 78%/1.5 Verdana, sans-serif;
background: white;
color: #333;
margin: 0 0 2em;
padding: 0;
}
h1 {
font-weight: normal !important;
font-size: 18pt;
margin: .6em 0;
}
h2 {
font-family: sans-serif;
font-weight: normal;
font-size: 14pt;
color: #888;
margin: .6em 0;
}
a {
text-decoration: none;
color: #4197E3;
}
a span {
color: #999;
}
h3 {
font-size: 110%;
font-weight: bold;
margin: 1em 0;
}
p { margin: .8em 0 }
pre, code, table {
font-family: Consolas, monospace;
}
pre, table {
background: #ffffcc;
padding: .4em .7em;
border: 1px dotted silver;
}
table pre {
padding: 0;
margin: 0;
border: none;
font-size: 100%;
}
pre.dump span {
color: #c16549;
}
div.panel {
border-bottom: 1px solid #eee;
padding: 1px 2em;
}
div.inner {
padding: 0.1em 1em 1em;
background: #f5f5f5;
}
table {
border-collapse: collapse;
width: 100%;
}
td, th {
vertical-align: top;
padding: 2px 3px;
border: 1px solid #eeeebb;
}
ul {
font-size: 80%;
}
.highlight, #error {
background: red;
color: white;
font-weight: bold;
font-style: normal;
display: block;
}
.line {
color: #9e9e7e;
font-weight: normal;
font-style: normal;
}
/* ]]> */
</style>
<script type="text/javascript">
/* <![CDATA[ */
document.write('<style> .collapsed { display: none; } </style>');
function toggle(link, panel)
{
var span = link.getElementsByTagName('span')[0];
var div = document.getElementById(panel);
var collapsed = div.currentStyle ? div.currentStyle.display == 'none' : getComputedStyle(div, null).display == 'none';
span.innerHTML = String.fromCharCode(collapsed ? 0x25bc : 0x25b6);
div.style.display = collapsed ? 'block' : 'none';
return true;
}
/* ]]> */
</script>
</head>
<body>
<div id="error" class="panel">
<h1><?php echo
htmlspecialchars(get_class($exception)),($exception->getCode()?' #'.$exception->getCode():'')?></h1>
<p><?php echo
htmlspecialchars($exception->getMessage())?></p>
</div>
<?php $ex=$exception;$level=0;?>
<?php do{?>
<?php if($level++):?>
<?php _netteOpenPanel('Caused by',TRUE)?>
<div class="panel">
<h1><?php echo
htmlspecialchars(get_class($ex)),($ex->getCode()?' #'.$ex->getCode():'')?></h1>
<p><?php echo
htmlspecialchars($ex->getMessage())?></p>
</div>
<?php endif?>
<?php if(is_file($ex->getFile())):?>
<?php _netteOpenPanel('Source file',FALSE)?>
<p><strong>File:</strong> <?php echo
htmlspecialchars($ex->getFile())?> &nbsp; <strong>Line:</strong> <?php echo$ex->getLine()?></p>
<pre><?php _netteDebugPrintCode($ex->getFile(),$ex->getLine())?></pre>
<?php _netteClosePanel()?>
<?php endif?>
<?php _netteOpenPanel('Call stack',FALSE)?>
<ol>
<?php foreach($ex->getTrace()as$key=>$row):?>
<li><p>
<?php if(isset($row['file'])):?>
<span title="<?php echo
htmlSpecialChars($row['file'])?>"><?php echo
htmlSpecialChars(basename(dirname($row['file']))),'/<b>',htmlSpecialChars(basename($row['file'])),'</b></span> (',$row['line'],')'?>
<?php else:?>
&lt;PHP inner-code&gt;
<?php endif?>
<?php if(isset($row['file'])&&is_file($row['file'])):?><a href="#" onclick="return !toggle(this, 'src<?php echo"$level-$key"?>')">source <span>&#x25b6;</span></a> &nbsp; <?php endif?>
<?php if(isset($row['class']))echo$row['class'].$row['type']?>
<?php echo$row['function']?>
(<?php if(!empty($row['args'])):?><a href="#" onclick="return !toggle(this, 'args<?php echo"$level-$key"?>')">arguments <span>&#x25b6;</span></a><?php endif?>)
</p>
<?php if(!empty($row['args'])):?>
<div class="collapsed" id="args<?php echo"$level-$key"?>">
<table>
<?php
try{$r=isset($row['class'])?new
ReflectionMethod($row['class'],$row['function']):new
ReflectionFunction($row['function']);$params=$r->getParameters();}catch(Exception$e){$params=array();}foreach($row['args']as$k=>$v){echo'<tr><td>',(isset($params[$k])?'$'.$params[$k]->name:"#$k"),'</td>';echo'<td>',self::safeDump($v,isset($params[$k])?$params[$k]->name:NULL),"</td></tr>\n";}?>
</table>
</div>
<?php endif?>
<?php if(isset($row['file'])&&is_file($row['file'])):?>
<pre class="collapsed" id="src<?php echo"$level-$key"?>"><?php _netteDebugPrintCode($row['file'],$row['line'])?></pre>
<?php endif?>
</li>
<?php endforeach?>
</ol>
<?php _netteClosePanel()?>
<?php if($ex
instanceof
IDebuggable):?>
<?php foreach($ex->getPanels()as$name=>$panel):?>
<?php _netteOpenPanel($name,empty($panel['expanded']))?>
<?php echo$panel['content']?>
<?php _netteClosePanel()?>
<?php endforeach?>
<?php endif?>
<?php if(isset($ex->context)&&is_array($ex->context)):?>
<?php _netteOpenPanel('Variables',TRUE)?>
<table>
<?php
foreach($ex->context
as$k=>$v){echo'<tr><td>$',htmlspecialchars($k),'</td><td>',self::safeDump($v,$k),"</td></tr>\n";}?>
</table>
<?php _netteClosePanel()?>
<?php endif?>
<?php }while((method_exists($ex,'getPrevious')&&$ex=$ex->getPrevious())||(isset($ex->previous)&&$ex=$ex->previous));?>
<?php while(--$level)_netteClosePanel()?>
<?php _netteOpenPanel('Environment',TRUE)?>
<?php
$list=get_defined_constants(TRUE);if(!empty($list['user'])):?>
<h3><a href="#" onclick="return !toggle(this, 'pnl-env-const')">Constants <span>&#x25bc;</span></a></h3>
<table id="pnl-env-const">
<?php
foreach($list['user']as$k=>$v){echo'<tr><td>',htmlspecialchars($k),'</td><td>',self::safeDump($v,$k),"</td></tr>\n";}?>
</table>
<?php endif?>
<h3><a href="#" onclick="return !toggle(this, 'pnl-env-files')">Included files <span>&#x25b6;</span></a> (<?php echo
count(get_included_files())?>)</h3>
<table id="pnl-env-files" class="collapsed">
<?php
foreach(get_included_files()as$v){echo'<tr><td>',htmlspecialchars($v),"</td></tr>\n";}?>
</table>
<h3>$_SERVER</h3>
<?php if(empty($_SERVER)):?>
<p><i>empty</i></p>
<?php else:?>
<table>
<?php
foreach($_SERVER
as$k=>$v)echo'<tr><td>',htmlspecialchars($k),'</td><td>',self::dump($v,TRUE),"</td></tr>\n";?>
</table>
<?php endif?>
<?php _netteClosePanel()?>
<?php _netteOpenPanel('HTTP request',TRUE)?>
<?php if(function_exists('apache_request_headers')):?>
<h3>Headers</h3>
<table>
<?php
foreach(apache_request_headers()as$k=>$v)echo'<tr><td>',htmlspecialchars($k),'</td><td>',htmlspecialchars($v),"</td></tr>\n";?>
</table>
<?php endif?>
<?php foreach(array('_GET','_POST','_COOKIE')as$name):?>
<h3>$<?php echo$name?></h3>
<?php if(empty($GLOBALS[$name])):?>
<p><i>empty</i></p>
<?php else:?>
<table>
<?php
foreach($GLOBALS[$name]as$k=>$v)echo'<tr><td>',htmlspecialchars($k),'</td><td>',self::dump($v,TRUE),"</td></tr>\n";?>
</table>
<?php endif?>
<?php endforeach?>
<?php _netteClosePanel()?>
<?php _netteOpenPanel('HTTP response',TRUE)?>
<h3>Headers</h3>
<?php if(headers_list()):?>
<pre><?php
foreach(headers_list()as$s)echo
htmlspecialchars($s),'<br>';?></pre>
<?php else:?>
<p><i>no headers</i></p>
<?php endif?>
<?php _netteClosePanel()?>
<ul>
<?php foreach($colophons
as$callback):?>
<?php foreach((array)call_user_func($callback,'bluescreen')as$line):?><li><?php echo$line,"\n"?></li><?php endforeach?>
<?php endforeach?>
</ul>
</body>
</html><?php }public
static
function
writeFile($buffer){fwrite(self::$logHandle,$buffer);}private
static
function
observeErrorLog(){if(!self::$sendEmails)return;$monitorFile=self::$logFile.'.monitor';$saved=@file_get_contents($monitorFile);$actual=(int)@filemtime(self::$logFile);if($saved===FALSE){file_put_contents($monitorFile,$actual);}elseif(is_numeric($saved)&&$saved!=$actual){if(file_put_contents($monitorFile,'e-mail has been sent')){call_user_func(self::$mailer);}}}private
static
function
sendEmail(){$host=isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:(isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:'');$headers=str_replace(array('%host%','%date%'),array($host,@date('Y-m-d H:i:s',Debug::$time)),self::$emailHeaders);$subject=$headers['Subject'];$to=$headers['To'];$body=$headers['Body'];unset($headers['Subject'],$headers['To'],$headers['Body']);$header='';foreach($headers
as$key=>$value){$header.="$key: $value\r\n";}$body=str_replace("\r\n","\n",$body);if(PHP_OS!='Linux')$body=str_replace("\n","\r\n",$body);if($to==='debug'){self::dump(array($to,$subject,$body,$header));}else{mail($to,$subject,$body,$header);}}private
static
function
safeDump($var,$key=NULL){self::$keyFilter=array_change_key_case(array_flip(self::$keysToHide),CASE_LOWER);if($key!==NULL&&isset(self::$keyFilter[strtolower($key)])){return'<i>*** hidden ***</i>';}return"<pre class=\"dump\">".self::_dump($var,0)."</pre>\n";}public
static
function
enableProfiler(){register_shutdown_function(array(__CLASS__,'paintProfiler'));}public
static
function
paintProfiler(){$colophons=self::$colophons;if(self::$useFirebug){foreach(self::$colophons
as$callback){foreach((array)call_user_func($callback,'profiler')as$line)self::fireLog(strip_tags($line));}}if(!isset($_SERVER['HTTP_X_REQUESTED_WITH'])||$_SERVER['HTTP_X_REQUESTED_WITH']!=='XMLHttpRequest'){?>
</pre></xmp>
<style type="text/css">
/* <![CDATA[ */
#netteProfilerContainer {
position: absolute;
right: 5px;
bottom: 5px;
}
#netteProfiler {
position: relative;
margin: 0;
padding: 1px;
width: 350px;
color: black;
background: #EEE;
border: 1px dotted gray;
cursor: move;
opacity: .70;
=filter: alpha(opacity=70);
}
#netteProfiler:hover {
opacity: 1;
=filter: none;
}
#netteProfiler li {
margin: 0;
padding: 1px;
font: normal normal 11px/1.4 Consolas, Arial;
text-align: left;
list-style: none;
}
#netteProfiler span[title] {
border-bottom: 1px dotted gray;
cursor: help;
}
#netteProfiler strong {
color: red;
}
/* ]]> */
</style>
<div id="netteProfilerContainer">
<ul id="netteProfiler">
<?php foreach($colophons
as$callback):?>
<?php foreach((array)call_user_func($callback,'profiler')as$line):?><li><?php echo$line,"\n"?></li><?php endforeach?>
<?php endforeach?>
</ul>
</div>
<script type="text/javascript">
/* <![CDATA[ */
document.getElementById('netteProfiler').onmousedown = function(e) {
e = e || event;
this.posX = parseInt(this.style.left + '0');
this.posY = parseInt(this.style.top + '0');
this.mouseX = e.clientX;
this.mouseY = e.clientY;
var thisObj = this;
document.documentElement.onmousemove = function(e) {
e = e || event;
thisObj.style.left = (e.clientX - thisObj.mouseX + thisObj.posX) + "px";
thisObj.style.top = (e.clientY - thisObj.mouseY + thisObj.posY) + "px";
return false;
};
document.documentElement.onmouseup = function(e) {
document.documentElement.onmousemove = null;
document.documentElement.onmouseup = null;
return false;
};
};
/* ]]> */
</script>
<?php }}public
static
function
addColophon($callback){if(!in_array($callback,self::$colophons,TRUE)&&is_callable($callback)){self::$colophons[]=$callback;}}public
static
function
getDefaultColophons($sender){if($sender==='profiler'){$arr[]='Elapsed time: '.sprintf('%0.3f',(microtime(TRUE)-Debug::$time)*1000).' ms';foreach((array)self::$counters
as$name=>$value){if(is_array($value))$value=implode(', ',$value);$arr[]=htmlSpecialChars($name).' = <strong>'.htmlSpecialChars($value).'</strong>';}$autoloaded=class_exists('AutoLoader',FALSE)?AutoLoader::$count:0;$s='<span>'.count(get_included_files()).'/'.$autoloaded.' files</span>, ';$exclude=array('stdClass','Exception','ErrorException','Traversable','IteratorAggregate','Iterator','ArrayAccess','Serializable','Closure');foreach(get_loaded_extensions()as$ext){$ref=new
ReflectionExtension($ext);$exclude=array_merge($exclude,$ref->getClassNames());}$classes=array_diff(get_declared_classes(),$exclude);$intf=array_diff(get_declared_interfaces(),$exclude);$func=get_defined_functions();$func=(array)@$func['user'];$consts=get_defined_constants(TRUE);$consts=array_keys((array)@$consts['user']);foreach(array('classes','intf','func','consts')as$item){$s.='<span '.($$item?'title="'.implode(", ",$$item).'"':'').'>'.count($$item).' '.$item.'</span>, ';}$arr[]=$s;}if($sender==='bluescreen'){$arr[]='PHP '.PHP_VERSION;if(isset($_SERVER['SERVER_SOFTWARE']))$arr[]=htmlSpecialChars($_SERVER['SERVER_SOFTWARE']);$arr[]='Nette Framework '.Framework::VERSION.' (revision '.Framework::REVISION.')';$arr[]='Report generated at '.@strftime('%c',Debug::$time);}return$arr;}public
static
function
fireDump($var,$key){return
self::fireSend(2,array((string)$key=>$var));}public
static
function
fireLog($message,$priority=self::LOG,$label=NULL){if($message
instanceof
Exception){$priority='TRACE';$message=array('Class'=>get_class($message),'Message'=>$message->getMessage(),'File'=>$message->getFile(),'Line'=>$message->getLine(),'Trace'=>self::replaceObjects($message->getTrace()));}elseif($priority==='GROUP_START'){$label=$message;$message=NULL;}return
self::fireSend(1,array(array('Type'=>$priority,'Label'=>$label),self::replaceObjects($message)));}private
static
function
fireSend($index,$payload){if(headers_sent())return
FALSE;header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');if($index===1){header('X-Wf-nette-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');}elseif($index===2){header('X-Wf-nette-Structure-2: http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');}$payload=json_encode($payload);static$counter;foreach(str_split($payload,4990)as$s){$num=++$counter;header("X-Wf-nette-$index-1-n$num: |$s|\\");}header("X-Wf-nette-$index-1-n$num: |$s|");header("X-Wf-nette-Index: n$num");return
TRUE;}static
private
function
replaceObjects($val){if(is_object($val)){return'object '.get_class($val).'';}elseif(is_array($val)){foreach($val
as$k=>$v){unset($val[$k]);$val[$k]=self::replaceObjects($v);}}return$val;}}Debug::$html=PHP_SAPI!=='cli';Debug::$time=microtime(TRUE);

File diff suppressed because one or more lines are too long

View File

@@ -1,60 +1,62 @@
Licenses The Nette License, Version 1
======== ============================
Good news! You may use Nette Framework under the terms of either Copyright (c) 2004, 2008 David Grudl (http://davidgrudl.com)
the New BSD License or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the framework. If the GPL
fits better to your project, you can use the framework under this license.
You don't have to notify anyone which license you are using. You can freely
use Nette Framework in commercial projects as long as the copyright header
remains intact.
Please be advised that the name "Nette Framework" is a protected trademark and its
usage has some limitations. So please do not use word "Nette" in the name of your
project or top-level domain, and choose a name that stands on its own merits.
If your stuff is good, it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com)
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, This license is a legal agreement between you and David Grudl (the "Author")
are permitted provided that the following conditions are met: for the use of Nette Framework (the "Software"). By obtaining, using and/or
copying the Software, you agree that you have read, understood, and will
comply with the terms and conditions of this license.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, PERMITTED USE
this list of conditions and the following disclaimer in the documentation -------------
and/or other materials provided with the distribution.
* Neither the name of "Nette Framework" nor the names of its contributors You are permitted to use, copy, modify, and distribute the Software and its
may be used to endorse or promote products derived from this software documentation, with or without modification, for any purpose, provided that
without specific prior written permission. the following conditions are met:
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 1. A copy of this license agreement must be included with the distribution.
2. Redistributions of source code must retain the above copyright notice in
all source code files.
3. Redistributions in binary form must reproduce the above copyright notice
in the documentation and/or other materials provided with the distribution.
4. Products derived from the Software must include an acknowledgment that
they are derived from Nette Framework in their documentation and/or other
materials provided with the distribution.
5. The name "Nette Framework" must not be used to endorse or promote products
derived from the Software without prior written permission from Author.
6. Products derived from the Software may not be called "Nette Framework",
nor may "Nette" appear in their name, without prior written
permission from Author.
INDEMNITY
---------
You agree to indemnify and hold harmless the Author and any contributors
for any direct, indirect, incidental, or consequential third-party claims,
actions or suits, as well as any related expenses, liabilities, damages,
settlements or fees arising from your use or misuse of the Software,
or a violation of any terms of this license.
DISCLAIMER OF WARRANTY
----------------------
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- GPL version 2: http://www.gnu.org/licenses/gpl-2.0.html
- GPL version 3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@@ -1,3 +1,3 @@
This file is part of Nette Framework This file is part of Nette Framework
For more information please see http://nette.org For more information please see http://nettephp.com

View File

@@ -1,16 +1,14 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>dibi apply limit/offset example</h1>
<pre>
<h1>Using Limit & Offset | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
)); ));
@@ -19,11 +17,15 @@ dibi::test('SELECT * FROM [products]');
// -> SELECT * FROM [products] // -> SELECT * FROM [products]
echo '<hr>';
// with limit = 2 // with limit = 2
dibi::test('SELECT * FROM [products] %lmt', 2); dibi::test('SELECT * FROM [products] %lmt', 2);
// -> SELECT * FROM [products] LIMIT 2 // -> SELECT * FROM [products] LIMIT 2
echo '<hr>';
// with limit = 2, offset = 1 // with limit = 2, offset = 1
dibi::test('SELECT * FROM [products] %lmt %ofs', 2, 1); dibi::test('SELECT * FROM [products] %lmt %ofs', 2, 1);
// -> SELECT * FROM [products] LIMIT 2 OFFSET 1 // -> SELECT * FROM [products] LIMIT 2 OFFSET 1

View File

@@ -1,19 +1,16 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>dibi::connect() example</h1>
<h1>Connecting to Databases | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
// connects to SQlite using dibi class // connects to SQlite
echo '<p>Connecting to Sqlite: '; echo '<p>Connecting to Sqlite: ';
try { try {
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
)); ));
echo 'OK'; echo 'OK';
@@ -23,25 +20,12 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to SQlite using DibiConnection object
echo '<p>Connecting to Sqlite: ';
try {
$connection = new DibiConnection(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
echo 'OK';
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to MySQL using DSN // connects to MySQL using DSN
echo '<p>Connecting to MySQL: '; echo '<p>Connecting to MySQL: ';
try { try {
dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=cp1250'); dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=utf8');
echo 'OK'; echo 'OK';
} catch (DibiException $e) { } catch (DibiException $e) {
@@ -50,8 +34,10 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to MySQLi using array // connects to MySQLi using array
echo '<p>Connecting to MySQLi: '; echo '<p>Connecting to MySQL: ';
try { try {
dibi::connect(array( dibi::connect(array(
'driver' => 'mysqli', 'driver' => 'mysqli',
@@ -59,10 +45,7 @@ try {
'username' => 'root', 'username' => 'root',
'password' => 'xxx', 'password' => 'xxx',
'database' => 'dibi', 'database' => 'dibi',
'options' => array( 'charset' => 'utf8',
MYSQLI_OPT_CONNECT_TIMEOUT => 30
),
'flags' => MYSQLI_CLIENT_COMPRESS,
)); ));
echo 'OK'; echo 'OK';
@@ -72,6 +55,8 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to ODBC // connects to ODBC
echo '<p>Connecting to ODBC: '; echo '<p>Connecting to ODBC: ';
try { try {
@@ -79,7 +64,7 @@ try {
'driver' => 'odbc', 'driver' => 'odbc',
'username' => 'root', 'username' => 'root',
'password' => '***', 'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/data/sample.mdb', 'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/sample.mdb',
)); ));
echo 'OK'; echo 'OK';
@@ -89,6 +74,8 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to PostgreSql // connects to PostgreSql
echo '<p>Connecting to PostgreSql: '; echo '<p>Connecting to PostgreSql: ';
try { try {
@@ -105,6 +92,8 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to PDO // connects to PDO
echo '<p>Connecting to Sqlite via PDO: '; echo '<p>Connecting to Sqlite via PDO: ';
try { try {
@@ -120,6 +109,7 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to MS SQL // connects to MS SQL
echo '<p>Connecting to MS SQL: '; echo '<p>Connecting to MS SQL: ';
try { try {
@@ -137,23 +127,6 @@ try {
echo "</p>\n"; echo "</p>\n";
// connects to MS SQL 2005
echo '<p>Connecting to MS SQL 2005: ';
try {
dibi::connect(array(
'driver' => 'mssql2005',
'host' => '(local)',
'username' => 'Administrator',
'password' => 'xxx',
'database' => 'main',
));
echo 'OK';
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to Oracle // connects to Oracle
echo '<p>Connecting to Oracle: '; echo '<p>Connecting to Oracle: ';
@@ -169,4 +142,4 @@ try {
} catch (DibiException $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,64 +0,0 @@
body {
font: 15px/1.5 Tahoma, Verdana, Myriad Web, Syntax, sans-serif;
color: #333;
background: #fff url('dibi-powered.gif') no-repeat 99% 1em;
margin: 1.6em;
padding: 0;
}
h1, h2 {
font-size: 210%;
font-weight: normal;
color: #036;
}
h2 {
font-size: 150%;
}
a {
color: #000080;
}
table.dump {
padding: 0;
margin: 0;
border-collapse:collapse;
}
table.dump td, table.dump th {
color: #505767;
background: #fff;
border: 1px solid #d1cdab;
padding: 6px 6px 6px 12px;
text-align: left;
}
table.dump th {
font-size: 80%;
color: #525b37;
background: #e3e9ba;
}
/* dump() */
pre.nette-dump, pre.dump {
color: #444; background: white;
border: 1px solid silver;
padding: 1em;
margin: 1em 0;
}
pre.nette-dump .php-array, pre.nette-dump .php-object {
color: #C22;
}
pre.nette-dump .php-string {
color: #080;
}
pre.nette-dump .php-int, pre.nette-dump .php-float {
color: #37D;
}
pre.nette-dump .php-null, pre.nette-dump .php-bool {
color: black;
}
pre.nette-dump .php-visibility {
font-size: 85%; color: #999;
}

View File

@@ -1,50 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Database Reflection | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// retrieve database reflection
$database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->name}'</h2>\n";
echo "<ul>\n";
foreach ($database->getTables() as $table) {
echo '<li>', ($table->view ? 'view' : 'table') . " $table->name</li>\n";
}
echo "</ul>\n";
// table reflection
$table = $database->getTable('products');
echo "<h2>Table '{$table->name}'</h2>\n";
echo "Columns\n";
echo "<ul>\n";
foreach ($table->getColumns() as $column) {
echo "<li>{$column->name} <i>{$column->nativeType}</i> <code>{$column->default}</code></li>\n";
}
echo "</ul>\n";
echo "Indexes";
echo "<ul>\n";
foreach ($table->getIndexes() as $index) {
echo "<li>{$index->name} " . ($index->primary ? 'primary ' : '') . ($index->unique ? 'unique' : '') . ' (';
foreach ($index->getColumns() as $column) {
echo "$column->name, ";
}
echo ")</li>\n";
}
echo "</ul>\n";

View File

@@ -1,30 +1,30 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>IDibiVariable example</h1>
<h1>Using DateTime | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
// required since PHP 5.1.0
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS! // CHANGE TO REAL PARAMETERS!
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
'formatDate' => "'Y-m-d'", 'formatDate' => "'Y-m-d'",
'formatDateTime' => "'Y-m-d H-i-s'", 'formatDateTime' => "'Y-m-d H-i-s'",
)); ));
// generate and dump SQL // generate and dump SQL
dibi::test(" dibi::test("
INSERT INTO [mytable]", array( INSERT INTO [mytable]", array(
'id' => 123, 'id' => 123,
'date' => new DateTime('12.3.2007'), 'date' => dibi::date('12.3.2007'),
'stamp' => new DateTime('23.1.2007 10:23'), 'stamp' => dibi::dateTime('23.1.2007 10:23'),
) ));
);
// -> INSERT INTO [mytable] ([id], [date], [stamp]) VALUES (123, '2007-03-12', '2007-01-23 10-23-00') // -> INSERT INTO [mytable] ([id], [date], [stamp]) VALUES (123, '2007-03-12', '2007-01-23 10-23-00')

101
examples/dibi.table.php Normal file
View File

@@ -0,0 +1,101 @@
<h1>DibiTableX demo</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
dibi::begin();
// autodetection: primary keys are customer_id, order_id, ...
DibiTableX::$primaryMask = '%s_id';
// table products
class Products extends DibiTableX
{
// rely on autodetection...
// protected $name = 'products';
// protected $primary = 'product_id';
}
// create table object
$products = new Products;
echo "Table name: $products->name\n";
echo "Primary key: $products->primary\n";
// Finds rows by primary key
foreach ($products->find(1, 3) as $row) {
Debug::dump($row);
}
// select all
$products->findAll()->dump();
// select all, order by title, product_id
$products->findAll('title', $products->primary)->dump();
$products->findAll(array('title' => 'Chair'), 'title')->dump();
// fetches single row with id 3
$row = $products->fetch(3);
// deletes row from a table
$count = $products->delete(1);
// deletes multiple rows
$count = $products->delete(array(1, 2, 3));
Debug::dump($count); // number of deleted rows
// update row #2 in a table
$data = (object) NULL;
$data->title = 'New title';
$count = $products->update(2, $data);
Debug::dump($count); // number of updated rows
// update multiple rows in a table
$count = $products->update(array(3, 5), $data);
Debug::dump($count); // number of updated rows
// inserts row into a table
$data = array();
$data['title'] = 'New product';
$id = $products->insert($data);
Debug::dump($id); // generated id
// inserts or updates row into a table
$data = array();
$data['title'] = 'New product';
$data[$products->primary] = 5;
$products->insertOrUpdate($data);
// is absolutely SQL injection safe
$key = '3 OR 1=1';
$products->delete($key);
// --> DELETE FROM [products] WHERE [product_id] IN ( 3 )
// select all using fluent interface
Debug::dump($products->select('*')->orderBy('title')->fetchAll());

33
examples/dump.php Normal file
View File

@@ -0,0 +1,33 @@
<h1>dibi dump example</h1>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$res = dibi::query('
SELECT * FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
');
echo '<h2>dibi::dump()</h2>';
// dump last query (dibi::$sql)
dibi::dump();
// -> SELECT * FROM [products] INNER JOIN [orders] USING ([product_id]) INNER JOIN [customers] USING ([customer_id])
// dump result table
echo '<h2>DibiResult::dump()</h2>';
$res->dump();
// -> [table]

View File

@@ -1,33 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Dumping SQL and Result Set | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
$res = dibi::query('
SELECT * FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo '<h2>dibi::dump()</h2>';
// dump last query (dibi::$sql)
dibi::dump();
// dump result table
echo '<h2>DibiResult::dump()</h2>';
$res->dump();

View File

@@ -1,20 +1,18 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>dibi extension method example</h1>
<pre>
<h1>Using Extension Methods | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
ndebug();
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
)); ));
// using the "prototype" to add custom method to class DibiResult // using the "prototype" to add custom method to class DibiResult
function DibiResult_prototype_fetchShuffle(DibiResult $obj) function DibiResult_prototype_fetchShuffle(DibiResult $obj)
{ {
@@ -27,4 +25,4 @@ function DibiResult_prototype_fetchShuffle(DibiResult $obj)
// fetch complete result set shuffled // fetch complete result set shuffled
$res = dibi::query('SELECT * FROM [customers]'); $res = dibi::query('SELECT * FROM [customers]');
$all = $res->fetchShuffle(); $all = $res->fetchShuffle();
dump($all); Debug::dump($all);

94
examples/fetch.php Normal file
View File

@@ -0,0 +1,94 @@
<h1>dibi fetch example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
/*
TABLE products
product_id | title
-----------+----------
1 | Chair
2 | Table
3 | Computer
*/
// fetch a single row
$row = dibi::fetch('SELECT title FROM [products]');
Debug::dump($row); // Chair
echo '<hr>';
// fetch a single value
$value = dibi::fetchSingle('SELECT [title] FROM [products]');
Debug::dump($value); // Chair
echo '<hr>';
// fetch complete result set
$all = dibi::fetchAll('SELECT * FROM [products]');
Debug::dump($all);
echo '<hr>';
// fetch complete result set like association array
$res = dibi::query('SELECT * FROM [products]');
$assoc = $res->fetchAssoc('title'); // key
Debug::dump($assoc);
echo '<hr>';
// fetch complete result set like pairs key => value
$pairs = $res->fetchPairs('product_id', 'title');
Debug::dump($pairs);
echo '<hr>';
// fetch row by row
foreach ($res as $n => $row) {
Debug::dump($row);
}
echo '<hr>';
// fetch row by row with defined offset
foreach ($res->getIterator(2) as $n => $row) {
Debug::dump($row);
}
// fetch row by row with defined offset and limit
foreach ($res->getIterator(2, 1) as $n => $row) {
Debug::dump($row);
}
// more complex association array
$res = dibi::query('
SELECT *
FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
');
$assoc = $res->fetchAssoc('customers.name,products.title'); // key
Debug::dump($assoc);
echo '<hr>';
$assoc = $res->fetchAssoc('customers.name,#,products.title'); // key
Debug::dump($assoc);
echo '<hr>';
$assoc = $res->fetchAssoc('customers.name,=,products.title'); // key
Debug::dump($assoc);
echo '<hr>';

View File

@@ -1,86 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
ndebug();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
/*
TABLE products
product_id | title
-----------+----------
1 | Chair
2 | Table
3 | Computer
*/
// fetch a single row
echo "<h2>fetch()</h2>\n";
$row = dibi::fetch('SELECT title FROM products');
dump($row); // Chair
// fetch a single value
echo "<h2>fetchSingle()</h2>\n";
$value = dibi::fetchSingle('SELECT title FROM products');
dump($value); // Chair
// fetch complete result set
echo "<h2>fetchAll()</h2>\n";
$all = dibi::fetchAll('SELECT * FROM products');
dump($all);
// fetch complete result set like association array
echo "<h2>fetchAssoc('title')</h2>\n";
$res = dibi::query('SELECT * FROM products');
$assoc = $res->fetchAssoc('title'); // key
dump($assoc);
// fetch complete result set like pairs key => value
echo "<h2>fetchPairs('product_id', 'title')</h2>\n";
$pairs = $res->fetchPairs('product_id', 'title');
dump($pairs);
// fetch row by row
echo "<h2>using foreach</h2>\n";
foreach ($res as $n => $row) {
dump($row);
}
// more complex association array
$res = dibi::query('
SELECT *
FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo "<h2>fetchAssoc('customers.name|products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name|products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('customers.name[]products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name[]products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('customers.name->products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name->products.title'); // key
dump($assoc);

View File

@@ -1,18 +1,13 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>dibi dump example</h1>
<h1>Using Fluent Syntax | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
)); ));
@@ -35,6 +30,9 @@ dibi::select('product_id')->as('id')
// USING (product_id) INNER JOIN customers USING (customer_id) ORDER BY [title] // USING (product_id) INNER JOIN customers USING (customer_id) ORDER BY [title]
echo "\n";
// SELECT ... // SELECT ...
echo dibi::select('title')->as('id') echo dibi::select('title')->as('id')
->from('products') ->from('products')
@@ -42,6 +40,9 @@ echo dibi::select('title')->as('id')
// -> Chair (as result of query: SELECT [title] AS [id] FROM [products]) // -> Chair (as result of query: SELECT [title] AS [id] FROM [products])
echo "\n";
// INSERT ... // INSERT ...
dibi::insert('products', $record) dibi::insert('products', $record)
->setFlag('IGNORE') ->setFlag('IGNORE')
@@ -49,29 +50,41 @@ dibi::insert('products', $record)
// -> INSERT IGNORE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1) // -> INSERT IGNORE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1)
echo "\n";
// UPDATE ... // UPDATE ...
dibi::update('products', $record) dibi::update('products', $record)
->where('product_id = ?', $id) ->where('product_id = %d', $id)
->test(); ->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10 // -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
echo "\n";
// DELETE ... // DELETE ...
dibi::delete('products') dibi::delete('products')
->where('product_id = ?', $id) ->where('product_id = %d', $id)
->test(); ->test();
// -> DELETE FROM [products] WHERE product_id = 10 // -> DELETE FROM [products] WHERE product_id = 10
echo "\n";
// custom commands // custom commands
dibi::command() dibi::command()
->update('products') ->update('products')
->where('product_id = ?', $id) ->where('product_id = %d', $id)
->set($record) ->set($record)
->test(); ->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10 // -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
echo "\n";
dibi::command() dibi::command()
->truncate('products') ->truncate('products')
->test(); ->test();

View File

@@ -1,19 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Importing SQL Dump from File | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
$count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz');
echo 'Number of SQL commands:', $count;

View File

@@ -0,0 +1,17 @@
<h1>dibi import SQL dump example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$count = dibi::loadFile('compress.zlib://sample.dump.sql.gz');
echo 'Number of SQL commands:', $count;

34
examples/logger.php Normal file
View File

@@ -0,0 +1,34 @@
<h1>dibi logger example</h1>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
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);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', 5);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38);
} catch (DibiException $e) {
echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>';
}
echo "<h2>File log.sql:</h2>";
echo '<pre>', file_get_contents('log.sql'), '</pre>';

28
examples/metatypes.php Normal file
View File

@@ -0,0 +1,28 @@
<h1>dibi metatypes example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$res = dibi::query('SELECT * FROM [customers]');
// auto-converts this column to integer
$res->setType('customer_id', Dibi::FIELD_INTEGER);
$res->setType('added', Dibi::FIELD_DATETIME, 'H:i j.n.Y');
$row = $res->fetch();
Debug::dump($row);
// outputs:
// object(DibiRow)#3 (3) {
// customer_id => int(1)
// name => string(11) "Dave Lister"
// added => string(15) "17:20 11.3.2007"
// }

View File

@@ -1,44 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Nette Debugger & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via Nette Debugger, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nette.org
</ul>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
// enable Nette Debugger
ndebug();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
'profiler' => array(
'run' => TRUE,
)
));
// throws error because SQL is bad
dibi::query('SELECT * FROM customers WHERE customer_id < ?', 38);
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
'profiler' => array(
'run' => TRUE,
)
));
// throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);

View File

@@ -1,30 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Nette Debugger & Variables | dibi</h1>
<p>Dibi can dump variables via Nette Debugger, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nette.org
</ul>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
// enable Nette Debugger
NDebugger::enable();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
'profiler' => array(
'run' => TRUE,
)
));
NDebugger::barDump( dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]' );

28
examples/nette-debug.php Normal file
View File

@@ -0,0 +1,28 @@
<h1>Nette::Debug & dibi example</h1>
<p>Dibi can display and log exceptions via Nette::Debug, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nettephp.com
</ul>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
// enable Nette::Debug
Debug::enable();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// throws error
dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38);

View File

@@ -1,31 +1,23 @@
<?php ob_start(1) // needed by FirePHP ?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Profiler | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
'profiler' => array( 'profiler' => TRUE,
'run' => TRUE,
)
)); ));
// execute some queries...
for ($i=0; $i<20; $i++) { for ($i=0; $i<20; $i++) {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', $i); $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', $i);
} }
// display output
?> ?>
<h1>Dibi profiler example</h1>
<p>Last query: <strong><?php echo dibi::$sql; ?></strong></p> <p>Last query: <strong><?php echo dibi::$sql; ?></strong></p>
<p>Number of queries: <strong><?php echo dibi::$numOfQueries; ?></strong></p> <p>Number of queries: <strong><?php echo dibi::$numOfQueries; ?></strong></p>
@@ -41,4 +33,4 @@ for ($i=0; $i<20; $i++) {
<ul> <ul>
<li>Firebug: https://addons.mozilla.org/en-US/firefox/addon/1843 <li>Firebug: https://addons.mozilla.org/en-US/firefox/addon/1843
<li>FirePHP: http://www.firephp.org/ <li>FirePHP: http://www.firephp.org/
</ul> </ul>

View File

@@ -1,55 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Query Language & Conditions | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// some variables
$cond1 = TRUE;
$cond2 = FALSE;
$foo = -1;
$bar = 2;
// conditional variable
$name = $cond1 ? 'K%' : NULL;
// if & end
dibi::test('
SELECT *
FROM customers
%if', isset($name), 'WHERE name LIKE ?', $name, '%end'
);
// -> SELECT * FROM customers WHERE name LIKE 'K%'
// if & else & (optional) end
dibi::test("
SELECT *
FROM people
WHERE id > 0
%if", ($foo > 0), "AND foo=?", $foo, "
%else %if", ($bar > 0), "AND bar=?", $bar, "
");
// -> SELECT * FROM people WHERE id > 0 AND bar=2
// nested condition
dibi::test('
SELECT *
FROM customers
WHERE
%if', isset($name), 'name LIKE ?', $name, '
%if', $cond2, 'AND admin=1 %end
%else 1 LIMIT 10 %end'
);
// -> SELECT * FROM customers WHERE LIMIT 10

View File

@@ -1,88 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Query Language Basic Examples | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// SELECT
$ipMask = '192.168.%';
$timestamp = mktime(0, 0, 0, 10, 13, 1997);
dibi::test('
SELECT COUNT(*) as [count]
FROM [comments]
WHERE [ip] LIKE ?', $ipMask, '
AND [date] > ', new DibiDateTime($timestamp)
);
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// dibi detects INSERT or REPLACE command
dibi::test('
REPLACE INTO products', array(
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
));
// -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1)
// multiple INSERT command
$array = array(
'title' => 'Super Product',
'price' => 12,
'brand' => NULL,
'created' => new DateTime,
);
dibi::test("INSERT INTO products", $array, $array, $array);
// -> INSERT INTO products ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...)
// dibi detects UPDATE command
dibi::test("
UPDATE colors SET", array(
'color' => 'blue',
'order' => 12,
), "
WHERE id=?", 123);
// -> UPDATE colors SET [color]='blue', [order]=12 WHERE id=123
// modifier applied to array
$array = array(1, 2, 3);
dibi::test("
SELECT *
FROM people
WHERE id IN (?)", $array
);
// -> SELECT * FROM people WHERE id IN ( 1, 2, 3 )
// modifier %by for ORDER BY
$order = array(
'field1' => 'asc',
'field2' => 'desc',
);
dibi::test("
SELECT *
FROM people
ORDER BY %by", $order, "
");
// -> SELECT * FROM people ORDER BY [field1] ASC, [field2] DESC
// indentifiers and strings syntax mix
dibi::test('UPDATE [table] SET `item` = "5 1/4"" diskette"');
// -> UPDATE [table] SET [item] = '5 1/4" diskette'

View File

@@ -1,46 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
ndebug();
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// using manual hints
$res = dibi::query('SELECT * FROM [customers]');
$res->setType('customer_id', Dibi::INTEGER)
->setType('added', Dibi::DATETIME)
->setFormat(dibi::DATETIME, 'Y-m-d H:i:s');
dump( $res->fetch() );
// outputs:
// object(DibiRow)#3 (3) {
// customer_id => int(1)
// name => string(11) "Dave Lister"
// added => object(DateTime53) {}
// }
// using auto-detection (works well with MySQL or other strictly typed databases)
$res = dibi::query('SELECT * FROM [customers]');
dump( $res->fetch() );
// outputs:
// object(DibiRow)#3 (3) {
// customer_id => int(1)
// name => string(11) "Dave Lister"
// added => string(15) "17:20 11.3.2007"
// }

90
examples/sql-builder.php Normal file
View File

@@ -0,0 +1,90 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<h1>dibi SQL builder example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
// required since PHP 5.1.0
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// dibi detects INSERT or REPLACE command
dibi::test('
REPLACE INTO [products]', array(
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
));
// -> REPLACE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1)
// multiple INSERT command
$array = array(
'title' => 'Super Product',
'price' => 12,
'brand' => NULL,
'created' => dibi::datetime(),
);
dibi::test("INSERT INTO [products]", $array, $array, $array);
// -> INSERT INTO [products] ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...)
// dibi detects UPDATE command
dibi::test("
UPDATE [colors] SET", array(
'color' => 'blue',
'order' => 12,
), "
WHERE [id]=%i", 123);
// -> UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123
// SELECT
$ipMask = '192.168.%';
$timestamp = mktime(0, 0, 0, 10, 13, 1997);
dibi::test('
SELECT COUNT(*) as [count]
FROM [comments]
WHERE [ip] LIKE %s', $ipMask, '
AND [date] > ', dibi::date($timestamp)
);
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// IN array
$array = array(1, 2, 3);
dibi::test("
SELECT *
FROM [people]
WHERE [id] IN (", $array, ")
");
// -> SELECT * FROM [people] WHERE [id] IN ( 1, 2, 3 )
// ORDER BY array
$order = array(
'field1' => 'asc',
'field2' => 'desc',
);
dibi::test("
SELECT *
FROM [people]
ORDER BY %by", $order, "
");
// -> SELECT * FROM [people] ORDER BY [field1] ASC, [field2] DESC

View File

@@ -0,0 +1,58 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<h1>dibi conditional SQL example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$cond1 = TRUE;
$cond2 = FALSE;
$foo = -1;
$bar = 2;
$name = $cond1 ? 'K%' : NULL;
// if & end
dibi::test('
SELECT *
FROM [customers]
%if', isset($name), 'WHERE [name] LIKE %s', $name, '%end'
);
// -> SELECT * FROM [customers] WHERE [name] LIKE 'K%'
// if & else & (optional) end
dibi::test("
SELECT *
FROM [people]
WHERE [id] > 0
%if", ($foo > 0), "AND [foo]=%i", $foo, "
%else %if", ($bar > 0), "AND [bar]=%i", $bar, "
");
// -> SELECT * FROM [people] WHERE [id] > 0 AND [bar]=2
// nested condition
dibi::test('
SELECT *
FROM [customers]
WHERE
%if', isset($name), '[name] LIKE %s', $name, '
%if', $cond2, 'AND [admin]=1 %end
%else 1 LIMIT 10 %end'
);
// -> SELECT * FROM [customers] WHERE LIMIT 10

45
examples/table-prefix.php Normal file
View File

@@ -0,0 +1,45 @@
<h1>dibi prefix & substitute example</h1>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// create new substitution :blog: ==> wp_
dibi::addSubst('blog', 'wp_');
dibi::test("UPDATE [:blog:items] SET [text]='Hello World'");
// -> UPDATE [wp_items] SET [text]='Hello World'
// create new substitution :: (empty) ==> my_
dibi::addSubst('', 'my_');
dibi::test("UPDATE [database.::table] SET [text]='Hello World'");
// -> UPDATE [database].[my_table] SET [text]='Hello World'
// create substitution fallback
function substFallBack($expr)
{
return 'the_' . $expr;
}
dibi::setSubstFallBack('substFallBack');
dibi::test("UPDATE [:account:user] SET [name]='John Doe'");
// -> UPDATE [the_accountuser] SET [name]='John Doe'

View File

@@ -1,20 +1,18 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css"> <h1>dibi transaction example</h1>
<pre>
<h1>Using Transactions | dibi</h1>
<?php <?php
require_once 'Nette/Debugger.php'; require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php'; require_once '../dibi/dibi.php';
dibi::connect(array( dibi::connect(array(
'driver' => 'sqlite', 'driver' => 'sqlite',
'database' => 'data/sample.sdb', 'database' => 'sample.sdb',
)); ));
echo "<h2>Before</h2>\n"; echo "<h2>Before:</h2>\n";
dibi::query('SELECT * FROM [products]')->dump(); dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows // -> 3 rows
@@ -23,13 +21,10 @@ dibi::begin();
dibi::query('INSERT INTO [products]', array( dibi::query('INSERT INTO [products]', array(
'title' => 'Test product', 'title' => 'Test product',
)); ));
echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
dibi::rollback(); // or dibi::commit(); dibi::rollback(); // or dibi::commit();
echo "<h2>After rollback</h2>\n";
echo "<h2>After:</h2>\n";
dibi::query('SELECT * FROM [products]')->dump(); dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows again // -> 3 rows

View File

@@ -1,39 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Logger | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
// enable query logging to this file
'profiler' => array(
'run' => TRUE,
'file' => 'data/log.sql',
),
));
try {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = ?', 1);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', 5);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < ?', 38);
} catch (DibiException $e) {
echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>';
}
// outputs a log file
echo "<h2>File data/log.sql:</h2>";
echo '<pre>', file_get_contents('data/log.sql'), '</pre>';

View File

@@ -1,54 +0,0 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Substitutions | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// create new substitution :blog: ==> wp_
dibi::getSubstitutes()->blog = 'wp_';
dibi::test("SELECT * FROM [:blog:items]");
// -> SELECT * FROM [wp_items]
// create new substitution :: (empty) ==> my_
dibi::getSubstitutes()->{''} = 'my_';
dibi::test("UPDATE ::table SET [text]='Hello World'");
// -> UPDATE my_table SET [text]='Hello World'
// create substitutions using fallback callback
function substFallBack($expr)
{
$const = 'SUBST_' . strtoupper($expr);
if (defined($const)) {
return constant($const);
} else {
throw new Exception("Undefined substitution :$expr:");
}
}
// define callback
dibi::getSubstitutes()->setCallback('substFallBack');
// define substitutes as constants
define('SUBST_ACCOUNT', 'eshop_');
define('SUBST_ACTIVE', 7);
dibi::test("
UPDATE :account:user
SET name='John Doe', status=:active:
WHERE id=", 7
);
// -> UPDATE eshop_user SET name='John Doe', status=7 WHERE id= 7

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

16
icons/example.html Normal file
View File

@@ -0,0 +1,16 @@
<style>
h1 {
font-family: Trebuchet MS,"Geneva CE", lucida, sans-serif;
color: #1e5eb6;
}
img {
border: none;
}
</style>
<h1>Icon for your website</h1>
<a href="http://dibiphp.com" title="dibi - tiny 'n' smart database abstraction layer"
><img src="dibi-powered.gif" width="80" height="15" alt="dibi powered" /></a>

69
license.cs.txt Normal file
View File

@@ -0,0 +1,69 @@
----------------------------------------------------------------------------------
Tento text je NEOFICI<43>LN<4C>M p<>ekladem "Dibi license". Nevyjad<61>uje pr<70>vn<76> podstatu
podm<EFBFBD>nek pro <20><><EFBFBD>en<65> tohoto softwaru - k tomuto <20><>elu slou<6F><75> v<>hradn<64> p<>vodn<64>
anglick<EFBFBD> verze licence.
----------------------------------------------------------------------------------
Dibi Licence, verze 1
======================
Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
V<EFBFBD>echna pr<70>va vyhrazena.
Tato licence je pr<70>vn<76> ujedn<64>n<EFBFBD> mezi v<>mi a Davidem Grudlem (d<>le Author)
pro pot<6F>eby pou<6F>it<69> "dibi" (d<>le "Software"). Z<>sk<73>n<EFBFBD>m, pou<6F>it<69>m
a/nebo zkop<6F>rov<6F>n<EFBFBD>m Software projevujete souhlas s t<>m, <20>e jste p<>e<EFBFBD>etli,
porozum<EFBFBD>li a budete jednat v souladu s podm<64>nkami t<>to licence.
POVOLEN<EFBFBD> POU<4F>IT<49>
----------------
Je povoleno pou<6F><75>vat, kop<6F>rovat, modifikovat a distribuovat Software
a jeho dokumentaci, v p<>vodn<64>m i upravovan<61>m tvaru, pro jak<61>koliv <20><>el,
za p<>edpokladu, <20>e jsou spln<6C>ny tyto podm<64>nky:
1. Kopie t<>to licen<65>n<EFBFBD> smlouvy mus<75> b<>t sou<6F><75>st<73> distribuce.
2. <20><><EFBFBD>en<65> zdrojov<6F> k<>d mus<75> zachovat v<><76>e uvedenou informaci o autorsk<73>ch
pr<70>vech ve v<>ech souborech zdrojov<6F>ho k<>du.
3. <20><><EFBFBD>en<65> bin<69>rn<72> tvar mus<75> reprodukovat v<><76>e uvedenou informaci o autorsk<73>ch
pr<70>vech v dokumentaci a/nebo jin<69>ch materi<72>lech poskytovan<61>ch s distribuc<75>.
4. Produkty odvozen<65> od Software mus<75> obsahovat potvrzen<65>, <20>e jsou odvozen<65>
od "dibi", ve sv<73> dokumentaci a/nebo jin<69>ch materi<72>lech
poskytovan<61>ch s distribuc<75>.
5. N<>zev "dibi" nesm<73> b<>t pou<6F>it p<>i podpo<70>e nebo propagaci produkt<6B>
odvozen<65>mi ze Software bez p<>edchoz<6F>ho p<>semn<6D>ho souhlasu Autora.
6. Produkty odvozen<65> od Software nesm<73> b<>t nazv<7A>ny "dibi",
ani se nesm<73> "dibi" objevit v jejich n<>zvu bez p<>edchoz<6F>ho
p<>semn<6D>ho souhlasu Autora.
ZBAVEN<EFBFBD> ZODPOV<4F>DNOSTI
---------------------
Souhlas<EFBFBD>te se zbaven<65>m zodpov<6F>dnosti a kryt<79>m Autora a p<>isp<73>vatel<65> v<><76>i
jak<EFBFBD>mkoliv p<><70>m<EFBFBD>m, nep<65><70>m<EFBFBD>m, n<>hodn<64>m nebo n<>sledn<64>m odjinud poch<63>zej<65>c<EFBFBD>m <20>kod<6F>m,
<EFBFBD>alob<EFBFBD>m nebo spor<6F>m, jako<6B> i p<>ed v<>emi souvisej<65>c<EFBFBD>mi n<>klady, z<>vazky,
od<EFBFBD>kodn<EFBFBD>n<EFBFBD>mi, <20>hradami nebo poplatky vypl<70>vaj<61>c<EFBFBD>ch z pou<6F><75>v<EFBFBD>n<EFBFBD> nebo
nespr<EFBFBD>vn<EFBFBD>ho u<>it<69> Software, nebo z poru<72>en<65> podm<64>nek t<>to licence.
Z<EFBFBD>RUKA SE NEPOSKYTUJE
---------------------
TENTO SOFTWARE JE POSKYTOV<4F>N DR<44>ITELEM LICENCE A JEHO P<>ISP<53>VATELI "JAK STOJ<4F> A LE<4C><45>"
A JAK<41>KOLIV V<>SLOVN<56> NEBO P<>EDPOKL<4B>DAN<41> Z<>RUKY V<>ETN<54>, ALE NEJEN, P<>EDPOKL<4B>DAN<41>CH
OBCHODN<EFBFBD>CH Z<>RUK A Z<>RUKY VHODNOSTI PRO JAK<41>KOLIV <20><>EL JSOU POP<4F>ENY.
DR<EFBFBD>ITEL, ANI P<>ISP<53>VATEL<45> NEBUDOU V <20><>DN<44>M P<><50>PAD<41> ODPOV<4F>DNI ZA JAK<41>KOLIV P<><50>M<EFBFBD>,
NEP<EFBFBD><EFBFBD>M<EFBFBD>, N<>HODN<44>, ZVL<56><4C>TN<54>, P<><50>KLADN<44> NEBO VYPL<50>VAJ<41>C<EFBFBD> <20>KODY (V<>ETN<54>, ALE NEJEN,
<EFBFBD>KOD VZNIKL<4B>CH NARU<52>EN<45>M DOD<4F>VEK ZBO<42><4F> NEBO SLU<4C>EB; ZTR<54>TOU POU<4F>ITELNOSTI,
DAT NEBO ZISK<53>; NEBO P<>ERU<52>EN<45>M OBCHODN<44> <20>INNOSTI) JAKKOLIV ZP<5A>SOBEN<45> NA Z<>KLAD<41>
JAK<EFBFBD>KOLIV TEORIE O ZODPOV<4F>DNOSTI, A<> U<> PLYNOUC<55> Z JIN<49>HO SMLUVN<56>HO VZTAHU,
UR<EFBFBD>IT<EFBFBD> ZODPOV<4F>DNOSTI NEBO P<>E<EFBFBD>INU (V<>ETN<54> NEDBALOSTI) NA JAK<41>MKOLIV ZP<5A>SOBU POU<4F>IT<49>
TOHOTO SOFTWARE, I V P<><50>PAD<41>, <20>E DR<44>ITEL PR<50>V BYL UPOZORN<52>N NA MO<4D>NOST TAKOV<4F>CH <20>KOD.

View File

@@ -1,61 +1,62 @@
Licenses The Dibi License, Version 1
======== ============================
Good news! You may use Dibi under the terms of either the New BSD License Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the library. If the GPL
fits better to your project, you can use the Dibi under this license.
You don't have to notify anyone which license you are using. You can freely
use Dibi in commercial projects as long as the copyright header
remains intact.
Please do not use word "Dibi" in the name of your project or top-level domain,
and choose a name that stands on its own merits. If your stuff is good,
it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com)
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, This license is a legal agreement between you and David Grudl (the "Author")
are permitted provided that the following conditions are met: for the use of "dibi" (the "Software"). By obtaining, using and/or
copying the Software, you agree that you have read, understood, and will
comply with the terms and conditions of this license.
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, PERMITTED USE
this list of conditions and the following disclaimer in the documentation -------------
and/or other materials provided with the distribution.
* Neither the name of "Dibi" nor the names of its contributors You are permitted to use, copy, modify, and distribute the Software and its
may be used to endorse or promote products derived from this software documentation, with or without modification, for any purpose, provided that
without specific prior written permission. the following conditions are met:
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 1. A copy of this license agreement must be included with the distribution.
2. Redistributions of source code must retain the above copyright notice in
all source code files.
3. Redistributions in binary form must reproduce the above copyright notice
in the documentation and/or other materials provided with the distribution.
4. Products derived from the Software must include an acknowledgment that
they are derived from "dibi" in their documentation and/or other
materials provided with the distribution.
5. The name "dibi" must not be used to endorse or promote products
derived from the Software without prior written permission from Author.
6. Products derived from the Software may not be called "dibi",
nor may "dibi" appear in their name, without prior written
permission from Author.
INDEMNITY
---------
You agree to indemnify and hold harmless the Author and any contributors
for any direct, indirect, incidental, or consequential third-party claims,
actions or suits, as well as any related expenses, liabilities, damages,
settlements or fees arising from your use or misuse of the Software,
or a violation of any terms of this license.
DISCLAIMER OF WARRANTY
----------------------
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- GPL version 2: http://www.gnu.org/licenses/gpl-2.0.html
- GPL version 3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@@ -1,3 +1,7 @@
Dibi (c) David Grudl, 2005-2008 (http://davidgrudl.com)
Introduction Introduction
------------ ------------
@@ -6,6 +10,10 @@ Thank you for downloading Dibi!
Database access functions in PHP are not standardised. This is class library Database access functions in PHP are not standardised. This is class library
to hide the differences between the different databases access. to hide the differences between the different databases access.
The files in this archive are released under the Dibi license.
See license.txt in this directory for a copy of the license.
Documentation and Examples Documentation and Examples
-------------------------- --------------------------
@@ -16,8 +24,9 @@ available on the homepage:
http://dibiphp.com http://dibiphp.com
Dibi.minified
------------- Dibi.compact
------------
This is shrinked single-file version of whole Dibi, useful when you don't This is shrinked single-file version of whole Dibi, useful when you don't
want to modify library, but just use it. want to modify library, but just use it.
@@ -26,6 +35,7 @@ This is exactly the same as normal version, just only comments and
whitespaces are removed. whitespaces are removed.
----- -----
For more information, visit the author's weblog (in czech language): For more information, visit the author's weblog (in czech language):
http://phpfashion.com http://phpfashion.com

View File

@@ -1,69 +0,0 @@
<?php
/**
* Test: Cloning of DibiFluent
*
* @author David Grudl
* @category Dibi
* @subpackage UnitTests
*/
require dirname(__FILE__) . '/initialize.php';
dibi::connect($config['sqlite']);
$fluent = new DibiFluent(dibi::getConnection());
$fluent->select('*')->from('table')->where('x=1');
$dolly = clone $fluent;
$dolly->where('y=1');
$dolly->clause('FOO');
$fluent->test();
$dolly->test();
$fluent = dibi::select('id')->from('table')->where('id = %i',1);
$dolly = clone $fluent;
$dolly->where('cd = %i',5);
$fluent->test();
$dolly->test();
$fluent = dibi::select("*")->from("table");
$dolly = clone $fluent;
$dolly->removeClause("select")->select("count(*)");
$fluent->test();
$dolly->test();
__halt_compiler() ?>
------EXPECT------
SELECT *
FROM [table]
WHERE x=1
SELECT *
FROM [table]
WHERE x=1 AND y=1 FOO
SELECT [id]
FROM [table]
WHERE id = 1
SELECT [id]
FROM [table]
WHERE id = 1 AND cd = 5
SELECT *
FROM [table]
SELECT count(*)
FROM [table]

View File

@@ -1,138 +0,0 @@
<?php
/**
* Nette Framework
*
* @copyright Copyright (c) 2004 David Grudl
* @license http://nette.org/license Nette license
* @link http://nette.org
* @category Nette
* @package Nette\Test
*/
/**
* Asseratation test helpers.
*
* @author David Grudl
* @package Nette\Test
*/
class Assert
{
/**
* Checks assertation.
* @param mixed expected
* @param mixed actual
* @return void
*/
public static function same($expected, $actual)
{
if ($actual !== $expected) {
self::note('Failed asserting that ' . self::dump($actual) . ' is not identical to ' . self::dump($expected));
}
}
/**
* Checks TRUE assertation.
* @param mixed actual
* @return void
*/
public static function true($actual)
{
if ($actual !== TRUE) {
self::note('Failed asserting that ' . self::dump($actual) . ' is not TRUE');
}
}
/**
* Checks FALSE assertation.
* @param mixed actual
* @return void
*/
public static function false($actual)
{
if ($actual !== FALSE) {
self::note('Failed asserting that ' . self::dump($actual) . ' is not FALSE');
}
}
/**
* Checks NULL assertation.
* @param mixed actual
* @return void
*/
public static function null($actual)
{
if ($actual !== NULL) {
self::note('Failed asserting that ' . self::dump($actual) . ' is not NULL');
}
}
/**
* Dumps information about a variable in readable format.
* @param mixed variable to dump
* @return void
*/
private static function dump($var)
{
if (is_bool($var)) {
return $var ? 'TRUE' : 'FALSE';
} elseif ($var === NULL) {
return "NULL";
} elseif (is_int($var)) {
return "$var";
} elseif (is_float($var)) {
return "$var";
} elseif (is_string($var)) {
return var_export($var, TRUE);
} elseif (is_array($var)) {
return "array(" . count($var) . ")";
} elseif ($var instanceof Exception) {
return 'Exception ' . get_class($var) . ': ' . ($var->getCode() ? '#' . $var->getCode() . ' ' : '') . $var->getMessage();
} elseif (is_object($var)) {
$arr = (array) $var;
return "object(" . get_class($var) . ") (" . count($arr) . ")";
} elseif (is_resource($var)) {
return "resource(" . get_resource_type($var) . ")";
} else {
return "unknown type";
}
}
/**
* Returns message and file and line from call stack.
* @param string
* @return void
*/
private static function note($message)
{
echo $message;
$trace = debug_backtrace();
if (isset($trace[1]['file'], $trace[1]['line'])) {
echo ' in file ' . $trace[1]['file'] . ' on line ' . $trace[1]['line'];
}
echo "\n\n";
}
}

View File

@@ -1,43 +0,0 @@
Nette Test Framework (v0.3)
---------------------------
<?php
require_once dirname(__FILE__) . '/TestRunner.php';
/**
* Help
*/
if (!isset($_SERVER['argv'][1])) { ?>
Usage:
php RunTests.php [options] [file or directory]
Options:
-p <php> Specify PHP-CGI executable to run.
-c <path> Look for php.ini in directory <path> or use <path> as php.ini.
-d key=val Define INI entry 'key' with value 'val'.
-l <path> Specify path to shared library files (LD_LIBRARY_PATH)
-e <name> Load php environment <name>
-s Show information about skipped tests
<?php
}
/**
* Execute tests
*/
try {
@unlink(dirname(__FILE__) . '/coverage.tmp'); // @ - file may not exist
$manager = new TestRunner;
$manager->parseConfigFile();
$manager->parseArguments();
$res = $manager->run();
die($res ? 0 : 1);
} catch (Exception $e) {
echo 'Error: ', $e->getMessage(), "\n";
die(2);
}

View File

@@ -1,400 +0,0 @@
<?php
/**
* Nette Framework
*
* @copyright Copyright (c) 2004 David Grudl
* @license http://nette.org/license Nette license
* @link http://nette.org
* @category Nette
* @package Nette\Test
*/
/**
* Single test case.
*
* @author David Grudl
* @package Nette\Test
*/
class TestCase
{
/** @var string test file */
private $file;
/** @var array test file multiparts */
private $sections;
/** @var string test output */
private $output;
/** @var string output headers in raw format */
private $headers;
/** @var string PHP command line */
private $cmdLine;
/** @var string PHP command line */
private $phpVersion;
/** @var array */
private static $cachedPhp;
/**
* @param string test file name
* @param string PHP command line
* @return void
*/
public function __construct($testFile)
{
$this->file = (string) $testFile;
$this->sections = self::parseSections($this->file);
}
/**
* Runs single test.
* @return void
*/
public function run()
{
// pre-skip?
$options = $this->sections['options'];
if (isset($options['skip'])) {
$message = $options['skip'] ? $options['skip'] : 'No message.';
throw new TestCaseException($message, TestCaseException::SKIPPED);
} elseif (isset($options['phpversion'])) {
$operator = '>=';
if (preg_match('#^(<=|le|<|lt|==|=|eq|!=|<>|ne|>=|ge|>|gt)#', $options['phpversion'], $matches)) {
$options['phpversion'] = trim(substr($options['phpversion'], strlen($matches[1])));
$operator = $matches[1];
}
if (version_compare($options['phpversion'], $this->phpVersion, $operator)) {
throw new TestCaseException("Requires PHP $operator $options[phpversion].", TestCaseException::SKIPPED);
}
}
$this->execute();
$output = $this->output;
$headers = array_change_key_case(self::parseLines($this->headers, ':'), CASE_LOWER);
$tests = 0;
// post-skip?
if (isset($headers['x-nette-test-skip'])) {
throw new TestCaseException($headers['x-nette-test-skip'], TestCaseException::SKIPPED);
}
// compare output
$expectedOutput = $this->getExpectedOutput();
if ($expectedOutput !== NULL) {
$tests++;
$binary = (bool) preg_match('#[\x00-\x08\x0B\x0C\x0E-\x1F]#', $expectedOutput);
if ($binary) {
if ($expectedOutput !== $output) {
throw new TestCaseException("Binary output doesn't match.");
}
} else {
$output = self::normalize($output, isset($options['keeptrailingspaces']));
$expectedOutput = self::normalize($expectedOutput, isset($options['keeptrailingspaces']));
if (!$this->compare($output, $expectedOutput)) {
throw new TestCaseException("Output doesn't match.");
}
}
}
// compare headers
$expectedHeaders = $this->getExpectedHeaders();
if ($expectedHeaders !== NULL) {
$tests++;
$expectedHeaders = self::normalize($expectedHeaders, FALSE);
$expectedHeaders = array_change_key_case(self::parseLines($expectedHeaders, ':'), CASE_LOWER);
foreach ($expectedHeaders as $name => $header) {
if (!isset($headers[$name])) {
throw new TestCaseException("Missing header '$name'.");
} elseif (!$this->compare($headers[$name], $header)) {
throw new TestCaseException("Header '$name' doesn't match.");
}
}
}
if (!$tests) { // expecting no output
if (trim($output) !== '') {
throw new TestCaseException("Empty output doesn't match.");
}
}
}
/**
* Sets PHP command line.
* @param string
* @param string
* @param string
* @return TestCase provides a fluent interface
*/
public function setPhp($binary, $args, $environment)
{
if (isset(self::$cachedPhp[$binary])) {
$this->phpVersion = self::$cachedPhp[$binary];
} else {
exec($environment . escapeshellarg($binary) . ' -v', $output, $res);
if ($res !== 0 && $res !== 255) {
throw new Exception("Unable to execute '$binary -v'.");
}
if (!preg_match('#^PHP (\S+).*cli#i', $output[0], $matches)) {
throw new Exception("Unable to detect PHP version (output: $output[0]).");
}
$this->phpVersion = self::$cachedPhp[$binary] = $matches[1];
}
$this->cmdLine = $environment . escapeshellarg($binary) . $args;
return $this;
}
/**
* Execute test.
* @return array
*/
private function execute()
{
$this->headers = $this->output = NULL;
$tempFile = tempnam('', 'tmp');
if (!$tempFile) {
throw new Exception("Unable to create temporary file.");
}
$command = $this->cmdLine;
if (isset($this->sections['options']['phpini'])) {
foreach (explode(';', $this->sections['options']['phpini']) as $item) {
$command .= " -d " . escapeshellarg(trim($item));
}
}
$command .= ' ' . escapeshellarg($this->file) . ' > ' . escapeshellarg($tempFile);
chdir(dirname($this->file));
exec($command, $foo, $res);
if ($res === 255) {
// exit_status 255 => parse or fatal error
} elseif ($res !== 0) {
throw new Exception("Unable to execute '$command'.");
}
$this->output = file_get_contents($tempFile);
unlink($tempFile);
}
/**
* Returns test file section.
* @return string
*/
public function getSection($name)
{
return isset($this->sections[$name]) ? $this->sections[$name] : NULL;
}
/**
* Returns test name.
* @return string
*/
public function getName()
{
return $this->sections['options']['name'];
}
/**
* Returns test output.
* @return string
*/
public function getOutput()
{
return $this->output;
}
/**
* Returns output headers.
* @return string
*/
public function getHeaders()
{
return $this->headers;
}
/**
* Returns expected output.
* @return string
*/
public function getExpectedOutput()
{
if (isset($this->sections['expect'])) {
return $this->sections['expect'];
} elseif (is_file($expFile = str_replace('.phpt', '', $this->file) . '.expect')) {
return file_get_contents($expFile);
} else {
return NULL;
}
}
/**
* Returns expected headers.
* @return string
*/
public function getExpectedHeaders()
{
return $this->getSection('expectheaders');
}
/********************* helpers ****************d*g**/
/**
* Splits file into sections.
* @param string file
* @return array
*/
public static function parseSections($testFile)
{
$content = file_get_contents($testFile);
$sections = array(
'options' => array(),
);
// phpDoc
$phpDoc = preg_match('#^/\*\*(.*?)\*/#ms', $content, $matches) ? trim($matches[1]) : '';
preg_match_all('#^\s*\*\s*@(\S+)(.*)#mi', $phpDoc, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$sections['options'][strtolower($match[1])] = isset($match[2]) ? trim($match[2]) : TRUE;
}
$sections['options']['name'] = preg_match('#^\s*\*\s*TEST:(.*)#mi', $phpDoc, $matches) ? trim($matches[1]) : $testFile;
// file parts
$tmp = preg_split('#^-{3,}([^\s-]+)-{1,}(?:\r?\n|$)#m', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
$i = 1;
while (isset($tmp[$i])) {
$sections[strtolower($tmp[$i])] = $tmp[$i+1];
$i += 2;
}
return $sections;
}
/**
* Splits HTTP headers into array.
* @param string
* @param string
* @return array
*/
public static function parseLines($raw, $separator)
{
$headers = array();
foreach (explode("\r\n", $raw) as $header) {
$a = strpos($header, $separator);
if ($a !== FALSE) {
$headers[trim(substr($header, 0, $a))] = (string) trim(substr($header, $a + 1));
}
}
return $headers;
}
/**
* Compares results.
* @param string
* @param string
* @return bool
*/
public static function compare($left, $right)
{
$right = strtr($right, array(
'%a%' => '[^\r\n]+', // one or more of anything except the end of line characters
'%a?%'=> '[^\r\n]*', // zero or more of anything except the end of line characters
'%A%' => '.+', // one or more of anything including the end of line characters
'%A?%'=> '.*', // zero or more of anything including the end of line characters
'%s%' => '[\t ]+', // one or more white space characters except the end of line characters
'%s?%'=> '[\t ]*', // zero or more white space characters except the end of line characters
'%S%' => '\S+', // one or more of characters except the white space
'%S?%'=> '\S*', // zero or more of characters except the white space
'%c%' => '[^\r\n]', // a single character of any sort (except the end of line)
'%d%' => '[0-9]+', // one or more digits
'%d?%'=> '[0-9]*', // zero or more digits
'%i%' => '[+-]?[0-9]+', // signed integer value
'%f%' => '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', // floating point number
'%h%' => '[0-9a-fA-F]+',// one or more HEX digits
'%ns%'=> '(?:[_0-9a-zA-Z\\\\]+\\\\|N)?',// PHP namespace
'%[^' => '[^', // reg-exp
'%[' => '[', // reg-exp
']%' => ']+', // reg-exp
'.' => '\.', '\\' => '\\\\', '+' => '\+', '*' => '\*', '?' => '\?', '[' => '\[', '^' => '\^', ']' => '\]', '$' => '\$', '(' => '\(', ')' => '\)', // preg quote
'{' => '\{', '}' => '\}', '=' => '\=', '!' => '\!', '>' => '\>', '<' => '\<', '|' => '\|', ':' => '\:', '-' => '\-', "\x00" => '\000', '#' => '\#', // preg quote
));
return (bool) preg_match("#^$right$#s", $left);
}
/**
* Normalizes whitespace
* @param string
* @param bool
* @return string
*/
public static function normalize($s, $keepTrailingSpaces)
{
$s = str_replace("\n", PHP_EOL, str_replace("\r\n", "\n", $s)); // normalize EOL
if (!$keepTrailingSpaces) {
$s = preg_replace("#[\t ]+(\r?\n)#", '$1', $s); // multiline right trim
$s = rtrim($s); // ending trim
}
return $s;
}
}
/**
* Single test exception.
*
* @author David Grudl
* @package Nette\Test
*/
class TestCaseException extends Exception
{
const SKIPPED = 1;
}

View File

@@ -1,312 +0,0 @@
<?php
/**
* Nette Framework
*
* @copyright Copyright (c) 2004 David Grudl
* @license http://nette.org/license Nette license
* @link http://nette.org
* @category Nette
* @package Nette\Test
*/
require dirname(__FILE__) . '/TestCase.php';
/**
* Test helpers.
*
* @author David Grudl
* @package Nette\Test
*/
class TestHelpers
{
/** @var int */
static public $maxDepth = 5;
/** @var array */
private static $sections;
/**
* Configures PHP and environment.
* @return void
*/
public static function startup()
{
error_reporting(E_ALL | E_STRICT);
ini_set('display_errors', TRUE);
ini_set('html_errors', FALSE);
ini_set('log_errors', FALSE);
$_SERVER = array_intersect_key($_SERVER, array_flip(array('PHP_SELF', 'SCRIPT_NAME', 'SERVER_ADDR', 'SERVER_SOFTWARE', 'HTTP_HOST', 'DOCUMENT_ROOT', 'OS')));
$_SERVER['REQUEST_TIME'] = 1234567890;
$_ENV = array();
if (PHP_SAPI !== 'cli') {
header('Content-Type: text/plain; charset=utf-8');
}
if (extension_loaded('xdebug')) {
xdebug_disable();
xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
register_shutdown_function(array(__CLASS__, 'prepareSaveCoverage'));
}
set_exception_handler(array(__CLASS__, 'exceptionHandler'));
}
/**
* Purges directory.
* @param string
* @return void
*/
public static function purge($dir)
{
@mkdir($dir); // @ - directory may already exist
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::CHILD_FIRST) as $entry) {
if ($entry->getBasename() === '.gitignore') {
// ignore
} elseif ($entry->isDir()) {
rmdir($entry);
} else {
unlink($entry);
}
}
}
/**
* Returns current test section.
* @param string
* @param string
* @return mixed
*/
public static function getSection($file, $section)
{
if (!isset(self::$sections[$file])) {
self::$sections[$file] = TestCase::parseSections($file);
}
$lowerSection = strtolower($section);
if (!isset(self::$sections[$file][$lowerSection])) {
throw new Exception("Missing section '$section' in file '$file'.");
}
if (in_array($section, array('GET', 'POST', 'SERVER'), TRUE)) {
return TestCase::parseLines(self::$sections[$file][$lowerSection], '=');
} else {
return self::$sections[$file][$lowerSection];
}
}
/**
* Writes new message.
* @param string
* @return void
*/
public static function note($message = NULL)
{
echo $message ? "$message\n\n" : "===\n\n";
}
/**
* Dumps information about a variable in readable format.
* @param mixed variable to dump
* @param string
* @return mixed variable itself or dump
*/
public static function dump($var, $message = NULL)
{
if ($message) {
echo $message . (preg_match('#[.:?]$#', $message) ? ' ' : ': ');
}
self::_dump($var, 0);
echo "\n";
return $var;
}
private static function _dump(& $var, $level = 0)
{
static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
if ($tableUtf === NULL) {
foreach (range("\x00", "\xFF") as $ch) {
if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
elseif (ord($ch) < 127) $tableUtf[$ch] = $tableBin[$ch] = $ch;
else { $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); }
}
$tableBin["\\"] = '\\\\';
$tableBin["\r"] = '\\r';
$tableBin["\n"] = '\\n';
$tableBin["\t"] = '\\t';
$tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
}
if (is_bool($var)) {
echo ($var ? 'TRUE' : 'FALSE') . "\n";
} elseif ($var === NULL) {
echo "NULL\n";
} elseif (is_int($var)) {
echo "$var\n";
} elseif (is_float($var)) {
$var = (string) $var;
if (strpos($var, '.') === FALSE) $var .= '.0';
echo "$var\n";
} elseif (is_string($var)) {
$s = strtr($var, preg_match($reBinary, $var) || preg_last_error() ? $tableBin : $tableUtf);
echo "\"$s\"\n";
} elseif (is_array($var)) {
echo "array(";
$space = str_repeat("\t", $level);
static $marker;
if ($marker === NULL) $marker = uniqid("\x00", TRUE);
if (empty($var)) {
} elseif (isset($var[$marker])) {
echo " *RECURSION* ";
} elseif ($level < self::$maxDepth) {
echo "\n";
$vector = range(0, count($var) - 1) === array_keys($var);
$var[$marker] = 0;
foreach ($var as $k => &$v) {
if ($k === $marker) continue;
if ($vector) {
echo "$space\t";
} else {
$k = is_int($k) ? $k : '"' . strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf) . '"';
echo "$space\t$k => ";
}
self::_dump($v, $level + 1);
}
unset($var[$marker]);
echo "$space";
} else {
echo " ... ";
}
echo ")\n";
} elseif ($var instanceof Exception) {
echo 'Exception ', get_class($var), ': ', ($var->getCode() ? '#' . $var->getCode() . ' ' : '') . $var->getMessage(), "\n";
} elseif (is_object($var)) {
$arr = (array) $var;
echo get_class($var) . "(";
$space = str_repeat("\t", $level);
static $list = array();
if (empty($arr)) {
} elseif (in_array($var, $list, TRUE)) {
echo " *RECURSION* ";
} elseif ($level < self::$maxDepth) {
echo "\n";
$list[] = $var;
foreach ($arr as $k => &$v) {
$m = '';
if ($k[0] === "\x00") {
$m = $k[1] === '*' ? ' protected' : ' private';
$k = substr($k, strrpos($k, "\x00") + 1);
}
$k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
echo "$space\t\"$k\"$m => ";
echo self::_dump($v, $level + 1);
}
array_pop($list);
echo "$space";
} else {
echo " ... ";
}
echo ")\n";
} elseif (is_resource($var)) {
echo get_resource_type($var) . " resource\n";
} else {
echo "unknown type\n";
}
}
/**
* Custom exception handler.
* @param Exception
* @return void
*/
public static function exceptionHandler(Exception $exception)
{
echo 'Error: Uncaught ';
echo $exception;
}
/**
* Coverage saving helper.
* @return void
*/
public static function prepareSaveCoverage()
{
register_shutdown_function(array(__CLASS__, 'saveCoverage'));
}
/**
* Saves information about code coverage.
* @return void
*/
public static function saveCoverage()
{
$file = dirname(__FILE__) . '/coverage.tmp';
$coverage = @unserialize(file_get_contents($file));
$root = realpath(dirname(__FILE__) . '/../../Nette') . DIRECTORY_SEPARATOR;
foreach (xdebug_get_code_coverage() as $filename => $lines) {
if (strncmp($root, $filename, strlen($root))) continue;
foreach ($lines as $num => $val) {
if (empty($coverage[$filename][$num]) || $val > 0) {
$coverage[$filename][$num] = $val; // -1 => untested; -2 => dead code
}
}
}
file_put_contents($file, serialize($coverage));
}
/**
* Skips this test.
* @return void
*/
public static function skip($message = 'No message.')
{
header('X-Nette-Test-Skip: '. $message);
exit;
}
}

View File

@@ -1,230 +0,0 @@
<?php
/**
* Nette Framework
*
* @copyright Copyright (c) 2004 David Grudl
* @license http://nette.org/license Nette license
* @link http://nette.org
* @category Nette
* @package Nette\Test
*/
require dirname(__FILE__) . '/TestCase.php';
/**
* Test runner.
*
* @author David Grudl
* @package Nette\Test
*/
class TestRunner
{
const OUTPUT = 'output';
const EXPECTED = 'expect';
const HEADERS = 'headers';
/** @var string path to test file/directory */
public $path;
/** @var string php-cgi binary */
public $phpBinary;
/** @var string php-cgi command-line arguments */
public $phpArgs;
/** @var string php-cgi environment variables */
public $phpEnvironment;
/** @var bool display skipped tests information? */
public $displaySkipped = FALSE;
/**
* Runs all tests.
* @return void
*/
public function run()
{
$count = 0;
$failed = $passed = $skipped = array();
if (is_file($this->path)) {
$files = array($this->path);
} else {
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->path));
}
foreach ($files as $entry) {
$entry = (string) $entry;
$info = pathinfo($entry);
if (!isset($info['extension']) || $info['extension'] !== 'phpt') {
continue;
}
$count++;
$testCase = new TestCase($entry);
$testCase->setPhp($this->phpBinary, $this->phpArgs, $this->phpEnvironment);
try {
$testCase->run();
echo '.';
$passed[] = array($testCase->getName(), $entry);
} catch (TestCaseException $e) {
if ($e->getCode() === TestCaseException::SKIPPED) {
echo 's';
$skipped[] = array($testCase->getName(), $entry, $e->getMessage());
} else {
echo 'F';
$failed[] = array($testCase->getName(), $entry, $e->getMessage());
$this->log($entry, $testCase->getOutput(), self::OUTPUT);
$this->log($entry, $testCase->getExpectedOutput(), self::EXPECTED);
if ($testCase->getExpectedHeaders() !== NULL) {
$this->log($entry, $testCase->getHeaders(), self::OUTPUT, self::HEADERS);
$this->log($entry, $testCase->getExpectedHeaders(), self::EXPECTED, self::HEADERS);
}
}
}
}
$failedCount = count($failed);
$skippedCount = count($skipped);
if ($this->displaySkipped && $skippedCount) {
echo "\n\nSkipped:\n";
foreach ($skipped as $i => $item) {
list($name, $file, $message) = $item;
echo "\n", ($i + 1), ") $name\n $message\n $file\n";
}
}
if (!$count) {
echo "No tests found\n";
} elseif ($failedCount) {
echo "\n\nFailures:\n";
foreach ($failed as $i => $item) {
list($name, $file, $message) = $item;
echo "\n", ($i + 1), ") $name\n $message\n $file\n";
}
echo "\nFAILURES! ($count tests, $failedCount failures, $skippedCount skipped)\n";
return FALSE;
} else {
echo "\n\nOK ($count tests, $skippedCount skipped)\n";
}
return TRUE;
}
/**
* Returns output file for logging.
* @param string
* @param string
* @param string
* @param string
* @return void
*/
public function log($testFile, $content, $type, $section = '')
{
$file = dirname($testFile) . '/' . $type . '/' . basename($testFile, '.phpt') . ($section ? ".$section" : '') . '.raw';
@mkdir(dirname($file)); // @ - directory may already exist
file_put_contents($file, $content);
}
/**
* Parses configuration file.
* @return void
*/
public function parseConfigFile()
{
$configFile = dirname(__FILE__) . '/config.ini';
if (file_exists($configFile)) {
$this->config = parse_ini_file($configFile, TRUE);
if ($this->config === FALSE) {
throw new Exception('Config file parsing failed.');
}
foreach ($this->config as & $environment) {
$environment += array(
'binary' => 'php-cgi',
'args' => '',
'environment' => '',
);
// shorthand options
if (isset($environment['php.ini'])) {
$environment['args'] .= ' -c '. escapeshellarg($environment['php.ini']);
}
if (isset($environment['libraries'])) {
$environment['environment'] .= 'LD_LIBRARY_PATH='. escapeshellarg($environment['libraries']) .' ';
}
}
}
}
/**
* Parses command line arguments.
* @return void
*/
public function parseArguments()
{
$this->phpBinary = 'php-cgi';
$this->phpArgs = '';
$this->phpEnvironment = '';
$this->path = getcwd(); // current directory
$args = new ArrayIterator(array_slice(isset($_SERVER['argv']) ? $_SERVER['argv'] : array(), 1));
foreach ($args as $arg) {
if (!preg_match('#^[-/][a-z]$#', $arg)) {
if ($path = realpath($arg)) {
$this->path = $path;
} else {
throw new Exception("Invalid path '$arg'.");
}
} else switch ($arg[1]) {
case 'p':
$args->next();
$this->phpBinary = $args->current();
break;
case 'c':
case 'd':
$args->next();
$this->phpArgs .= " -$arg[1] " . escapeshellarg($args->current());
break;
case 'l':
$args->next();
$this->phpEnvironment .= 'LD_LIBRARY_PATH='. escapeshellarg($args->current()) . ' ';
break;
case 'e':
$args->next();
$name = $args->current();
if (!isset($this->config[$name])) {
throw new Exception("Unknown environment name '$name'.");
}
$this->phpBinary = $this->config[$name]['binary'];
$this->phpArgs = $this->config[$name]['args'];
$this->phpEnvironment = $this->config[$name]['environment'];
break;
case 's':
$this->displaySkipped = TRUE;
break;
default:
throw new Exception("Unknown option -$arg[1].");
exit;
}
}
}
}

View File

@@ -1,67 +0,0 @@
[mysql]
driver = mysql
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[mysqli]
driver = mysqli
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[sqlite]
driver = sqlite
database = DIR "/data/sample.sdb"
[sqlite3]
driver = sqlite3
database = DIR "/data/sample.sdb3"
[odbc]
driver = odbc
username = dibi
password = dibi
dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=" DIR "/data/sample.mdb"
[postgresql]
driver = postgre
host = localhost
port = 5432
username = dibi
password = dibi
database = dibi_test
persistent = TRUE
[sqlite-pdo]
driver = pdo
dsn = "sqlite2::" DIR "/data/sample.sdb"
[mysql-pdo]
driver = pdo
dsn = "mysql:dbname=dibi_test;host=localhost"
username = dibi
password = dibi
[mssql]
driver = mssql
host = localhost
username = dibi
password = dibi
[mssql2005]
driver = mssql2005
host = "(local)"
username = dibi
password = dibi
database = dibi_test
[oracle]
driver = oracle
username = dibi
password = dibi
database = dibi_test

View File

@@ -1,149 +0,0 @@
-- phpMyAdmin SQL Dump
-- version 2.11.1.2
-- http://www.phpmyadmin.net
--
-- Po<50><6F>ta<74>: localhost
-- Vygenerov<6F>no: Ned<65>le 02. prosince 2007, 19:49
-- Verze MySQL: 5.0.45
-- Verze PHP: 5.2.1
SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- Datab<61>ze: `dibi`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `customers`
--
DROP TABLE IF EXISTS `customers`;
CREATE TABLE IF NOT EXISTS `customers` (
`customer_id` int(11) NOT NULL auto_increment,
`name` varchar(100) default NULL,
PRIMARY KEY (`customer_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
--
-- Vypisuji data pro tabulku `customers`
--
INSERT INTO `customers` (`customer_id`, `name`) VALUES
(1, 'Dave Lister'),
(2, 'Arnold Rimmer'),
(3, 'The Cat'),
(4, 'Holly'),
(5, 'Kryten'),
(6, 'Kristine Kochanski');
-- --------------------------------------------------------
--
-- Struktura tabulky `enumtest`
--
DROP TABLE IF EXISTS `enumtest`;
CREATE TABLE IF NOT EXISTS `enumtest` (
`id` int(11) NOT NULL auto_increment,
`test` enum('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `enumtest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `orders`
--
DROP TABLE IF EXISTS `orders`;
CREATE TABLE IF NOT EXISTS `orders` (
`order_id` int(11) NOT NULL,
`customer_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`amount` float NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `orders`
--
INSERT INTO `orders` (`order_id`, `customer_id`, `product_id`, `amount`) VALUES
(1, 2, 1, 7),
(2, 2, 3, 2),
(3, 1, 2, 3),
(4, 6, 3, 5);
-- --------------------------------------------------------
--
-- Struktura tabulky `products`
--
DROP TABLE IF EXISTS `products`;
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(11) NOT NULL auto_increment,
`title` varchar(100) default NULL,
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `products`
--
INSERT INTO `products` (`product_id`, `title`) VALUES
(1, 'Chair'),
(2, 'Table'),
(3, 'Computer');
-- --------------------------------------------------------
--
-- Struktura tabulky `settest`
--
DROP TABLE IF EXISTS `settest`;
CREATE TABLE IF NOT EXISTS `settest` (
`id` int(11) NOT NULL auto_increment,
`test` set('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `settest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `where`
--
DROP TABLE IF EXISTS `where`;
CREATE TABLE IF NOT EXISTS `where` (
`select` int(11) NOT NULL,
`dot.dot` int(11) NOT NULL,
`is` int(11) NOT NULL,
`quot'n' space` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `where`
--
INSERT INTO `where` (`select`, `dot.dot`, `is`, `quot'n' space`) VALUES
(1, 2, 3, 4);
SET FOREIGN_KEY_CHECKS=1;
SET SQL_MODE="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

Binary file not shown.

View File

@@ -1,26 +0,0 @@
<?php
/**
* Test initialization and helpers.
*
* @author David Grudl
* @package Nette\Test
*/
require dirname(__FILE__) . '/NetteTest/TestHelpers.php';
require dirname(__FILE__) . '/NetteTest/Assert.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
TestHelpers::startup();
if (function_exists('class_alias')) {
class_alias('TestHelpers', 'T');
} else {
class T extends TestHelpers {}
}
// load connections
define('DIR', dirname(__FILE__));
$config = parse_ini_file('config.ini', TRUE);

View File

@@ -1 +1 @@
Dibi 2.0.5 (released on 2015-01-13) Dibi 1.0 (revision $WCREV$ released on $WCDATE$)