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

Compare commits

..

25 Commits
v3.0.0 ... v2.3

Author SHA1 Message Date
David Grudl
fc6ef0b121 Released version 2.3.5 2015-12-16 15:14:32 +01:00
Jan Langer
96188d2edc Helpers::detectType resolves tinyint as integer
# Conflicts:
#	src/Dibi/Helpers.php
2015-12-16 15:14:32 +01:00
Michal Kočárek
f392728e0c PdoDriver: unset remaining references to PDO to allow disconnection 2015-12-16 15:14:31 +01:00
David Grudl
c14dd863b6 uses https 2015-12-16 15:09:30 +01:00
David Grudl
7812e74602 tests: improved 2015-11-26 12:27:37 +01:00
Petr Soukup
f457504037 DibiFluent: added missing annotations [Closes #191] 2015-11-06 23:11:56 +01:00
David Grudl
50324fd815 typo 2015-11-06 23:11:12 +01:00
David Grudl
8f8fd040ff tests: fixes 2015-11-04 17:29:05 +01:00
David Grudl
120f0946e0 tests: improved ini quering 2015-11-04 17:29:05 +01:00
David Grudl
fef3eccc61 tests: added missing items to databases.sample.ini & etc 2015-11-04 17:29:04 +01:00
David Grudl
411862d5d8 DibiFluent: fixed combination of modifier and inner fluent [Closes #192] 2015-11-02 14:49:08 +01:00
David Grudl
84f3a5ddef DibiResultInfo: fixed case insensitivity 2015-11-02 11:45:27 +01:00
David Grudl
47ef875c73 Released version 2.3.4 2015-10-26 19:31:39 +01:00
David Grudl
63c644a860 DibiFluent::fetch() uses limit only when there is no LIMIT & OFFSET (fixes 20f2093 on MSSQL) 2015-10-26 19:31:08 +01:00
David Grudl
1c3ef5f5cf DibiFluent: prevents doubled processing 2015-10-26 19:26:07 +01:00
Pavel Zelezny
9100f94b8f DibiConnection: option 'driver' can contain driver instance or class name [Closes #153] 2015-10-26 19:26:06 +01:00
David Grudl
e339eff00f tests: improved sql dumps, renamed pgsql -> postgre 2015-10-26 14:32:48 +01:00
David Grudl
48adcec6dc added DibiSqlsrvDriver as alias for DibiMsSql2005Driver 2015-10-26 14:28:55 +01:00
David Grudl
389026d697 DibiTranslator: removed die() 2015-10-23 17:20:15 +02:00
David Grudl
8f0d0fb115 Released version 2.3.3 2015-10-22 02:44:33 +02:00
David Grudl
965570c067 removed unused code 2015-10-22 02:43:57 +02:00
David Grudl
4ae4f49c21 Result: fixed normalization of float when ends with "0" [Closes #189] 2015-10-13 15:13:59 +02:00
David Grudl
5b9ffe14ba Result: normalize converts FALSE 2015-10-13 15:13:59 +02:00
castamir
2d9358e4f7 DibiFluent::fetch(): fixed limit clause duplication [Closes #188][Closes #186][Closes #185] 2015-10-09 11:41:12 +02:00
David Grudl
e3748420f5 DibiFluent: removed keyword AS from SQL [Closes #172] 2015-10-09 00:17:10 +02:00
116 changed files with 4866 additions and 4812 deletions

View File

@@ -1,5 +1,6 @@
language: php language: php
php: php:
- 5.3.3
- 5.4 - 5.4
- 5.5 - 5.5
- 5.6 - 5.6
@@ -8,11 +9,12 @@ php:
matrix: matrix:
allow_failures: allow_failures:
- php: 7.0
- php: hhvm - php: hhvm
script: script:
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini - vendor/bin/tester tests -s -p php -c tests/php-unix.ini
- php temp/code-checker/src/code-checker.php --short-arrays - php code-checker/src/code-checker.php
after_failure: after_failure:
# Print *.actual content # Print *.actual content
@@ -21,10 +23,10 @@ after_failure:
before_script: before_script:
# Install Nette Tester & Code Checker # Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction - travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction - travis_retry composer create-project nette/code-checker code-checker ~2.5 --no-interaction
# Create databases.ini # Create databases.ini
- cp ./tests/databases.travis.ini ./tests/databases.ini - cp ./tests/databases.sample.ini ./tests/databases.ini
# Create Postgre database # Create Postgre database
- psql -c 'CREATE DATABASE dibi_test' -U postgres - psql -c 'CREATE DATABASE dibi_test' -U postgres

View File

@@ -1,39 +0,0 @@
build: off
cache:
- c:\php -> appveyor.yml
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
clone_folder: c:\projects\dibi
services:
- mysql
init:
- SET PATH=c:\php;%PATH%
- SET PHP=1
- SET ANSICON=121x90 (121x90)
install:
# Install PHP
- IF EXIST c:\php (SET PHP=0) ELSE (mkdir c:\php)
- IF %PHP%==1 cd c:\php
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip
- IF %PHP%==1 7z x php-5.6.14-Win32-VC11-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==1 del /Q *.zip
- cd c:\projects\dibi
# Install Nette Tester
- appveyor DownloadFile https://getcomposer.org/composer.phar
- php composer.phar install --prefer-dist --no-interaction --no-progress
# Create databases.ini
- copy tests\databases.appveyor.ini tests\databases.ini
test_script:
- vendor\bin\tester tests -s -p php -c tests\php-win.ini
on_failure:
# Print *.actual content
- type tests\dibi\output\*.actual

View File

@@ -1,8 +1,8 @@
{ {
"name": "dibi/dibi", "name": "dibi/dibi",
"description": "Dibi is Database Abstraction Library for PHP", "description": "Dibi is Database Abstraction Library for PHP",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"], "keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "oracle", "access", "pdo", "odbc"],
"homepage": "http://dibiphp.com", "homepage": "https://dibiphp.com",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"], "license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [ "authors": [
{ {
@@ -11,7 +11,7 @@
} }
], ],
"require": { "require": {
"php": ">=5.4.4" "php": ">=5.2.0"
}, },
"require-dev": { "require-dev": {
"tracy/tracy": "~2.2", "tracy/tracy": "~2.2",
@@ -21,12 +21,11 @@
"dg/dibi": "self.version" "dg/dibi": "self.version"
}, },
"autoload": { "autoload": {
"classmap": ["src/"], "classmap": ["dibi/"]
"files": ["src/loader.php"]
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.0-dev" "dev-master": "2.3-dev"
} }
} }
} }

View File

@@ -5,7 +5,7 @@ The issue tracker is the preferred channel for bug reports, features requests
and submitting pull requests, but please respect the following restrictions: and submitting pull requests, but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use * Please **do not** use the issue tracker for personal support requests (use
[dibi forum](http://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)). [dibi forum](https://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)).
* Please **do not** derail or troll issues. Keep the discussion on topic and * Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others. respect the opinions of others.

View File

@@ -5,15 +5,14 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Bridges\Nette;
use Nette;
/** /**
* Dibi extension for Nette Framework 2.1. Creates 'connection' service. * Dibi extension for Nette Framework 2.1. Creates 'connection' service.
*
* @package dibi\nette
* @phpversion 5.3
*/ */
class DibiExtension21 extends Nette\DI\CompilerExtension class DibiNette21Extension extends Nette\DI\CompilerExtension
{ {
public function loadConfiguration() public function loadConfiguration()
@@ -36,16 +35,16 @@ class DibiExtension21 extends Nette\DI\CompilerExtension
} }
$connection = $container->addDefinition($this->prefix('connection')) $connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config]) ->setClass('DibiConnection', array($config))
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE); ->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if ($useProfiler) { if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel')) $panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Nette\Panel') ->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self']) ->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', array('@self'))
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']); ->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', array('DibiNettePanel::renderException'));
$connection->addSetup('$service->onEvent[] = ?', [[$panel, 'logEvent']]); $connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
} }
} }

View File

@@ -5,24 +5,18 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Bridges\Nette;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Nette;
use Nette\Diagnostics\Debugger; use Nette\Diagnostics\Debugger;
/** /**
* Dibi panel for Nette\Diagnostics. * Dibi panel for Nette\Diagnostics.
*
* @package dibi\nette
*/ */
class Panel implements Nette\Diagnostics\IBarPanel class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
{ {
use Dibi\Strict;
/** @var int maximum SQL length */ /** @var int maximum SQL length */
public static $maxLength = 1000; static public $maxLength = 1000;
/** @var bool explain queries? */ /** @var bool explain queries? */
public $explain; public $explain;
@@ -31,21 +25,21 @@ class Panel implements Nette\Diagnostics\IBarPanel
public $filter; public $filter;
/** @var array */ /** @var array */
private $events = []; private $events = array();
public function __construct($explain = TRUE, $filter = NULL) public function __construct($explain = TRUE, $filter = NULL)
{ {
$this->filter = $filter ? (int) $filter : Event::QUERY; $this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->explain = $explain; $this->explain = $explain;
} }
public function register(Dibi\Connection $connection) public function register(DibiConnection $connection)
{ {
Debugger::getBar()->addPanel($this); Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']); Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = [$this, 'logEvent']; $connection->onEvent[] = array($this, 'logEvent');
} }
@@ -53,7 +47,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
* After event notification. * After event notification.
* @return void * @return void
*/ */
public function logEvent(Event $event) public function logEvent(DibiEvent $event)
{ {
if (($event->type & $this->filter) === 0) { if (($event->type & $this->filter) === 0) {
return; return;
@@ -68,11 +62,11 @@ class Panel implements Nette\Diagnostics\IBarPanel
*/ */
public static function renderException($e) public static function renderException($e)
{ {
if ($e instanceof Dibi\Exception && $e->getSql()) { if ($e instanceof DibiException && $e->getSql()) {
return [ return array(
'tab' => 'SQL', 'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE), 'panel' => dibi::dump($e->getSql(), TRUE),
]; );
} }
} }
@@ -105,15 +99,15 @@ class Panel implements Nette\Diagnostics\IBarPanel
foreach ($this->events as $event) { foreach ($this->events as $event) {
$totalTime += $event->time; $totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() $explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) { if ($this->explain && $event->type === DibiEvent::SELECT) {
try { try {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime]; $backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL; $event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN'); $cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE); $explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
} }
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup; list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
} }
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000); $s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
@@ -123,7 +117,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
$s .= "<br /><a href='#nette-debug-DibiProfiler-row-$counter' class='nette-toggler nette-toggle-collapsed' rel='#nette-debug-DibiProfiler-row-$counter'>explain</a>"; $s .= "<br /><a href='#nette-debug-DibiProfiler-row-$counter' class='nette-toggler nette-toggle-collapsed' rel='#nette-debug-DibiProfiler-row-$counter'>explain</a>";
} }
$s .= '</td><td class="nette-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE); $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) { if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>"; $s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
} }
@@ -132,7 +126,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
if (!class_exists($helpers)) { if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers'; $helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
} }
$s .= call_user_func([$helpers, 'editorLink'], $event->source[0], $event->source[1])->class('nette-DibiProfiler-source'); $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>"; $s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";

View File

@@ -0,0 +1,12 @@
# This will create service named 'dibi.connection'.
# Requires Nette Framework 2.1
extensions:
dibi: DibiNette21Extension
dibi:
host: localhost
username: root
password: ***
database: foo
lazy: TRUE

View File

@@ -7,11 +7,14 @@
namespace Dibi\Bridges\Nette; namespace Dibi\Bridges\Nette;
use dibi;
use Nette; use Nette;
/** /**
* Dibi extension for Nette Framework 2.2. Creates 'connection' & 'panel' services. * Dibi extension for Nette Framework 2.2. Creates 'connection' & 'panel' services.
*
* @package dibi\nette
*/ */
class DibiExtension22 extends Nette\DI\CompilerExtension class DibiExtension22 extends Nette\DI\CompilerExtension
{ {
@@ -36,19 +39,13 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
} }
$connection = $container->addDefinition($this->prefix('connection')) $connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config]) ->setClass('DibiConnection', array($config))
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE); ->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if (class_exists('Tracy\Debugger')) {
$connection->addSetup(
[new Nette\DI\Statement('Tracy\Debugger::getBlueScreen'), 'addPanel'],
[['Dibi\Bridges\Tracy\Panel', 'renderException']]
);
}
if ($useProfiler) { if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel')) $panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Tracy\Panel'); ->setClass('Dibi\Bridges\Tracy\Panel');
$connection->addSetup([$panel, 'register'], [$connection]); $connection->addSetup(array($panel, 'register'), array($connection));
} }
} }

View File

@@ -7,21 +7,18 @@
namespace Dibi\Bridges\Tracy; namespace Dibi\Bridges\Tracy;
use Dibi; use dibi;
use Dibi\Event;
use Dibi\Helpers;
use Tracy; use Tracy;
/** /**
* Dibi panel for Tracy. * Dibi panel for Tracy.
* @package dibi\nette
*/ */
class Panel implements Tracy\IBarPanel class Panel extends \DibiObject implements Tracy\IBarPanel
{ {
use Dibi\Strict;
/** @var int maximum SQL length */ /** @var int maximum SQL length */
public static $maxLength = 1000; static public $maxLength = 1000;
/** @var bool explain queries? */ /** @var bool explain queries? */
public $explain; public $explain;
@@ -30,21 +27,21 @@ class Panel implements Tracy\IBarPanel
public $filter; public $filter;
/** @var array */ /** @var array */
private $events = []; private $events = array();
public function __construct($explain = TRUE, $filter = NULL) public function __construct($explain = TRUE, $filter = NULL)
{ {
$this->filter = $filter ? (int) $filter : Event::QUERY; $this->filter = $filter ? (int) $filter : \DibiEvent::QUERY;
$this->explain = $explain; $this->explain = $explain;
} }
public function register(Dibi\Connection $connection) public function register(\DibiConnection $connection)
{ {
Tracy\Debugger::getBar()->addPanel($this); Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']); Tracy\Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = [$this, 'logEvent']; $connection->onEvent[] = array($this, 'logEvent');
} }
@@ -52,7 +49,7 @@ class Panel implements Tracy\IBarPanel
* After event notification. * After event notification.
* @return void * @return void
*/ */
public function logEvent(Event $event) public function logEvent(\DibiEvent $event)
{ {
if (($event->type & $this->filter) === 0) { if (($event->type & $this->filter) === 0) {
return; return;
@@ -67,11 +64,11 @@ class Panel implements Tracy\IBarPanel
*/ */
public static function renderException($e) public static function renderException($e)
{ {
if ($e instanceof Dibi\Exception && $e->getSql()) { if ($e instanceof \DibiException && $e->getSql()) {
return [ return array(
'tab' => 'SQL', 'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE), 'panel' => dibi::dump($e->getSql(), TRUE),
]; );
} }
} }
@@ -105,15 +102,15 @@ class Panel implements Tracy\IBarPanel
foreach ($this->events as $event) { foreach ($this->events as $event) {
$totalTime += $event->time; $totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS() $explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) { if ($this->explain && $event->type === \DibiEvent::SELECT) {
try { try {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime]; $backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL; $event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN'); $cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE); $explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) { } catch (\DibiException $e) {
} }
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup; list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
} }
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000); $s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
@@ -123,7 +120,7 @@ class Panel implements Tracy\IBarPanel
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>"; $s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
} }
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE); $s .= '</td><td class="tracy-DibiProfiler-sql">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) { if ($explain) {
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>"; $s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
} }

View File

@@ -1,3 +1,35 @@
<?php <?php
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php'; /**
* dibi - smart database abstraction layer (http://dibiphp.com)
*
* Copyright (c) 2005, 2012 David Grudl (https://davidgrudl.com)
*/
/**
* Check PHP configuration.
*/
if (version_compare(PHP_VERSION, '5.2.0', '<')) {
throw new Exception('dibi needs PHP 5.2.0 or newer.');
}
require_once dirname(__FILE__) . '/libs/interfaces.php';
require_once dirname(__FILE__) . '/libs/Dibi.php';
require_once dirname(__FILE__) . '/libs/DibiDateTime.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/DibiConnection.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/DibiDataSource.php';
require_once dirname(__FILE__) . '/libs/DibiFluent.php';
require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php';
require_once dirname(__FILE__) . '/libs/DibiEvent.php';
require_once dirname(__FILE__) . '/libs/DibiFileLogger.php';
require_once dirname(__FILE__) . '/libs/DibiFirePhpLogger.php';

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi driver for Firebird/InterBase database. * The dibi driver for Firebird/InterBase database.
@@ -20,12 +16,12 @@ use Dibi;
* - charset => character encoding to set * - 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. * - 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 * - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{ {
use Dibi\Strict;
const ERROR_EXCEPTION_THROWN = -836; const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */ /** @var resource Connection resource */
@@ -45,12 +41,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('interbase')) { if (!extension_loaded('interbase')) {
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded."); throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
} }
} }
@@ -58,33 +54,37 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
Dibi\Helpers::alias($config, 'database', 'db'); DibiConnection::alias($config, 'database', 'db');
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
} else { } else {
// default values // default values
$config += [ $config += array(
'username' => ini_get('ibase.default_password'), 'username' => ini_get('ibase.default_password'),
'password' => ini_get('ibase.default_user'), 'password' => ini_get('ibase.default_user'),
'database' => ini_get('ibase.default_db'), 'database' => ini_get('ibase.default_db'),
'charset' => ini_get('ibase.default_charset'), 'charset' => ini_get('ibase.default_charset'),
'buffers' => 0, 'buffers' => 0,
]; );
DibiDriverException::tryError();
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @ $this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else { } else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @ $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)) { if (!is_resource($this->connection)) {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode()); throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
} }
} }
} }
@@ -103,22 +103,27 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception * @throws DibiDriverException|DibiException
*/ */
public function query($sql) public function query($sql)
{ {
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection; $resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql); $res = ibase_query($resource, $sql);
if ($res === FALSE) { if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) { if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match); preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql); throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else { } else {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql); throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), dibi::$sql);
} }
}
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
@@ -151,14 +156,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
if ($savepoint !== NULL) { if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
$this->transaction = ibase_trans($this->getResource()); $this->transaction = ibase_trans($this->resource);
$this->inTransaction = TRUE; $this->inTransaction = TRUE;
} }
@@ -167,16 +172,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
if ($savepoint !== NULL) { if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
if (!ibase_commit($this->transaction)) { if (!ibase_commit($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.'); throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
} }
$this->inTransaction = FALSE; $this->inTransaction = FALSE;
@@ -187,16 +192,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
if ($savepoint !== NULL) { if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.'); throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
} }
if (!ibase_rollback($this->transaction)) { if (!ibase_rollback($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.'); throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
} }
$this->inTransaction = FALSE; $this->inTransaction = FALSE;
@@ -225,7 +230,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
@@ -236,7 +241,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -251,48 +256,34 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param string * @param mixed value
* @return string * @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . str_replace("'", "''", $value) . "'"; switch ($type) {
} case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return $value;
public function escapeBinary($value) case dibi::BOOL:
{ return $value ? 1 : 0;
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
public function escapeIdentifier($value) default:
{ throw new InvalidArgumentException('Unsupported type.');
return $value;
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
} }
@@ -304,26 +295,23 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function escapeLike($value, $pos) public function escapeLike($value, $pos)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -359,7 +347,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getRowCount() public function getRowCount()
{ {
throw new Dibi\NotSupportedException('Firebird/Interbase do not support returning number of rows in result set.'); throw new DibiNotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
} }
@@ -370,15 +358,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function fetch($assoc) public function fetch($assoc)
{ {
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @ $result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (ibase_errcode()) { if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) { if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match); preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]); throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
} else { } else {
throw new Dibi\DriverException($msg, ibase_errcode()); throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
} }
} }
@@ -390,11 +379,11 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* 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 bool TRUE on success, FALSE if unable to seek to specified record * @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception * @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
throw new Dibi\NotSupportedException('Firebird/Interbase do not support seek in result set.'); throw new DibiNotSupportedException('Firebird/Interbase do not support seek in result set.');
} }
@@ -427,21 +416,21 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns() public function getResultColumns()
{ {
$count = ibase_num_fields($this->resultSet); $count = ibase_num_fields($this->resultSet);
$columns = []; $columns = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i); $row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = [ $columns[] = array(
'name' => $row['name'], 'name' => $row['name'],
'fullname' => $row['name'], 'fullname' => $row['name'],
'table' => $row['relation'], 'table' => $row['relation'],
'nativetype' => $row['type'], 'nativetype' => $row['type'],
]; );
} }
return $columns; return $columns;
} }
/********************* Dibi\Reflector ********************/ /********************* IDibiReflector ********************/
/** /**
@@ -456,12 +445,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$RELATIONS FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB\$SYSTEM_FLAG = 0;"
); );
$tables = []; $tables = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$tables[] = [ $tables[] = array(
'name' => $row[0], 'name' => $row[0],
'view' => $row[1] === 'TRUE', 'view' => $row[1] === 'TRUE',
]; );
} }
return $tables; return $tables;
} }
@@ -505,10 +494,10 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY r.RDB\$FIELD_POSITION;" ORDER BY r.RDB\$FIELD_POSITION;"
); );
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME']; $key = $row['FIELD_NAME'];
$columns[$key] = [ $columns[$key] = array(
'name' => $key, 'name' => $key,
'table' => $table, 'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']), 'nativetype' => trim($row['FIELD_TYPE']),
@@ -516,7 +505,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'nullable' => $row['NULLABLE'] === 'TRUE', 'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'], 'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE, 'autoincrement' => FALSE,
]; );
} }
return $columns; return $columns;
} }
@@ -543,7 +532,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table' WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION" ORDER BY s.RDB\$FIELD_POSITION"
); );
$indexes = []; $indexes = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME']; $key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key; $indexes[$key]['name'] = $key;
@@ -573,14 +562,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY' AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION" ORDER BY s.RDB\$FIELD_POSITION"
); );
$keys = []; $keys = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME']; $key = $row['INDEX_NAME'];
$keys[$key] = [ $keys[$key] = array(
'name' => $key, 'name' => $key,
'column' => $row['FIELD_NAME'], 'column' => $row['FIELD_NAME'],
'table' => $table, 'table' => $table,
]; );
} }
return $keys; return $keys;
} }
@@ -600,7 +589,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND RDB\$UNIQUE_FLAG IS NULL AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;" AND RDB\$FOREIGN_KEY IS NULL;"
); );
$indices = []; $indices = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0]; $indices[] = $row[0];
} }
@@ -624,7 +613,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
OR RDB\$FOREIGN_KEY IS NOT NULL OR RDB\$FOREIGN_KEY IS NOT NULL
);" );"
); );
$constraints = []; $constraints = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0]; $constraints[] = $row[0];
} }
@@ -667,15 +656,15 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE RDB\$SYSTEM_FLAG = 0" WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');") . ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
); );
$triggers = []; $triggers = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = [ $triggers[$row['TRIGGER_NAME']] = array(
'name' => $row['TRIGGER_NAME'], 'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'], 'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']), 'type' => trim($row['TRIGGER_TYPE']),
'event' => trim($row['TRIGGER_EVENT']), 'event' => trim($row['TRIGGER_EVENT']),
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE', 'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
]; );
} }
return $triggers; return $triggers;
} }
@@ -695,7 +684,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')"; $q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q); $res = $this->query($q);
$triggers = []; $triggers = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0]; $triggers[] = $row[0];
} }
@@ -742,7 +731,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;" ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
); );
$procedures = []; $procedures = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME']; $key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']); $io = trim($row['PARAMETER_TYPE']);
@@ -766,7 +755,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
SELECT TRIM(RDB\$PROCEDURE_NAME) SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;" FROM RDB\$PROCEDURES;"
); );
$procedures = []; $procedures = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0]; $procedures[] = $row[0];
} }
@@ -785,7 +774,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$GENERATORS FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB\$SYSTEM_FLAG = 0;"
); );
$generators = []; $generators = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0]; $generators[] = $row[0];
} }
@@ -804,7 +793,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$FUNCTIONS FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;" WHERE RDB\$SYSTEM_FLAG = 0;"
); );
$functions = []; $functions = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0]; $functions[] = $row[0];
} }
@@ -812,3 +801,39 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
} }
/**
* Database procedure exception.
*
* @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

@@ -5,15 +5,12 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi; require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
use Dibi\Connection;
use Dibi\Helpers;
/** /**
* The dibi driver for Microsoft SQL Server and SQL Azure databases. * The dibi driver for MS SQL Driver 2005 database.
* *
* Driver options: * Driver 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)
@@ -23,12 +20,12 @@ use Dibi\Helpers;
* - options (array) => connection options {@link https://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx} * - options (array) => connection options {@link https://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - charset => character encoding to set (default is UTF-8) * - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{ {
use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
@@ -41,17 +38,14 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** @var int|FALSE Affected rows */ /** @var int|FALSE Affected rows */
private $affectedRows = FALSE; private $affectedRows = FALSE;
/** @var string */
private $version;
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('sqlsrv')) { if (!extension_loaded('sqlsrv')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded."); throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
} }
} }
@@ -59,14 +53,14 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
Helpers::alias($config, 'options|UID', 'username'); DibiConnection::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password'); DibiConnection::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database'); DibiConnection::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset'); DibiConnection::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
@@ -82,9 +76,8 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
$info = sqlsrv_errors(); $info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']); throw new DibiDriverException($info[0]['message'], $info[0]['code']);
} }
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
} }
@@ -101,8 +94,8 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -111,7 +104,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
if ($res === FALSE) { if ($res === FALSE) {
$info = sqlsrv_errors(); $info = sqlsrv_errors();
throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql); throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res); $this->affectedRows = sqlsrv_rows_affected($res);
@@ -149,7 +142,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
@@ -161,7 +154,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
@@ -173,7 +166,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
@@ -193,18 +186,18 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
return new SqlsrvReflector($this); return new DibiMssql2005Reflector($this);
} }
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -220,48 +213,34 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . str_replace("'", "''", $value) . "'"; switch ($type) {
} case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
public function escapeBinary($value) case dibi::BOOL:
{ return $value ? 1 : 0;
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
public function escapeIdentifier($value) default:
{ throw new InvalidArgumentException('Unsupported type.');
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
} }
@@ -273,27 +252,24 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function escapeLike($value, $pos) public function escapeLike($value, $pos)
{ {
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
} }
@@ -303,21 +279,13 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { // offset support is missing
throw new Dibi\NotSupportedException('Negative offset or limit.'); if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') AS T ';
}
} elseif (version_compare($this->version, 11, '<')) { // 11 == SQL Server 2012 if ($offset) {
if ($offset) { throw new DibiNotImplementedException('Offset is not implemented.');
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
} elseif ($limit !== NULL || $offset) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql .= ' OFFSET ' . (int) $offset . ' ROWS '
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
} }
} }
@@ -341,7 +309,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function getRowCount() public function getRowCount()
{ {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
} }
@@ -363,7 +331,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function seek($row) public function seek($row)
{ {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
} }
@@ -384,13 +352,13 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function getResultColumns() public function getResultColumns()
{ {
$columns = []; $columns = array();
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) { foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = [ $columns[] = array(
'name' => $fieldMetadata['Name'], 'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'], 'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'], 'nativetype' => $fieldMetadata['Type'],
]; );
} }
return $columns; return $columns;
} }

View File

@@ -5,24 +5,20 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi reflector for Microsoft SQL Server and SQL Azure databases. * The dibi reflector for MSSQL2005 databases.
*
* @package dibi\drivers
* @internal * @internal
*/ */
class SqlsrvReflector implements Dibi\Reflector class DibiMsSql2005Reflector extends DibiObject implements IDibiReflector
{ {
use Dibi\Strict; /** @var IDibiDriver */
/** @var Dibi\Driver */
private $driver; private $driver;
public function __construct(Dibi\Driver $driver) public function __construct(IDibiDriver $driver)
{ {
$this->driver = $driver; $this->driver = $driver;
} }
@@ -35,12 +31,12 @@ class SqlsrvReflector implements Dibi\Reflector
public function getTables() public function getTables()
{ {
$res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES'); $res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES');
$tables = []; $tables = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$tables[] = [ $tables[] = array(
'name' => $row[0], 'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW', 'view' => isset($row[1]) && $row[1] === 'VIEW',
]; );
} }
return $tables; return $tables;
} }
@@ -57,10 +53,10 @@ class SqlsrvReflector implements Dibi\Reflector
SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT
FROM sys.columns c FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id INNER JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name = {$this->driver->escapeText($table)} WHERE t.name = {$this->driver->escape($table, dibi::TEXT)}
"); ");
$autoIncrements = []; $autoIncrements = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT']; $autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
} }
@@ -78,11 +74,11 @@ class SqlsrvReflector implements Dibi\Reflector
And TC.CONSTRAINT_TYPE = 'PRIMARY KEY' And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
And CCU.COLUMN_NAME = C.COLUMN_NAME And CCU.COLUMN_NAME = C.COLUMN_NAME
) As Z ) As Z
WHERE C.TABLE_NAME = {$this->driver->escapeText($table)} WHERE C.TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
"); ");
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$columns[] = [ $columns[] = array(
'name' => $row['COLUMN_NAME'], 'name' => $row['COLUMN_NAME'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($row['DATA_TYPE']), 'nativetype' => strtoupper($row['DATA_TYPE']),
@@ -92,7 +88,7 @@ class SqlsrvReflector implements Dibi\Reflector
'default' => $row['COLUMN_DEFAULT'], 'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']], 'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }
@@ -105,19 +101,19 @@ class SqlsrvReflector implements Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
$keyUsagesRes = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escapeText($table)}"); $keyUsagesRes = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$keyUsages = []; $keyUsages = array();
while ($row = $keyUsagesRes->fetch(TRUE)) { while ($row = $keyUsagesRes->fetch(TRUE)) {
$keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME']; $keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME'];
} }
$res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escapeText($table)}"); $res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$indexes = []; $indexes = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME']; $indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME'];
$indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE'; $indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE';
$indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY'; $indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : []; $indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : array();
} }
return array_values($indexes); return array_values($indexes);
} }
@@ -130,7 +126,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
} }

View File

@@ -0,0 +1,365 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
/**
* The dibi driver for MS SQL 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
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @
} else {
$this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @
}
if (!is_resource($this->connection)) {
throw new DibiDriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($this->escape($config['database'], dibi::IDENTIFIER), $this->connection)) { // intentionally @
throw new DibiDriverException("Can't select DB '$config[database]'.");
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
mssql_close($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(mssql_get_last_message(), 0, $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 mssql_rows_affected($this->connection);
}
/**
* 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 = mssql_query('SELECT @@IDENTITY', $this->connection);
if (is_resource($res)) {
$row = mssql_fetch_row($res);
return $row[0];
}
return FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
$this->query('BEGIN TRANSACTION');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
$this->query('COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
$this->query('ROLLBACK');
}
/**
* 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()
{
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**/
/**
* 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 https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
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 . ') t';
}
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()
{
return mssql_num_rows($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)
{
return mssql_fetch_array($this->resultSet, $assoc ? MSSQL_ASSOC : MSSQL_NUM);
}
/**
* 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)
{
return mssql_data_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
mssql_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'],
'nativetype' => $row['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

@@ -0,0 +1,212 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
/**
* The dibi reflector for MsSQL databases.
*
* @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 int
*/
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

@@ -5,9 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi; require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/** /**
@@ -26,12 +25,12 @@ use Dibi;
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically? * - 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
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{ {
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045; const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062; const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265; const ERROR_DATA_TRUNCATED = 1265;
@@ -50,12 +49,12 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('mysql')) { if (!extension_loaded('mysql')) {
throw new Dibi\NotSupportedException("PHP extension 'mysql' is not loaded."); throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
} }
} }
@@ -63,7 +62,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
@@ -72,13 +71,13 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} else { } else {
// default values // default values
Dibi\Helpers::alias($config, 'flags', 'options'); DibiConnection::alias($config, 'flags', 'options');
$config += [ $config += array(
'charset' => 'utf8', 'charset' => 'utf8',
'timezone' => date('P'), 'timezone' => date('P'),
'username' => ini_get('mysql.default_user'), 'username' => ini_get('mysql.default_user'),
'password' => ini_get('mysql.default_password'), 'password' => ini_get('mysql.default_password'),
]; );
if (!isset($config['host'])) { if (!isset($config['host'])) {
$host = ini_get('mysql.default_host'); $host = ini_get('mysql.default_host');
if ($host) { if ($host) {
@@ -106,18 +105,23 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
throw new Dibi\DriverException(mysql_error(), mysql_errno()); throw new DibiDriverException(mysql_error(), mysql_errno());
} }
if (isset($config['charset'])) { if (isset($config['charset'])) {
if (!@mysql_set_charset($config['charset'], $this->connection)) { // intentionally @ $ok = FALSE;
if (function_exists('mysql_set_charset')) {
// affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3)
$ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
}
if (!$ok) {
$this->query("SET NAMES '$config[charset]'"); $this->query("SET NAMES '$config[charset]'");
} }
} }
if (isset($config['database'])) { if (isset($config['database'])) {
if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @ if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @
throw new Dibi\DriverException(mysql_error($this->connection), mysql_errno($this->connection)); throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
} }
} }
@@ -146,8 +150,8 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -157,8 +161,8 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @ $res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
} }
if ($code = mysql_errno($this->connection)) { if (mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql); throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
@@ -172,10 +176,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function getInfo() public function getInfo()
{ {
$res = []; $res = array();
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER); preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) { if (preg_last_error()) {
throw new Dibi\PcreException; throw new DibiPcreException;
} }
foreach ($matches as $m) { foreach ($matches as $m) {
@@ -209,7 +213,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
@@ -221,7 +225,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
@@ -233,7 +237,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
@@ -253,18 +257,18 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
return new MySqlReflector($this); return new DibiMySqlReflector($this);
} }
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -280,54 +284,42 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
if (!is_resource($this->connection)) { switch ($type) {
throw new Dibi\Exception('Lost connection to server.'); case dibi::TEXT:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::BINARY:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::IDENTIFIER:
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
default:
throw new InvalidArgumentException('Unsupported type.');
} }
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
}
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
}
public function escapeIdentifier($value)
{
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
} }
@@ -346,20 +338,17 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -369,13 +358,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit >= 0 || $offset > 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit) $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} }
} }
@@ -400,7 +386,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount() public function getRowCount()
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
} }
return mysql_num_rows($this->resultSet); return mysql_num_rows($this->resultSet);
} }
@@ -421,12 +407,12 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* 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 bool TRUE on success, FALSE if unable to seek to specified record * @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception * @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
} }
return mysql_data_seek($this->resultSet, $row); return mysql_data_seek($this->resultSet, $row);
@@ -451,17 +437,16 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns() public function getResultColumns()
{ {
$count = mysql_num_fields($this->resultSet); $count = mysql_num_fields($this->resultSet);
$columns = []; $columns = 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[] = [ $columns[] = 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'],
'nativetype' => strtoupper($row['type']), 'nativetype' => strtoupper($row['type']),
'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }

View File

@@ -5,24 +5,20 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi reflector for MySQL databases. * The dibi reflector for MySQL databases.
*
* @package dibi\drivers
* @internal * @internal
*/ */
class MySqlReflector implements Dibi\Reflector class DibiMySqlReflector extends DibiObject implements IDibiReflector
{ {
use Dibi\Strict; /** @var IDibiDriver */
/** @var Dibi\Driver */
private $driver; private $driver;
public function __construct(Dibi\Driver $driver) public function __construct(IDibiDriver $driver)
{ {
$this->driver = $driver; $this->driver = $driver;
} }
@@ -35,12 +31,12 @@ class MySqlReflector implements Dibi\Reflector
public function getTables() public function getTables()
{ {
$res = $this->driver->query('SHOW FULL TABLES'); $res = $this->driver->query('SHOW FULL TABLES');
$tables = []; $tables = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
$tables[] = [ $tables[] = array(
'name' => $row[0], 'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW', 'view' => isset($row[1]) && $row[1] === 'VIEW',
]; );
} }
return $tables; return $tables;
} }
@@ -53,11 +49,11 @@ class MySqlReflector implements Dibi\Reflector
*/ */
public function getColumns($table) public function getColumns($table)
{ {
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}"); $res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']); $type = explode('(', $row['Type']);
$columns[] = [ $columns[] = array(
'name' => $row['Field'], 'name' => $row['Field'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($type[0]), 'nativetype' => strtoupper($type[0]),
@@ -67,7 +63,7 @@ class MySqlReflector implements Dibi\Reflector
'default' => $row['Default'], 'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment', 'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }
@@ -80,8 +76,8 @@ class MySqlReflector implements Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}"); $res = $this->driver->query("SHOW INDEX FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$indexes = []; $indexes = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name']; $indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique']; $indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
@@ -96,13 +92,13 @@ class MySqlReflector implements Dibi\Reflector
* Returns metadata for all foreign keys in a table. * Returns metadata for all foreign keys in a table.
* @param string * @param string
* @return array * @return array
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
$data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escapeText($table)}")->fetch(TRUE); $data = $this->driver->query("SELECT `ENGINE` FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}")->fetch(TRUE);
if ($data['ENGINE'] !== 'InnoDB') { if ($data['ENGINE'] !== 'InnoDB') {
throw new Dibi\NotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables."); throw new DibiNotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables.");
} }
$res = $this->driver->query(" $res = $this->driver->query("
@@ -114,11 +110,11 @@ class MySqlReflector implements Dibi\Reflector
kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = DATABASE() WHERE rc.CONSTRAINT_SCHEMA = DATABASE()
AND rc.TABLE_NAME = {$this->driver->escapeText($table)} AND rc.TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
GROUP BY rc.CONSTRAINT_NAME GROUP BY rc.CONSTRAINT_NAME
"); ");
$foreignKeys = []; $foreignKeys = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$keyName = $row['CONSTRAINT_NAME']; $keyName = $row['CONSTRAINT_NAME'];

View File

@@ -5,9 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi; require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/** /**
@@ -27,12 +26,12 @@ use Dibi;
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically? * - 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
* - resource (mysqli) => existing connection resource * - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{ {
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045; const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062; const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265; const ERROR_DATA_TRUNCATED = 1265;
@@ -51,12 +50,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('mysqli')) { if (!extension_loaded('mysqli')) {
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded."); throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
} }
} }
@@ -64,7 +63,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
@@ -74,14 +73,14 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} else { } else {
// default values // default values
$config += [ $config += array(
'charset' => 'utf8', 'charset' => 'utf8',
'timezone' => date('P'), 'timezone' => date('P'),
'username' => ini_get('mysqli.default_user'), 'username' => ini_get('mysqli.default_user'),
'password' => ini_get('mysqli.default_pw'), 'password' => ini_get('mysqli.default_pw'),
'socket' => (string) ini_get('mysqli.default_socket'), 'socket' => (string) ini_get('mysqli.default_socket'),
'port' => NULL, 'port' => NULL,
]; );
if (!isset($config['host'])) { if (!isset($config['host'])) {
$host = ini_get('mysqli.default_host'); $host = ini_get('mysqli.default_host');
if ($host) { if ($host) {
@@ -110,7 +109,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @ @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()) { if ($errno = mysqli_connect_errno()) {
throw new Dibi\DriverException(mysqli_connect_error(), $errno); throw new DibiDriverException(mysqli_connect_error(), $errno);
} }
} }
@@ -145,15 +144,15 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @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 @ $res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if ($code = mysqli_errno($this->connection)) { if (mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql); throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
} elseif (is_object($res)) { } elseif (is_object($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
@@ -161,39 +160,16 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} }
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1216, 1217, 1451, 1452, 1701], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1062, 1557, 1569, 1586], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif ($code >= 2001 && $code <= 2028) {
return new Dibi\ConnectionException($message, $code, $sql);
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
/** /**
* Retrieves information about the most recently executed query. * Retrieves information about the most recently executed query.
* @return array * @return array
*/ */
public function getInfo() public function getInfo()
{ {
$res = []; $res = array();
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER); preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) { if (preg_last_error()) {
throw new Dibi\PcreException; throw new DibiPcreException;
} }
foreach ($matches as $m) { foreach ($matches as $m) {
@@ -227,7 +203,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
@@ -239,7 +215,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
@@ -251,7 +227,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
@@ -271,20 +247,20 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
return new MySqlReflector($this); return new DibiMySqlReflector($this);
} }
/** /**
* Result set driver factory. * Result set driver factory.
* @param mysqli_result * @param mysqli_result
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver(\mysqli_result $resource) public function createResultDriver(mysqli_result $resource)
{ {
$res = clone $this; $res = clone $this;
$res->resultSet = $resource; $res->resultSet = $resource;
@@ -298,47 +274,35 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . mysqli_real_escape_string($this->connection, $value) . "'"; switch ($type) {
} case dibi::TEXT:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::BINARY:
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
public function escapeBinary($value) case dibi::IDENTIFIER:
{ return '`' . str_replace('`', '``', $value) . '`';
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
public function escapeIdentifier($value) case dibi::DATE:
{ case dibi::DATETIME:
return '`' . str_replace('`', '``', $value) . '`'; if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
} $value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
default:
public function escapeBool($value) throw new InvalidArgumentException('Unsupported type.');
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
} }
@@ -357,20 +321,17 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -380,13 +341,10 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit >= 0 || $offset > 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html // see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit) $sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : ''); . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} }
} }
@@ -411,7 +369,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount() public function getRowCount()
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
} }
return mysqli_num_rows($this->resultSet); return mysqli_num_rows($this->resultSet);
} }
@@ -432,12 +390,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* 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 bool TRUE on success, FALSE if unable to seek to specified record * @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception * @throws DibiException
*/ */
public function seek($row) public function seek($row)
{ {
if (!$this->buffered) { if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
} }
return mysqli_data_seek($this->resultSet, $row); return mysqli_data_seek($this->resultSet, $row);
} }
@@ -463,8 +421,8 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
static $types; static $types;
if ($types === NULL) { if ($types === NULL) {
$consts = get_defined_constants(TRUE); $consts = get_defined_constants(TRUE);
$types = []; $types = array();
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) { foreach (isset($consts['mysqli']) ? $consts['mysqli'] : array() as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) { if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12); $types[$value] = substr($key, 12);
} }
@@ -473,17 +431,16 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} }
$count = mysqli_num_fields($this->resultSet); $count = mysqli_num_fields($this->resultSet);
$columns = []; $columns = 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[] = [ $columns[] = 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'],
'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'], 'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'],
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi driver interacting with databases via ODBC connections. * The dibi driver interacting with databases via ODBC connections.
@@ -19,12 +15,12 @@ use Dibi;
* - password (or pass) * - password (or pass)
* - persistent (bool) => try to find a persistent link? * - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{ {
use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
@@ -42,12 +38,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('odbc')) { if (!extension_loaded('odbc')) {
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded."); throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
} }
} }
@@ -55,7 +51,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
@@ -63,11 +59,11 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$this->connection = $config['resource']; $this->connection = $config['resource'];
} else { } else {
// default values // default values
$config += [ $config += array(
'username' => ini_get('odbc.default_user'), 'username' => ini_get('odbc.default_user'),
'password' => ini_get('odbc.default_pw'), 'password' => ini_get('odbc.default_pw'),
'dsn' => ini_get('odbc.default_db'), 'dsn' => ini_get('odbc.default_db'),
]; );
if (empty($config['persistent'])) { if (empty($config['persistent'])) {
$this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @ $this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @
@@ -77,7 +73,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error()); throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
} }
} }
@@ -95,8 +91,8 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -104,7 +100,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @odbc_exec($this->connection, $sql); // intentionally @ $res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($res === FALSE) {
throw new Dibi\DriverException(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)) { } elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res); $this->affectedRows = odbc_num_rows($res);
@@ -129,7 +125,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getInsertId($sequence) public function getInsertId($sequence)
{ {
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.'); throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
} }
@@ -137,12 +133,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
if (!odbc_autocommit($this->connection, FALSE)) { if (!odbc_autocommit($this->connection, FALSE)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
} }
@@ -151,12 +147,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
if (!odbc_commit($this->connection)) { if (!odbc_commit($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
odbc_autocommit($this->connection, TRUE); odbc_autocommit($this->connection, TRUE);
} }
@@ -166,12 +162,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
if (!odbc_rollback($this->connection)) { if (!odbc_rollback($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection)); throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
} }
odbc_autocommit($this->connection, TRUE); odbc_autocommit($this->connection, TRUE);
} }
@@ -199,7 +195,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
@@ -210,7 +206,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -226,47 +222,33 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . str_replace("'", "''", $value) . "'"; switch ($type) {
} case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
public function escapeBinary($value) case dibi::BOOL:
{ return $value ? 1 : 0;
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "#m/d/Y H:i:s#" : "#m/d/Y#");
public function escapeIdentifier($value) default:
{ throw new InvalidArgumentException('Unsupported type.');
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format("#m/d/Y#");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("#m/d/Y H:i:s#");
} }
@@ -278,27 +260,24 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function escapeLike($value, $pos) public function escapeLike($value, $pos)
{ {
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -308,15 +287,14 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($offset) { // offset support is missing
throw new Dibi\NotSupportedException('Offset is not supported by this database.'); if ($limit >= 0) {
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
} }
if ($offset) {
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
}
} }
@@ -359,7 +337,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return FALSE; return FALSE;
} }
$count = odbc_num_fields($set); $count = odbc_num_fields($set);
$cols = []; $cols = array();
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$cols[] = odbc_result($set, $i); $cols[] = odbc_result($set, $i);
} }
@@ -398,14 +376,14 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns() public function getResultColumns()
{ {
$count = odbc_num_fields($this->resultSet); $count = odbc_num_fields($this->resultSet);
$columns = []; $columns = array();
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$columns[] = [ $columns[] = 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 $columns;
} }
@@ -422,7 +400,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/********************* Dibi\Reflector ****************d*g**/ /********************* IDibiReflector ****************d*g**/
/** /**
@@ -432,13 +410,13 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables() public function getTables()
{ {
$res = odbc_tables($this->connection); $res = odbc_tables($this->connection);
$tables = []; $tables = array();
while ($row = odbc_fetch_array($res)) { while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') { if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = [ $tables[] = 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($res);
@@ -454,17 +432,17 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getColumns($table) public function getColumns($table)
{ {
$res = odbc_columns($this->connection); $res = odbc_columns($this->connection);
$columns = []; $columns = array();
while ($row = odbc_fetch_array($res)) { while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) { if ($row['TABLE_NAME'] === $table) {
$columns[] = [ $columns[] = array(
'name' => $row['COLUMN_NAME'], 'name' => $row['COLUMN_NAME'],
'table' => $table, 'table' => $table,
'nativetype' => $row['TYPE_NAME'], 'nativetype' => $row['TYPE_NAME'],
'size' => $row['COLUMN_SIZE'], 'size' => $row['COLUMN_SIZE'],
'nullable' => (bool) $row['NULLABLE'], 'nullable' => (bool) $row['NULLABLE'],
'default' => $row['COLUMN_DEF'], 'default' => $row['COLUMN_DEF'],
]; );
} }
} }
odbc_free_result($res); odbc_free_result($res);
@@ -479,7 +457,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
@@ -490,7 +468,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
} }

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi driver for Oracle database. * The dibi driver for Oracle database.
@@ -23,12 +19,12 @@ use Dibi;
* - formatDateTime => how to format datetime in SQL (@see date) * - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect * - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{ {
use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
@@ -46,12 +42,12 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('oci8')) { if (!extension_loaded('oci8')) {
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded."); throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
} }
} }
@@ -59,7 +55,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
@@ -77,7 +73,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
if (!$this->connection) { if (!$this->connection) {
$err = oci_error(); $err = oci_error();
throw new Dibi\DriverException($err['message'], $err['code']); throw new DibiDriverException($err['message'], $err['code']);
} }
if (isset($config['schema'])) { if (isset($config['schema'])) {
@@ -99,8 +95,8 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -109,34 +105,14 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT); @oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res); $err = oci_error($res);
if ($err) { if ($err) {
throw self::createException($err['message'], $err['code'], $sql); throw new DibiDriverException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
} else { } else {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code'], $sql); throw new DibiDriverException($err['message'], $err['code'], $sql);
}
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1, 2299, 38911], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1400], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [2266, 2291, 2292], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
} }
} }
@@ -147,7 +123,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
@@ -177,13 +153,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
if (!oci_commit($this->connection)) { if (!oci_commit($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']); throw new DibiDriverException($err['message'], $err['code']);
} }
$this->autocommit = TRUE; $this->autocommit = TRUE;
} }
@@ -193,13 +169,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
if (!oci_rollback($this->connection)) { if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection); $err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']); throw new DibiDriverException($err['message'], $err['code']);
} }
$this->autocommit = TRUE; $this->autocommit = TRUE;
} }
@@ -217,7 +193,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
@@ -228,7 +204,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -244,48 +220,34 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested switch ($type) {
} case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
case dibi::IDENTIFIER:
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
public function escapeBinary($value) case dibi::BOOL:
{ return $value ? 1 : 0;
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
public function escapeIdentifier($value) default:
{ throw new InvalidArgumentException('Unsupported type.');
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format($this->fmtDate);
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
} }
@@ -305,20 +267,17 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -328,16 +287,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($offset > 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html // see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset; . ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) { } elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
} }
} }
@@ -362,7 +318,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getRowCount() public function getRowCount()
{ {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
} }
@@ -384,7 +340,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function seek($row) public function seek($row)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
@@ -406,15 +362,15 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns() public function getResultColumns()
{ {
$count = oci_num_fields($this->resultSet); $count = oci_num_fields($this->resultSet);
$columns = []; $columns = array();
for ($i = 1; $i <= $count; $i++) { for ($i = 1; $i <= $count; $i++) {
$type = oci_field_type($this->resultSet, $i); $type = oci_field_type($this->resultSet, $i);
$columns[] = [ $columns[] = 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' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type, 'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
]; );
} }
return $columns; return $columns;
} }
@@ -431,7 +387,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/********************* Dibi\Reflector ****************d*g**/ /********************* IDibiReflector ****************d*g**/
/** /**
@@ -441,13 +397,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables() public function getTables()
{ {
$res = $this->query('SELECT * FROM cat'); $res = $this->query('SELECT * FROM cat');
$tables = []; $tables = array();
while ($row = $res->fetch(FALSE)) { while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') { if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = [ $tables[] = array(
'name' => $row[0], 'name' => $row[0],
'view' => $row[1] === 'VIEW', 'view' => $row[1] === 'VIEW',
]; );
} }
} }
return $tables; return $tables;
@@ -461,7 +417,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getColumns($table) public function getColumns($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
@@ -472,7 +428,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
@@ -483,7 +439,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
} }

View File

@@ -5,10 +5,9 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi; require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
use PDO; require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/** /**
@@ -21,16 +20,16 @@ use PDO;
* - options (array) => driver specific options {@see PDO::__construct} * - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection * - resource (PDO) => existing connection
* - version * - version
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class PdoDriver implements Dibi\Driver, Dibi\ResultDriver class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{ {
use Dibi\Strict;
/** @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 */
@@ -44,12 +43,12 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('pdo')) { if (!extension_loaded('pdo')) {
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded."); throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
} }
} }
@@ -57,25 +56,25 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
$foo = & $config['dsn']; $foo = & $config['dsn'];
$foo = & $config['options']; $foo = & $config['options'];
Dibi\Helpers::alias($config, 'resource', 'pdo'); DibiConnection::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) { if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
unset($config['resource'], $config['pdo']);
} else { } else {
try { 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']);
} catch (\PDOException $e) { } catch (PDOException $e) {
if ($e->getMessage() === 'could not find driver') { if ($e->getMessage() === 'could not find driver') {
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.'); throw new DibiNotSupportedException('PHP extension for PDO is not loaded.');
} }
throw new Dibi\DriverException($e->getMessage(), $e->getCode()); throw new DibiDriverException($e->getMessage(), $e->getCode());
} }
} }
@@ -99,46 +98,34 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @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 = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1]; static $list = array('UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1);
$this->affectedRows = FALSE; $this->affectedRows = FALSE;
if (isset($list[$cmd])) { if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql); $this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows !== FALSE) {
return; if ($this->affectedRows === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} }
} else { } else {
$res = $this->connection->query($sql); $res = $this->connection->query($sql);
if ($res) {
if ($res === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} else {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
} }
list($sqlState, $code, $message) = $this->connection->errorInfo();
$message = "SQLSTATE[$sqlState]: $message";
switch ($this->driverName) {
case 'mysql':
throw MySqliDriver::createException($message, $code, $sql);
case 'oci':
throw OracleDriver::createException($message, $code, $sql);
case 'pgsql':
throw PostgreDriver::createException($message, $sqlState, $sql);
case 'sqlite':
throw Sqlite3Driver::createException($message, $code, $sql);
default:
throw new Dibi\DriverException($message, $code, $sql);
}
} }
@@ -166,13 +153,13 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
if (!$this->connection->beginTransaction()) { if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]); throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
} }
} }
@@ -181,13 +168,13 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
if (!$this->connection->commit()) { if (!$this->connection->commit()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]); throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
} }
} }
@@ -196,13 +183,13 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
if (!$this->connection->rollBack()) { if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo(); $err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]); throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
} }
} }
@@ -219,29 +206,30 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
switch ($this->driverName) { switch ($this->driverName) {
case 'mysql': case 'mysql':
return new MySqlReflector($this); return new DibiMySqlReflector($this);
case 'sqlite': case 'sqlite':
return new SqliteReflector($this); case 'sqlite2':
return new DibiSqliteReflector($this);
default: default:
throw new Dibi\NotSupportedException; throw new DibiNotSupportedException;
} }
} }
/** /**
* Result set driver factory. * Result set driver factory.
* @param \PDOStatement * @param PDOStatement
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver(\PDOStatement $resource) public function createResultDriver(PDOStatement $resource)
{ {
$res = clone $this; $res = clone $this;
$res->resultSet = $resource; $res->resultSet = $resource;
@@ -255,82 +243,70 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
if ($this->driverName === 'odbc') { switch ($type) {
return "'" . str_replace("'", "''", $value) . "'"; case dibi::TEXT:
} else { case dibi::BINARY:
return $this->connection->quote($value, PDO::PARAM_STR); if ($this->driverName === 'odbc') {
} return "'" . str_replace("'", "''", $value) . "'";
} } else {
return $this->connection->quote($value, $type === dibi::TEXT ? PDO::PARAM_STR : PDO::PARAM_LOB);
}
case dibi::IDENTIFIER:
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
public function escapeBinary($value) case 'oci':
{ case 'pgsql':
if ($this->driverName === 'odbc') { return '"' . str_replace('"', '""', $value) . '"';
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
}
case 'sqlite':
case 'sqlite2':
return '[' . strtr($value, '[]', ' ') . ']';
public function escapeIdentifier($value) case 'odbc':
{ case 'mssql':
switch ($this->driverName) { return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci': case 'dblib':
case 'pgsql': case 'sqlsrv':
return '"' . str_replace('"', '""', $value) . '"'; return '[' . str_replace(']', ']]', $value) . ']';
case 'sqlite': default:
return '[' . strtr($value, '[]', ' ') . ']'; return $value;
}
case 'odbc': case dibi::BOOL:
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']'; if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? 1 : 0;
}
case 'dblib': case dibi::DATE:
case 'sqlsrv': case dibi::DATETIME:
return '[' . str_replace(']', ']]', $value) . ']'; if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
if ($this->driverName === 'odbc') {
return $value->format($type === dibi::DATETIME ? '#m/d/Y H:i:s#' : '#m/d/Y#');
} else {
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
}
default: default:
return $value; throw new InvalidArgumentException('Unsupported type.');
} }
} }
public function escapeBool($value)
{
if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? 1 : 0;
}
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? "#m/d/Y H:i:s#" : "'Y-m-d H:i:s'");
}
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
* @param string * @param string
@@ -352,41 +328,40 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
case 'pgsql': case 'pgsql':
$bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off $bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off
$value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1); $value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); $value = strtr($value, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite': case 'sqlite':
case 'sqlite2':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\'); $value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'"; return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc': case 'odbc':
case 'mssql':
case 'dblib': case 'dblib':
case 'sqlsrv': case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']); $value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default: default:
throw new Dibi\NotImplementedException; throw new DibiNotImplementedException;
} }
} }
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -396,71 +371,61 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit < 0 && $offset < 1) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); return;
} }
switch ($this->driverName) { switch ($this->driverName) {
case 'mysql': case 'mysql':
if ($limit !== NULL || $offset) { $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 === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break; break;
case 'pgsql': case 'pgsql':
if ($limit !== NULL) { if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit; $sql .= ' LIMIT ' . (int) $limit;
} }
if ($offset) { if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset; $sql .= ' OFFSET ' . (int) $offset;
} }
break; break;
case 'sqlite': case 'sqlite':
if ($limit !== NULL || $offset) { case 'sqlite2':
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit) $sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break; break;
case 'oci': case 'oci':
if ($offset) { if ($offset > 0) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' $sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset; . ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit; $sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
} }
break; break;
case 'mssql':
case 'sqlsrv': case 'sqlsrv':
case 'dblib': case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012 if (version_compare($this->serverVersion, '11.0') >= 0) {
if ($limit !== NULL || $offset) { if ($offset >= 0 || $limit >= 0) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx $sql .= ' OFFSET ' . (int) $offset . ' ROWS'
$sql .= ' OFFSET ' . (int) $offset . ' ROWS ' . ($limit > 0 ? ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY' : '');
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
} }
break; break;
} }
// intentionally break omitted // intentionally break omitted
case 'odbc': case 'odbc':
if ($offset) { if ($offset < 1) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t'; $sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break; break;
} }
// intentionally break omitted // intentionally break omitted
default: default:
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.'); throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
} }
} }
@@ -496,7 +461,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function seek($row) public function seek($row)
{ {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
} }
@@ -513,30 +478,31 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns metadata for all columns in a result set. * Returns metadata for all columns in a result set.
* @return array * @return array
* @throws Dibi\Exception * @throws DibiException
*/ */
public function getResultColumns() public function getResultColumns()
{ {
$count = $this->resultSet->columnCount(); $count = $this->resultSet->columnCount();
$columns = []; $columns = 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 Dibi\NotSupportedException('Driver does not support meta data.'); throw new DibiNotSupportedException('Driver does not support meta data.');
} }
$row = $row + [ // PHP < 5.2.3 compatibility
// @see: http://php.net/manual/en/pdostatement.getcolumnmeta.php#pdostatement.getcolumnmeta.changelog
$row = $row + array(
'table' => NULL, 'table' => NULL,
'native_type' => 'VAR_STRING', 'native_type' => 'VAR_STRING',
]; );
$columns[] = [ $columns[] = array(
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['table'], 'table' => $row['table'],
'nativetype' => $row['native_type'], 'nativetype' => $row['native_type'],
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : NULL,
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'], 'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }
@@ -544,7 +510,7 @@ class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the result set resource. * Returns the result set resource.
* @return \PDOStatement * @return PDOStatement
*/ */
public function getResultResource() public function getResultResource()
{ {

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi driver for PostgreSQL database. * The dibi driver for PostgreSQL database.
@@ -20,12 +16,12 @@ use Dibi;
* - charset => character encoding to set (default is utf8) * - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link? * - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource * - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{ {
use Dibi\Strict;
/** @var resource Connection resource */ /** @var resource Connection resource */
private $connection; private $connection;
@@ -40,12 +36,12 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('pgsql')) { if (!extension_loaded('pgsql')) {
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded."); throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
} }
} }
@@ -53,50 +49,51 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
$error = NULL;
if (isset($config['resource'])) { if (isset($config['resource'])) {
$this->connection = $config['resource']; $this->connection = $config['resource'];
} else { } else {
$config += [ $config += array(
'charset' => 'utf8', 'charset' => 'utf8',
]; );
if (isset($config['string'])) { if (isset($config['string'])) {
$string = $config['string']; $string = $config['string'];
} else { } else {
$string = ''; $string = '';
Dibi\Helpers::alias($config, 'user', 'username'); DibiConnection::alias($config, 'user', 'username');
Dibi\Helpers::alias($config, 'dbname', 'database'); DibiConnection::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) { foreach (array('host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service') as $key) {
if (isset($config[$key])) { if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' '; $string .= $key . '=' . $config[$key] . ' ';
} }
} }
} }
set_error_handler(function($severity, $message) use (& $error) { DibiDriverException::tryError();
$error = $message;
});
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);
} }
restore_error_handler(); if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
} }
if (!is_resource($this->connection)) { if (!is_resource($this->connection)) {
throw new Dibi\DriverException($error ?: 'Connecting error.'); throw new DibiDriverException('Connecting error.');
} }
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE); if (isset($config['charset'])) {
DibiDriverException::tryError();
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) { pg_set_client_encoding($this->connection, $config['charset']);
throw self::createException(pg_last_error($this->connection)); if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
} }
if (isset($config['schema'])) { if (isset($config['schema'])) {
@@ -128,8 +125,8 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -137,7 +134,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @pg_query($this->connection, $sql); // intentionally @ $res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) { if ($res === FALSE) {
throw self::createException(pg_last_error($this->connection), NULL, $sql); throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
} elseif (is_resource($res)) { } elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res); $this->affectedRows = pg_affected_rows($res);
@@ -148,34 +145,6 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code = NULL, $sql = NULL)
{
if ($code === NULL && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
$code = $m[1];
$message = substr($message, strlen($m[0]));
}
if ($code === '0A000' && strpos($message, 'truncate') !== FALSE) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23502') {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif ($code === '23503') {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23505') {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $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
@@ -212,7 +181,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
@@ -224,7 +193,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
@@ -236,7 +205,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
@@ -250,7 +219,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function inTransaction() public function inTransaction()
{ {
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE); return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
} }
@@ -266,7 +235,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
@@ -277,7 +246,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Result set driver factory. * Result set driver factory.
* @param resource * @param resource
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver($resource) public function createResultDriver($resource)
{ {
@@ -293,54 +262,42 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
if (!is_resource($this->connection)) { switch ($type) {
throw new Dibi\Exception('Lost connection to server.'); case dibi::TEXT:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
case dibi::BINARY:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
case dibi::IDENTIFIER:
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 'TRUE' : 'FALSE';
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
default:
throw new InvalidArgumentException('Unsupported type.');
} }
return "'" . pg_escape_string($this->connection, $value) . "'";
}
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
}
public function escapeIdentifier($value)
{
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
}
public function escapeBool($value)
{
return $value ? 'TRUE' : 'FALSE';
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
} }
@@ -354,27 +311,24 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{ {
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off $bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value); $value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']); $value = strtr($value, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'"); return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
} }
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return pg_unescape_bytea($value); if ($type === dibi::BINARY) {
} return pg_unescape_bytea($value);
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -384,13 +338,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit >= 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit; $sql .= ' LIMIT ' . (int) $limit;
} }
if ($offset) {
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset; $sql .= ' OFFSET ' . (int) $offset;
} }
} }
@@ -459,13 +411,13 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns() public function getResultColumns()
{ {
$count = pg_num_fields($this->resultSet); $count = pg_num_fields($this->resultSet);
$columns = []; $columns = array();
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$row = [ $row = array(
'name' => pg_field_name($this->resultSet, $i), 'name' => pg_field_name($this->resultSet, $i),
'table' => pg_field_table($this->resultSet, $i), 'table' => pg_field_table($this->resultSet, $i),
'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; $columns[] = $row;
} }
@@ -484,7 +436,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
} }
/********************* Dibi\Reflector ****************d*g**/ /********************* IDibiReflector ****************d*g**/
/** /**
@@ -493,9 +445,9 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getTables() public function getTables()
{ {
$version = pg_parameter_status($this->getResource(), 'server_version'); $version = pg_parameter_status($this->resource, 'server_version');
if ($version < 7.4) { if ($version < 7.4) {
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.'); throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
} }
$query = " $query = "
@@ -523,7 +475,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = $this->query($query); $res = $this->query($query);
$tables = pg_fetch_all($res->resultSet); $tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : []; return $tables ? $tables : array();
} }
@@ -534,7 +486,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getColumns($table) public function getColumns($table)
{ {
$_table = $this->escapeText($this->escapeIdentifier($table)); $_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query(" $res = $this->query("
SELECT indkey SELECT indkey
FROM pg_class FROM pg_class
@@ -576,10 +528,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
"); ");
} }
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']); $size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = [ $columns[] = array(
'name' => $row['column_name'], 'name' => $row['column_name'],
'table' => $table, 'table' => $table,
'nativetype' => strtoupper($row['udt_name']), 'nativetype' => strtoupper($row['udt_name']),
@@ -588,7 +540,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'default' => $row['column_default'], 'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval', 'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }
@@ -601,7 +553,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
$_table = $this->escapeText($this->escapeIdentifier($table)); $_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query(" $res = $this->query("
SELECT SELECT
a.attnum AS ordinal_position, a.attnum AS ordinal_position,
@@ -616,7 +568,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY ordinal_position ORDER BY ordinal_position
"); ");
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name']; $columns[$row['ordinal_position']] = $row['column_name'];
} }
@@ -629,7 +581,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE pg_class.oid = $_table::regclass WHERE pg_class.oid = $_table::regclass
"); ");
$indexes = []; $indexes = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname']; $indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't'; $indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
@@ -649,7 +601,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
$_table = $this->escapeText($this->escapeIdentifier($table)); $_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query(" $res = $this->query("
SELECT SELECT
@@ -690,17 +642,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
c.conrelid = $_table::regclass c.conrelid = $_table::regclass
"); ");
$fKeys = $references = []; $fKeys = $references = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) { if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = [ $fKeys[$row['name']] = array(
'name' => $row['name'], 'name' => $row['name'],
'table' => $row['table'], 'table' => $row['table'],
'local' => [], 'local' => array(),
'foreign' => [], 'foreign' => array(),
'onUpdate' => $row['onUpdate'], 'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'], 'onDelete' => $row['onDelete'],
]; );
$l = explode(',', trim($row['conkey'], '{}')); $l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}')); $f = explode(',', trim($row['confkey'], '{}'));

View File

@@ -5,10 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi; require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
use SQLite3;
/** /**
@@ -21,16 +19,16 @@ use SQLite3;
* - dbcharset => database character encoding (will be converted to 'charset') * - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8) * - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource * - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options * - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
*/ */
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{ {
use Dibi\Strict;
/** @var SQLite3 Connection resource */ /** @var SQLite3 Connection resource */
private $connection; private $connection;
/** @var \SQLite3Result Resultset resource */ /** @var SQLite3Result Resultset resource */
private $resultSet; private $resultSet;
/** @var bool */ /** @var bool */
@@ -44,12 +42,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function __construct() public function __construct()
{ {
if (!extension_loaded('sqlite3')) { if (!extension_loaded('sqlite3')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded."); throw new DibiNotSupportedException("PHP extension 'sqlite3' is not loaded.");
} }
} }
@@ -57,11 +55,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Connects to a database. * Connects to a database.
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public function connect(array & $config) public function connect(array & $config)
{ {
Dibi\Helpers::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';
@@ -70,8 +68,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
} else { } else {
try { try {
$this->connection = new SQLite3($config['database']); $this->connection = new SQLite3($config['database']);
} catch (\Exception $e) { } catch (Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode()); throw new DibiDriverException($e->getMessage(), $e->getCode());
} }
} }
@@ -102,8 +100,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function query($sql) public function query($sql)
{ {
@@ -112,45 +110,15 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
} }
$res = @$this->connection->query($sql); // intentionally @ $res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) { if ($this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql); throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
} elseif ($res instanceof \SQLite3Result) { } elseif ($res instanceof SQLite3Result) {
return $this->createResultDriver($res); return $this->createResultDriver($res);
} }
} }
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== FALSE
|| strpos($message, 'is not unique') !== FALSE
|| strpos($message, 'UNIQUE constraint failed') !== FALSE
) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be NULL') !== FALSE
|| strpos($message, 'NOT NULL constraint failed') !== FALSE
) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== FALSE
|| strpos($message, 'FOREIGN KEY constraint failed') !== FALSE
) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\ConstraintViolationException($message, $code, $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
@@ -175,7 +143,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
@@ -187,7 +155,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
@@ -199,7 +167,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\DriverException * @throws DibiDriverException
*/ */
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
@@ -219,20 +187,20 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Dibi\Reflector * @return IDibiReflector
*/ */
public function getReflector() public function getReflector()
{ {
return new SqliteReflector($this); return new DibiSqliteReflector($this);
} }
/** /**
* Result set driver factory. * Result set driver factory.
* @param \SQLite3Result * @param SQLite3Result
* @return Dibi\ResultDriver * @return IDibiResultDriver
*/ */
public function createResultDriver(\SQLite3Result $resource) public function createResultDriver(SQLite3Result $resource)
{ {
$res = clone $this; $res = clone $this;
$res->resultSet = $resource; $res->resultSet = $resource;
@@ -246,47 +214,35 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
public function escapeText($value) public function escape($value, $type)
{ {
return "'" . $this->connection->escapeString($value) . "'"; switch ($type) {
} case dibi::TEXT:
return "'" . $this->connection->escapeString($value) . "'";
case dibi::BINARY:
return "X'" . bin2hex((string) $value) . "'";
public function escapeBinary($value) case dibi::IDENTIFIER:
{ return '[' . strtr($value, '[]', ' ') . ']';
return "X'" . bin2hex((string) $value) . "'";
}
case dibi::BOOL:
return $value ? 1 : 0;
public function escapeIdentifier($value) case dibi::DATE:
{ case dibi::DATETIME:
return '[' . strtr($value, '[]', ' ') . ']'; if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
} $value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
default:
public function escapeBool($value) throw new InvalidArgumentException('Unsupported type.');
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
} }
return $value->format($this->fmtDate);
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
} }
@@ -305,20 +261,17 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
public function unescapeBinary($value) public function unescape($value, $type)
{ {
return $value; if ($type === dibi::BINARY) {
} return $value;
}
throw new InvalidArgumentException('Unsupported type.');
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
} }
@@ -328,12 +281,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
*/ */
public function applyLimit(& $sql, $limit, $offset) public function applyLimit(& $sql, $limit, $offset)
{ {
if ($limit < 0 || $offset < 0) { if ($limit >= 0 || $offset > 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.'); $sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} elseif ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
} }
} }
@@ -354,11 +303,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/** /**
* Returns the number of rows in a result set. * Returns the number of rows in a result set.
* @return int * @return int
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function getRowCount() public function getRowCount()
{ {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.'); throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
} }
@@ -372,12 +321,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM); $row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT'; $charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) { if ($row && ($assoc || $charset)) {
$tmp = []; $tmp = array();
foreach ($row as $k => $v) { foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) { if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v); $v = iconv($this->dbcharset, $charset, $v);
} }
$tmp[str_replace(['[', ']'], '', $k)] = $v; $tmp[str_replace(array('[', ']'), '', $k)] = $v;
} }
return $tmp; return $tmp;
} }
@@ -389,11 +338,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* 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 bool TRUE on success, FALSE if unable to seek to specified record * @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\NotSupportedException * @throws DibiNotSupportedException
*/ */
public function seek($row) public function seek($row)
{ {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.'); throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
} }
@@ -415,15 +364,15 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns() public function getResultColumns()
{ {
$count = $this->resultSet->numColumns(); $count = $this->resultSet->numColumns();
$columns = []; $columns = array();
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null']; static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $count; $i++) {
$columns[] = [ $columns[] = array(
'name' => $this->resultSet->columnName($i), 'name' => $this->resultSet->columnName($i),
'table' => NULL, 'table' => NULL,
'fullname' => $this->resultSet->columnName($i), 'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)], 'nativetype' => $types[$this->resultSet->columnType($i)],
]; );
} }
return $columns; return $columns;
} }
@@ -450,7 +399,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments * @param int num of arguments
* @return void * @return void
*/ */
public function registerFunction($name, callable $callback, $numArgs = -1) public function registerFunction($name, $callback, $numArgs = -1)
{ {
$this->connection->createFunction($name, $callback, $numArgs); $this->connection->createFunction($name, $callback, $numArgs);
} }
@@ -464,7 +413,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments * @param int num of arguments
* @return void * @return void
*/ */
public function registerAggregateFunction($name, callable $rowCallback, callable $agrCallback, $numArgs = -1) public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
{ {
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs); $this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
} }

View File

@@ -0,0 +1,419 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
* The dibi driver for SQLite database.
*
* Driver options:
* - database (or file) => the filename of the SQLite database
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - 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 (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite')) {
throw new DibiNotSupportedException("PHP extension 'sqlite' 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';
$errorMsg = '';
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @sqlite_open($config['database'], 0666, $errorMsg); // intentionally @
} else {
$this->connection = @sqlite_popen($config['database'], 0666, $errorMsg); // intentionally @
}
if (!$this->connection) {
throw new DibiDriverException($errorMsg);
}
$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.
* @return void
*/
public function disconnect()
{
sqlite_close($this->connection);
}
/**
* 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);
}
DibiDriverException::tryError();
if ($this->buffered) {
$res = sqlite_query($this->connection, $sql);
} else {
$res = sqlite_unbuffered_query($this->connection, $sql);
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, sqlite_last_error($this->connection), $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 sqlite_changes($this->connection);
}
/**
* 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 sqlite_last_insert_rowid($this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
$this->query('BEGIN');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
$this->query('COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
$this->query('ROLLBACK');
}
/**
* 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()
{
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**/
/**
* 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 "'" . sqlite_escape_string($value) . "'";
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
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 DibiNotSupportedException;
}
/**
* 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 ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return sqlite_num_rows($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)
{
$row = sqlite_fetch_array($this->resultSet, $assoc ? SQLITE_ASSOC : SQLITE_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 bool TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
public function seek($row)
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return sqlite_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = sqlite_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i));
$pair = explode('.', $name);
$columns[] = array(
'name' => isset($pair[1]) ? $pair[1] : $pair[0],
'table' => isset($pair[1]) ? $pair[0] : NULL,
'fullname' => $name,
'nativetype' => NULL,
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* 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)
{
sqlite_create_function($this->connection, $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)
{
sqlite_create_aggregate($this->connection, $name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -5,24 +5,20 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Drivers;
use Dibi;
/** /**
* The dibi reflector for SQLite database. * The dibi reflector for SQLite database.
*
* @package dibi\drivers
* @internal * @internal
*/ */
class SqliteReflector implements Dibi\Reflector class DibiSqliteReflector extends DibiObject implements IDibiReflector
{ {
use Dibi\Strict; /** @var IDibiDriver */
/** @var Dibi\Driver */
private $driver; private $driver;
public function __construct(Dibi\Driver $driver) public function __construct(IDibiDriver $driver)
{ {
$this->driver = $driver; $this->driver = $driver;
} }
@@ -40,7 +36,7 @@ class SqliteReflector implements Dibi\Reflector
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view') SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name ORDER BY name
"); ");
$tables = []; $tables = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$tables[] = $row; $tables[] = $row;
} }
@@ -55,12 +51,12 @@ class SqliteReflector implements Dibi\Reflector
*/ */
public function getColumns($table) public function getColumns($table)
{ {
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})"); $res = $this->driver->query("PRAGMA table_info({$this->driver->escape($table, dibi::IDENTIFIER)})");
$columns = []; $columns = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$column = $row['name']; $column = $row['name'];
$type = explode('(', $row['type']); $type = explode('(', $row['type']);
$columns[] = [ $columns[] = array(
'name' => $column, 'name' => $column,
'table' => $table, 'table' => $table,
'fullname' => "$table.$column", 'fullname' => "$table.$column",
@@ -70,7 +66,7 @@ class SqliteReflector implements Dibi\Reflector
'default' => $row['dflt_value'], 'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER', 'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'vendor' => $row, 'vendor' => $row,
]; );
} }
return $columns; return $columns;
} }
@@ -83,15 +79,15 @@ class SqliteReflector implements Dibi\Reflector
*/ */
public function getIndexes($table) public function getIndexes($table)
{ {
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})"); $res = $this->driver->query("PRAGMA index_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$indexes = []; $indexes = array();
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name']; $indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique']; $indexes[$row['name']]['unique'] = (bool) $row['unique'];
} }
foreach ($indexes as $index => $values) { foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})"); $res = $this->driver->query("PRAGMA index_info({$this->driver->escape($index, dibi::IDENTIFIER)})");
while ($row = $res->fetch(TRUE)) { while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name']; $indexes[$index]['columns'][$row['seqno']] = $row['name'];
} }
@@ -112,12 +108,12 @@ class SqliteReflector implements Dibi\Reflector
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) { foreach ($columns as $column) {
if ($column['vendor']['pk']) { if ($column['vendor']['pk']) {
$indexes[] = [ $indexes[] = array(
'name' => 'ROWID', 'name' => 'ROWID',
'unique' => TRUE, 'unique' => TRUE,
'primary' => TRUE, 'primary' => TRUE,
'columns' => [$column['name']], 'columns' => array($column['name']),
]; );
break; break;
} }
} }
@@ -134,8 +130,11 @@ class SqliteReflector implements Dibi\Reflector
*/ */
public function getForeignKeys($table) public function getForeignKeys($table)
{ {
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})"); if (!($this->driver instanceof DibiSqlite3Driver)) {
$keys = []; // 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)) { while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name $keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns $keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns

View File

@@ -0,0 +1,14 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiMsSql2005Driver.php';
class DibiSqlsrvDriver extends DibiMsSql2005Driver
{
}

View File

@@ -5,56 +5,55 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
use Dibi\Type;
/** /**
* This class is static container class for creating DB objects and * This class is static container class for creating DB objects and
* store connections info. * store connections info.
*
* @package dibi
*/ */
class dibi class dibi
{ {
use Dibi\Strict; /** column type */
const TEXT = 's', // as 'string'
BINARY = 'bin',
BOOL = 'b',
INTEGER = 'i',
FLOAT = 'f',
DATE = 'd',
DATETIME = 't',
TIME = 't';
const const IDENTIFIER = 'n',
AFFECTED_ROWS = 'a', AFFECTED_ROWS = 'a';
IDENTIFIER = 'n';
/** version */
const
VERSION = '3.0.0',
REVISION = 'released on 2015-11-07';
/** sorting order */
const
ASC = 'ASC',
DESC = 'DESC';
/** @deprecated */ /** @deprecated */
const const FIELD_TEXT = self::TEXT,
TEXT = Type::TEXT, FIELD_BINARY = self::BINARY,
BINARY = Type::BINARY, FIELD_BOOL = self::BOOL,
BOOL = Type::BOOL, FIELD_INTEGER = self::INTEGER,
INTEGER = Type::INTEGER, FIELD_FLOAT = self::FLOAT,
FLOAT = Type::FLOAT, FIELD_DATE = self::DATE,
DATE = Type::DATE, FIELD_DATETIME = self::DATETIME,
DATETIME = Type::DATETIME, FIELD_TIME = self::TIME;
TIME = Type::TIME,
FIELD_TEXT = Type::TEXT,
FIELD_BINARY = Type::BINARY,
FIELD_BOOL = Type::BOOL,
FIELD_INTEGER = Type::INTEGER,
FIELD_FLOAT = Type::FLOAT,
FIELD_DATE = Type::DATE,
FIELD_DATETIME = Type::DATETIME,
FIELD_TIME = Type::TIME;
/** @var Dibi\Connection[] Connection registry storage for DibiConnection objects */ /** version */
private static $registry = []; const VERSION = '2.3.5',
REVISION = 'released on 2015-12-16';
/** @var Dibi\Connection Current connection */ /** sorting order */
const ASC = 'ASC',
DESC = 'DESC';
/** @var DibiConnection[] Connection registry storage for DibiConnection objects */
private static $registry = array();
/** @var DibiConnection Current connection */
private static $connection; private static $connection;
/** @var array @see addHandler */
private static $handlers = array();
/** @var string Last SQL command @see dibi::query() */ /** @var string Last SQL command @see dibi::query() */
public static $sql; public static $sql;
@@ -84,20 +83,20 @@ class dibi
/** /**
* Creates a new Connection object and connects it to specified database. * Creates a new DibiConnection object and connects it to specified database.
* @param mixed connection parameters * @param mixed connection parameters
* @param string connection name * @param string connection name
* @return Dibi\Connection * @return DibiConnection
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function connect($config = [], $name = 0) public static function connect($config = array(), $name = 0)
{ {
return self::$connection = self::$registry[$name] = new Dibi\Connection($config, $name); return self::$connection = self::$registry[$name] = new DibiConnection($config, $name);
} }
/** /**
* Disconnects from database (doesn't destroy Connection object). * Disconnects from database (doesn't destroy DibiConnection object).
* @return void * @return void
*/ */
public static function disconnect() public static function disconnect()
@@ -119,21 +118,21 @@ class dibi
/** /**
* Retrieve active connection. * Retrieve active connection.
* @param string connection registy name * @param string connection registy name
* @return Dibi\Connection * @return DibiConnection
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function getConnection($name = NULL) public static function getConnection($name = NULL)
{ {
if ($name === NULL) { if ($name === NULL) {
if (self::$connection === NULL) { if (self::$connection === NULL) {
throw new Dibi\Exception('Dibi is not connected to database.'); throw new DibiException('Dibi is not connected to database.');
} }
return self::$connection; return self::$connection;
} }
if (!isset(self::$registry[$name])) { if (!isset(self::$registry[$name])) {
throw new Dibi\Exception("There is no connection named '$name'."); throw new DibiException("There is no connection named '$name'.");
} }
return self::$registry[$name]; return self::$registry[$name];
@@ -142,10 +141,10 @@ class dibi
/** /**
* Sets connection. * Sets connection.
* @param Dibi\Connection * @param DibiConnection
* @return Dibi\Connection * @return DibiConnection
*/ */
public static function setConnection(Dibi\Connection $connection) public static function setConnection(DibiConnection $connection)
{ {
return self::$connection = $connection; return self::$connection = $connection;
} }
@@ -165,10 +164,10 @@ class dibi
/** /**
* Generates and executes SQL query - Monostate for Dibi\Connection::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 Dibi\Result|int result set object (if any) * @return DibiResult|int result set object (if any)
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function query($args) public static function query($args)
{ {
@@ -178,9 +177,9 @@ class dibi
/** /**
* Executes the SQL query - Monostate for Dibi\Connection::nativeQuery(). * Executes the SQL query - Monostate for DibiConnection::nativeQuery().
* @param string SQL statement. * @param string SQL statement.
* @return Dibi\Result|int result set object (if any) * @return DibiResult|int result set object (if any)
*/ */
public static function nativeQuery($sql) public static function nativeQuery($sql)
{ {
@@ -189,7 +188,7 @@ class dibi
/** /**
* Generates and prints SQL query - Monostate for Dibi\Connection::test(). * Generates and prints SQL query - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return bool * @return bool
*/ */
@@ -201,9 +200,9 @@ class dibi
/** /**
* Generates and returns SQL query as DataSource - Monostate for Dibi\Connection::test(). * Generates and returns SQL query as DibiDataSource - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Dibi\DataSource * @return DibiDataSource
*/ */
public static function dataSource($args) public static function dataSource($args)
{ {
@@ -213,10 +212,10 @@ class dibi
/** /**
* Executes SQL query and fetch result - Monostate for Dibi\Connection::query() & fetch(). * Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Dibi\Row * @return DibiRow
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function fetch($args) public static function fetch($args)
{ {
@@ -226,10 +225,10 @@ class dibi
/** /**
* Executes SQL query and fetch results - Monostate for Dibi\Connection::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 Dibi\Row[] * @return DibiRow[]
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function fetchAll($args) public static function fetchAll($args)
{ {
@@ -239,10 +238,10 @@ class dibi
/** /**
* Executes SQL query and fetch first column - Monostate for Dibi\Connection::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
* @return string * @return string
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function fetchSingle($args) public static function fetchSingle($args)
{ {
@@ -252,10 +251,10 @@ class dibi
/** /**
* Executes SQL query and fetch pairs - Monostate for Dibi\Connection::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
* @return string * @return string
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function fetchPairs($args) public static function fetchPairs($args)
{ {
@@ -266,9 +265,9 @@ class dibi
/** /**
* Gets the number of affected rows. * Gets the number of affected rows.
* Monostate for Dibi\Connection::getAffectedRows() * Monostate for DibiConnection::getAffectedRows()
* @return int number of rows * @return int number of rows
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function getAffectedRows() public static function getAffectedRows()
{ {
@@ -279,7 +278,7 @@ class dibi
/** /**
* Gets the number of affected rows. Alias for getAffectedRows(). * Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows * @return int number of rows
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function affectedRows() public static function affectedRows()
{ {
@@ -289,10 +288,10 @@ class dibi
/** /**
* 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 Dibi\Connection::getInsertId() * Monostate for DibiConnection::getInsertId()
* @param string optional sequence name * @param string optional sequence name
* @return int * @return int
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function getInsertId($sequence = NULL) public static function getInsertId($sequence = NULL)
{ {
@@ -304,7 +303,7 @@ class dibi
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId(). * 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 Dibi\Exception * @throws DibiException
*/ */
public static function insertId($sequence = NULL) public static function insertId($sequence = NULL)
{ {
@@ -313,10 +312,10 @@ class dibi
/** /**
* Begins a transaction - Monostate for Dibi\Connection::begin(). * Begins a transaction - Monostate for DibiConnection::begin().
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function begin($savepoint = NULL) public static function begin($savepoint = NULL)
{ {
@@ -325,10 +324,10 @@ class dibi
/** /**
* Commits statements in a transaction - Monostate for Dibi\Connection::commit($savepoint = NULL). * Commits statements in a transaction - Monostate for DibiConnection::commit($savepoint = NULL).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function commit($savepoint = NULL) public static function commit($savepoint = NULL)
{ {
@@ -337,10 +336,10 @@ class dibi
/** /**
* Rollback changes in a transaction - Monostate for Dibi\Connection::rollback(). * Rollback changes in a transaction - Monostate for DibiConnection::rollback().
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws Dibi\Exception * @throws DibiException
*/ */
public static function rollback($savepoint = NULL) public static function rollback($savepoint = NULL)
{ {
@@ -349,8 +348,8 @@ class dibi
/** /**
* Gets a information about the current database - Monostate for Dibi\Connection::getDatabaseInfo(). * Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo().
* @return Dibi\Reflection\Database * @return DibiDatabaseInfo
*/ */
public static function getDatabaseInfo() public static function getDatabaseInfo()
{ {
@@ -365,7 +364,7 @@ class dibi
*/ */
public static function loadFile($file) public static function loadFile($file)
{ {
return Dibi\Helpers::loadFromFile(self::getConnection(), $file); return self::getConnection()->loadFile($file);
} }
@@ -373,7 +372,7 @@ class dibi
/** /**
* @return Dibi\Fluent * @return DibiFluent
*/ */
public static function command() public static function command()
{ {
@@ -383,19 +382,19 @@ class dibi
/** /**
* @param string column name * @param string column name
* @return Dibi\Fluent * @return DibiFluent
*/ */
public static function select($args) public static function select($args)
{ {
$args = func_get_args(); $args = func_get_args();
return call_user_func_array([self::getConnection(), 'select'], $args); return call_user_func_array(array(self::getConnection(), 'select'), $args);
} }
/** /**
* @param string table * @param string table
* @param array * @param array
* @return Dibi\Fluent * @return DibiFluent
*/ */
public static function update($table, $args) public static function update($table, $args)
{ {
@@ -406,7 +405,7 @@ class dibi
/** /**
* @param string table * @param string table
* @param array * @param array
* @return Dibi\Fluent * @return DibiFluent
*/ */
public static function insert($table, $args) public static function insert($table, $args)
{ {
@@ -416,7 +415,7 @@ class dibi
/** /**
* @param string table * @param string table
* @return Dibi\Fluent * @return DibiFluent
*/ */
public static function delete($table) public static function delete($table)
{ {
@@ -428,8 +427,8 @@ class dibi
/** /**
* Returns substitution hashmap - Monostate for Dibi\Connection::getSubstitutes(). * Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
* @return Dibi\HashMap * @return DibiHashMap
*/ */
public static function getSubstitutes() public static function getSubstitutes()
{ {
@@ -441,14 +440,89 @@ class dibi
/** /**
* Prints out a syntax highlighted version of the SQL command or Result. * Prints out a syntax highlighted version of the SQL command or DibiResult.
* @param string|Result * @param string|DibiResult
* @param bool return output instead of printing it? * @param bool return output instead of printing it?
* @return string * @return string
*/ */
public static function dump($sql = NULL, $return = FALSE) public static function dump($sql = NULL, $return = FALSE)
{ {
return Dibi\Helpers::dump($sql, $return); ob_start();
if ($sql instanceof DibiResult) {
$sql->dump();
} else {
if ($sql === NULL) {
$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|FETCH\s+NEXT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE|START\s+TRANSACTION|BEGIN|COMMIT|ROLLBACK(?:\s+TO\s+SAVEPOINT)?|(?:RELEASE\s+)?SAVEPOINT';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// insert new lines
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces
$sql = preg_replace('#[ \t]{2,}#', ' ', $sql);
$sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
// syntax highlight
$highlighter = "#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is";
if (PHP_SAPI === 'cli') {
if (substr(getenv('TERM'), 0, 5) === 'xterm') {
$sql = preg_replace_callback($highlighter, array('dibi', 'cliHighlightCallback'), $sql);
}
echo trim($sql) . "\n\n";
} else {
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback($highlighter, array('dibi', 'highlightCallback'), $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n\n";
}
}
if ($return) {
return ob_get_clean();
} else {
ob_end_flush();
}
}
private static function highlightCallback($matches)
{
if (!empty($matches[1])) { // comment
return '<em style="color:gray">' . $matches[1] . '</em>';
} elseif (!empty($matches[2])) { // error
return '<strong style="color:red">' . $matches[2] . '</strong>';
} elseif (!empty($matches[3])) { // most important keywords
return '<strong style="color:blue">' . $matches[3] . '</strong>';
} elseif (!empty($matches[4])) { // other keywords
return '<strong style="color:green">' . $matches[4] . '</strong>';
}
}
private static function cliHighlightCallback($matches)
{
if (!empty($matches[1])) { // comment
return "\033[1;30m" . $matches[1] . "\033[0m";
} elseif (!empty($matches[2])) { // error
return "\033[1;31m" . $matches[2] . "\033[0m";
} elseif (!empty($matches[3])) { // most important keywords
return "\033[1;34m" . $matches[3] . "\033[0m";
} elseif (!empty($matches[4])) { // other keywords
return "\033[1;32m" . $matches[4] . "\033[0m";
}
} }
} }

View File

@@ -5,37 +5,33 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
use Traversable;
/** /**
* dibi connection. * dibi connection.
* *
* @package dibi
*
* @property-read int $affectedRows * @property-read int $affectedRows
* @property-read int $insertId * @property-read int $insertId
*/ */
class Connection class DibiConnection extends DibiObject
{ {
use Strict; /** @var array of function (DibiEvent $event); Occurs after query is executed */
/** @var array of function (Event $event); Occurs after query is executed */
public $onEvent; public $onEvent;
/** @var array Current connection configuration */ /** @var array Current connection configuration */
private $config; private $config;
/** @var Driver */ /** @var IDibiDriver */
private $driver; private $driver;
/** @var Translator */ /** @var DibiTranslator */
private $translator; private $translator;
/** @var bool Is connected? */ /** @var bool Is connected? */
private $connected = FALSE; private $connected = FALSE;
/** @var HashMap Substitutes for identifiers */ /** @var DibiHashMap Substitutes for identifiers */
private $substitutes; private $substitutes;
@@ -51,7 +47,7 @@ class Connection
* @param mixed connection parameters * @param mixed connection parameters
* @param string connection name * @param string connection name
* @throws Exception * @throws DibiException
*/ */
public function __construct($config, $name = NULL) public function __construct($config, $name = NULL)
{ {
@@ -59,66 +55,71 @@ class Connection
parse_str($config, $config); parse_str($config, $config);
} elseif ($config instanceof Traversable) { } elseif ($config instanceof Traversable) {
$tmp = []; $tmp = array();
foreach ($config as $key => $val) { foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val; $tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
} }
$config = $tmp; $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 object.');
} }
Helpers::alias($config, 'username', 'user'); self::alias($config, 'username', 'user');
Helpers::alias($config, 'password', 'pass'); self::alias($config, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname'); self::alias($config, 'host', 'hostname');
Helpers::alias($config, 'result|formatDate', 'resultDate'); self::alias($config, 'result|formatDate', 'resultDate');
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime'); self::alias($config, 'result|formatDateTime', 'resultDateTime');
if (!isset($config['driver'])) { if (!isset($config['driver'])) {
$config['driver'] = \dibi::$defaultDriver; $config['driver'] = dibi::$defaultDriver;
} }
if ($config['driver'] instanceof Driver) { if ($config['driver'] instanceof IDibiDriver) {
$this->driver = $config['driver']; $this->driver = $config['driver'];
$config['driver'] = get_class($this->driver); $config['driver'] = get_class($this->driver);
} elseif (is_subclass_of($config['driver'], 'Dibi\Driver')) { } elseif (PHP_VERSION_ID >= 50307 && is_subclass_of($config['driver'], 'IDibiDriver')) {
$this->driver = new $config['driver']; $this->driver = new $config['driver'];
} else { } else {
$class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($config['driver']))); $class = preg_replace(array('#\W#', '#sql#'), array('_', 'Sql'), ucfirst(strtolower($config['driver'])));
$class = "Dibi\\Drivers\\{$class}Driver"; $class = "Dibi{$class}Driver";
if (!class_exists($class)) { if (!class_exists($class)) {
throw new Exception("Unable to create instance of dibi driver '$class'."); include_once dirname(__FILE__) . "/../drivers/$class.php";
if (!class_exists($class, FALSE)) {
throw new DibiException("Unable to create instance of dibi driver '$class'.");
}
} }
$this->driver = new $class; $this->driver = new $class;
} }
$config['name'] = $name; $config['name'] = $name;
$this->config = $config; $this->config = $config;
$this->translator = new DibiTranslator($this);
// profiler // profiler
$profilerCfg = & $config['profiler']; $profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) { if (is_scalar($profilerCfg)) {
$profilerCfg = ['run' => (bool) $profilerCfg]; $profilerCfg = array('run' => (bool) $profilerCfg);
} }
if (!empty($profilerCfg['run'])) { if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY; $filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
if (isset($profilerCfg['file'])) { if (isset($profilerCfg['file'])) {
$this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $filter), 'logEvent']; $this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
} }
if (Loggers\FirePhpLogger::isAvailable()) { if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent']; $this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
} }
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('Dibi\Bridges\Nette\Panel')) { if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('DibiNettePanel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter); $panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this); $panel->register($this);
} }
} }
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; }); $this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
if (!empty($config['substitutes'])) { if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) { foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value; $this->substitutes->$key = $value;
@@ -148,13 +149,13 @@ class Connection
*/ */
final public function connect() final public function connect()
{ {
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL; $event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
try { try {
$this->driver->connect($this->config); $this->driver->connect($this->config);
$this->connected = TRUE; $this->connected = TRUE;
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
} catch (Exception $e) { } catch (DibiException $e) {
$event && $this->onEvent($event->done($e)); $event && $this->onEvent($event->done($e));
throw $e; throw $e;
} }
@@ -203,17 +204,30 @@ class Connection
} }
/** @deprecated */ /**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(& $config, $key, $alias) public static function alias(& $config, $key, $alias)
{ {
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED); $foo = & $config;
Helpers::alias($config, $key, $alias); foreach (explode('|', $key) as $key) {
$foo = & $foo[$key];
}
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
} }
/** /**
* Returns the driver and connects to a database in lazy mode. * Returns the driver and connects to a database in lazy mode.
* @return Driver * @return IDibiDriver
*/ */
final public function getDriver() final public function getDriver()
{ {
@@ -225,8 +239,8 @@ class Connection
/** /**
* 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 Result|int result set object (if any) * @return DibiResult|int result set object (if any)
* @throws Exception * @throws DibiException
*/ */
final public function query($args) final public function query($args)
{ {
@@ -239,7 +253,7 @@ class Connection
* Generates SQL query. * Generates SQL query.
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return string
* @throws Exception * @throws DibiException
*/ */
final public function translate($args) final public function translate($args)
{ {
@@ -257,12 +271,12 @@ class Connection
{ {
$args = func_get_args(); $args = func_get_args();
try { try {
Helpers::dump($this->translateArgs($args)); dibi::dump($this->translateArgs($args));
return TRUE; return TRUE;
} catch (Exception $e) { } catch (DibiException $e) {
if ($e->getSql()) { if ($e->getSql()) {
Helpers::dump($e->getSql()); dibi::dump($e->getSql());
} else { } else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>'); echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
} }
@@ -272,15 +286,15 @@ class Connection
/** /**
* Generates (translates) and returns SQL query as DataSource. * Generates (translates) and returns SQL query as DibiDataSource.
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return DataSource * @return DibiDataSource
* @throws Exception * @throws DibiException
*/ */
final public function dataSource($args) final public function dataSource($args)
{ {
$args = func_get_args(); $args = func_get_args();
return new DataSource($this->translateArgs($args), $this); return new DibiDataSource($this->translateArgs($args), $this);
} }
@@ -292,30 +306,26 @@ class Connection
private function translateArgs($args) private function translateArgs($args)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
if (!$this->translator) { return $this->translator->translate($args);
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
} }
/** /**
* Executes the SQL query. * Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return Result|int result set object (if any) * @return DibiResult|int result set object (if any)
* @throws Exception * @throws DibiException
*/ */
final public function nativeQuery($sql) final public function nativeQuery($sql)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
\dibi::$sql = $sql; dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL; $event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
try { try {
$res = $this->driver->query($sql); $res = $this->driver->query($sql);
} catch (Exception $e) { } catch (DibiException $e) {
$event && $this->onEvent($event->done($e)); $event && $this->onEvent($event->done($e));
throw $e; throw $e;
} }
@@ -334,14 +344,14 @@ class Connection
/** /**
* 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 Exception * @throws DibiException
*/ */
public function getAffectedRows() public function getAffectedRows()
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$rows = $this->driver->getAffectedRows(); $rows = $this->driver->getAffectedRows();
if (!is_int($rows) || $rows < 0) { if (!is_int($rows) || $rows < 0) {
throw new Exception('Cannot retrieve number of affected rows.'); throw new DibiException('Cannot retrieve number of affected rows.');
} }
return $rows; return $rows;
} }
@@ -350,7 +360,7 @@ class Connection
/** /**
* Gets the number of affected rows. Alias for getAffectedRows(). * Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows * @return int number of rows
* @throws Exception * @throws DibiException
*/ */
public function affectedRows() public function affectedRows()
{ {
@@ -362,14 +372,14 @@ class 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.
* @param string optional sequence name * @param string optional sequence name
* @return int * @return int
* @throws Exception * @throws DibiException
*/ */
public function getInsertId($sequence = NULL) public function getInsertId($sequence = NULL)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence); $id = $this->driver->getInsertId($sequence);
if ($id < 1) { if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.'); throw new DibiException('Cannot retrieve last generated ID.');
} }
return (int) $id; return (int) $id;
} }
@@ -379,7 +389,7 @@ class Connection
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId(). * 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 Exception * @throws DibiException
*/ */
public function insertId($sequence = NULL) public function insertId($sequence = NULL)
{ {
@@ -395,12 +405,12 @@ class Connection
public function begin($savepoint = NULL) public function begin($savepoint = NULL)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL; $event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
try { try {
$this->driver->begin($savepoint); $this->driver->begin($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
} catch (Exception $e) { } catch (DibiException $e) {
$event && $this->onEvent($event->done($e)); $event && $this->onEvent($event->done($e));
throw $e; throw $e;
} }
@@ -415,12 +425,12 @@ class Connection
public function commit($savepoint = NULL) public function commit($savepoint = NULL)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL; $event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
try { try {
$this->driver->commit($savepoint); $this->driver->commit($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
} catch (Exception $e) { } catch (DibiException $e) {
$event && $this->onEvent($event->done($e)); $event && $this->onEvent($event->done($e));
throw $e; throw $e;
} }
@@ -435,12 +445,12 @@ class Connection
public function rollback($savepoint = NULL) public function rollback($savepoint = NULL)
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL; $event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
try { try {
$this->driver->rollback($savepoint); $this->driver->rollback($savepoint);
$event && $this->onEvent($event->done()); $event && $this->onEvent($event->done());
} catch (Exception $e) { } catch (DibiException $e) {
$event && $this->onEvent($event->done($e)); $event && $this->onEvent($event->done($e));
throw $e; throw $e;
} }
@@ -449,14 +459,14 @@ class Connection
/** /**
* Result set factory. * Result set factory.
* @param ResultDriver * @param IDibiResultDriver
* @return Result * @return DibiResult
*/ */
public function createResultSet(ResultDriver $resultDriver) public function createResultSet(IDibiResultDriver $resultDriver)
{ {
$res = new Result($resultDriver); $res = new DibiResult($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate']) return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']); ->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
} }
@@ -464,17 +474,17 @@ class Connection
/** /**
* @return Fluent * @return DibiFluent
*/ */
public function command() public function command()
{ {
return new Fluent($this); return new DibiFluent($this);
} }
/** /**
* @param string column name * @param string column name
* @return Fluent * @return DibiFluent
*/ */
public function select($args) public function select($args)
{ {
@@ -486,12 +496,12 @@ class Connection
/** /**
* @param string table * @param string table
* @param array * @param array
* @return Fluent * @return DibiFluent
*/ */
public function update($table, $args) public function update($table, $args)
{ {
if (!(is_array($args) || $args instanceof Traversable)) { if (!(is_array($args) || $args instanceof Traversable)) {
throw new \InvalidArgumentException('Arguments must be array or 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);
} }
@@ -500,14 +510,14 @@ class Connection
/** /**
* @param string table * @param string table
* @param array * @param array
* @return Fluent * @return DibiFluent
*/ */
public function insert($table, $args) public function insert($table, $args)
{ {
if ($args instanceof Traversable) { if ($args instanceof Traversable) {
$args = iterator_to_array($args); $args = iterator_to_array($args);
} elseif (!is_array($args)) { } elseif (!is_array($args)) {
throw new \InvalidArgumentException('Arguments must be array or Traversable.'); 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', $args);
@@ -516,7 +526,7 @@ class Connection
/** /**
* @param string table * @param string table
* @return Fluent * @return DibiFluent
*/ */
public function delete($table) public function delete($table)
{ {
@@ -529,7 +539,7 @@ class Connection
/** /**
* Returns substitution hashmap. * Returns substitution hashmap.
* @return HashMap * @return DibiHashMap
*/ */
public function getSubstitutes() public function getSubstitutes()
{ {
@@ -543,9 +553,16 @@ class Connection
*/ */
public function substitute($value) public function substitute($value)
{ {
return strpos($value, ':') === FALSE return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
? $value }
: preg_replace_callback('#:([^:\s]*):#', function ($m) { $this->substitutes->{$m[1]}; }, $value);
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
} }
@@ -555,8 +572,8 @@ class Connection
/** /**
* Executes SQL query and fetch result - shortcut for query() & fetch(). * Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Row * @return DibiRow
* @throws Exception * @throws DibiException
*/ */
public function fetch($args) public function fetch($args)
{ {
@@ -568,8 +585,8 @@ class Connection
/** /**
* Executes SQL query and fetch results - shortcut for query() & fetchAll(). * Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return Row[] * @return DibiRow[]
* @throws Exception * @throws DibiException
*/ */
public function fetchAll($args) public function fetchAll($args)
{ {
@@ -582,7 +599,7 @@ class Connection
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle(). * Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return string
* @throws Exception * @throws DibiException
*/ */
public function fetchSingle($args) public function fetchSingle($args)
{ {
@@ -595,7 +612,7 @@ class Connection
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs(). * Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments * @param array|mixed one or more arguments
* @return string * @return string
* @throws Exception * @throws DibiException
*/ */
public function fetchPairs($args) public function fetchPairs($args)
{ {
@@ -604,37 +621,59 @@ class Connection
} }
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/ /********************* misc ****************d*g**/
/** /**
* Import SQL dump from file. * Import SQL dump from file - extreme fast!
* @param string filename * @param string filename
* @return int count of sql commands * @return int count of sql commands
*/ */
public function loadFile($file) public function loadFile($file)
{ {
return Helpers::loadFromFile($this, $file); $this->connected || $this->connect();
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$delimiter = ';';
$sql = '';
while (!feof($handle)) {
$s = rtrim(fgets($handle));
if (substr($s, 0, 10) === 'DELIMITER ') {
$delimiter = substr($s, 10);
} elseif (substr($s, -strlen($delimiter)) === $delimiter) {
$sql .= substr($s, 0, -strlen($delimiter));
$this->driver->query($sql);
$sql = '';
$count++;
} else {
$sql .= $s . "\n";
}
}
if (trim($sql) !== '') {
$this->driver->query($sql);
$count++;
}
fclose($handle);
return $count;
} }
/** /**
* Gets a information about the current database. * Gets a information about the current database.
* @return Reflection\Database * @return DibiDatabaseInfo
*/ */
public function getDatabaseInfo() public function getDatabaseInfo()
{ {
$this->connected || $this->connect(); $this->connected || $this->connect();
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL); return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
} }
@@ -643,7 +682,7 @@ class Connection
*/ */
public function __wakeup() public function __wakeup()
{ {
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.'); throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
} }
@@ -652,15 +691,7 @@ class Connection
*/ */
public function __sleep() public function __sleep()
{ {
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.'); throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
protected function onEvent($arg)
{
foreach ($this->onEvent ?: [] as $handler) {
call_user_func($handler, $arg);
}
} }
} }

View File

@@ -5,24 +5,21 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* Default implementation of IDataSource for dibi. * Default implementation of IDataSource for dibi.
* *
* @package dibi
*/ */
class DataSource implements IDataSource class DibiDataSource extends DibiObject implements IDataSource
{ {
use Strict; /** @var DibiConnection */
/** @var Connection */
private $connection; private $connection;
/** @var string */ /** @var string */
private $sql; private $sql;
/** @var Result */ /** @var DibiResult */
private $result; private $result;
/** @var int */ /** @var int */
@@ -32,13 +29,13 @@ class DataSource implements IDataSource
private $totalCount; private $totalCount;
/** @var array */ /** @var array */
private $cols = []; private $cols = array();
/** @var array */ /** @var array */
private $sorting = []; private $sorting = array();
/** @var array */ /** @var array */
private $conds = []; private $conds = array();
/** @var int */ /** @var int */
private $offset; private $offset;
@@ -49,12 +46,12 @@ class DataSource implements IDataSource
/** /**
* @param string SQL command or table or view name, as data source * @param string SQL command or table or view name, as data source
* @param Connection connection * @param DibiConnection connection
*/ */
public function __construct($sql, Connection $connection) public function __construct($sql, DibiConnection $connection)
{ {
if (strpbrk($sql, " \t\r\n") === FALSE) { if (strpbrk($sql, " \t\r\n") === FALSE) {
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name $this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
} else { } else {
$this->sql = '(' . $sql . ') t'; // SQL command $this->sql = '(' . $sql . ') t'; // SQL command
} }
@@ -133,7 +130,7 @@ class DataSource implements IDataSource
/** /**
* Returns the dibi connection. * Returns the dibi connection.
* @return Connection * @return DibiConnection
*/ */
final public function getConnection() final public function getConnection()
{ {
@@ -145,8 +142,8 @@ class DataSource implements IDataSource
/** /**
* Returns (and queries) Result. * Returns (and queries) DibiResult.
* @return Result * @return DibiResult
*/ */
public function getResult() public function getResult()
{ {
@@ -158,7 +155,7 @@ class DataSource implements IDataSource
/** /**
* @return ResultIterator * @return DibiResultIterator
*/ */
public function getIterator() public function getIterator()
{ {
@@ -168,7 +165,7 @@ class DataSource implements IDataSource
/** /**
* Generates, executes SQL query and fetches the single row. * Generates, executes SQL query and fetches the single row.
* @return Row|FALSE array on success, FALSE if no next record * @return DibiRow|FALSE array on success, FALSE if no next record
*/ */
public function fetch() public function fetch()
{ {
@@ -233,8 +230,8 @@ class DataSource implements IDataSource
/** /**
* Returns this data source wrapped in Fluent object. * Returns this data source wrapped in DibiFluent object.
* @return Fluent * @return DibiFluent
*/ */
public function toFluent() public function toFluent()
{ {
@@ -243,8 +240,8 @@ class DataSource implements IDataSource
/** /**
* Returns this data source wrapped in DataSource object. * Returns this data source wrapped in DibiDataSource object.
* @return DataSource * @return DibiDataSource
*/ */
public function toDataSource() public function toDataSource()
{ {
@@ -262,11 +259,11 @@ class DataSource implements IDataSource
return $this->connection->translate(' return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), ' SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, ' FROM %SQL', $this->sql, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : NULL, ' %ex', $this->conds ? array('WHERE %and', $this->conds) : NULL, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : NULL, ' %ex', $this->sorting ? array('ORDER BY %by', $this->sorting) : NULL, '
%ofs %lmt', $this->offset, $this->limit %ofs %lmt', $this->offset, $this->limit
); );
} catch (\Exception $e) { } catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR); trigger_error($e->getMessage(), E_USER_ERROR);
} }
} }

View File

@@ -0,0 +1,702 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
/**
* Reflection metadata class for a database.
*
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/
class DibiDatabaseInfo extends DibiObject
{
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
/** @var array */
private $tables;
public function __construct(IDibiReflector $reflector, $name)
{
$this->reflector = $reflector;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return DibiTableInfo[]
*/
public function getTables()
{
$this->init();
return array_values($this->tables);
}
/**
* @return string[]
*/
public function getTableNames()
{
$this->init();
$res = array();
foreach ($this->tables as $table) {
$res[] = $table->getName();
}
return $res;
}
/**
* @param string
* @return DibiTableInfo
*/
public function getTable($name)
{
$this->init();
$l = strtolower($name);
if (isset($this->tables[$l])) {
return $this->tables[$l];
} else {
throw new DibiException("Database '$this->name' has no table '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasTable($name)
{
$this->init();
return isset($this->tables[strtolower($name)]);
}
/**
* @return void
*/
protected function init()
{
if ($this->tables === NULL) {
$this->tables = array();
foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new DibiTableInfo($this->reflector, $info);
}
}
}
}
/**
* Reflection metadata class for a database table.
*
* @package dibi\reflection
*
* @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
{
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
/** @var bool */
private $view;
/** @var array */
private $columns;
/** @var array */
private $foreignKeys;
/** @var array */
private $indexes;
/** @var DibiIndexInfo */
private $primaryKey;
public function __construct(IDibiReflector $reflector, array $info)
{
$this->reflector = $reflector;
$this->name = $info['name'];
$this->view = !empty($info['view']);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isView()
{
return $this->view;
}
/**
* @return DibiColumnInfo[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @return string[]
*/
public function getColumnNames()
{
$this->initColumns();
$res = array();
foreach ($this->columns as $column) {
$res[] = $column->getName();
}
return $res;
}
/**
* @param string
* @return DibiColumnInfo
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->columns[$l])) {
return $this->columns[$l];
} else {
throw new DibiException("Table '$this->name' has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->columns[strtolower($name)]);
}
/**
* @return DibiForeignKeyInfo[]
*/
public function getForeignKeys()
{
$this->initForeignKeys();
return $this->foreignKeys;
}
/**
* @return DibiIndexInfo[]
*/
public function getIndexes()
{
$this->initIndexes();
return $this->indexes;
}
/**
* @return DibiIndexInfo
*/
public function getPrimaryKey()
{
$this->initIndexes();
return $this->primaryKey;
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = array();
foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->reflector, $info);
}
}
}
/**
* @return void
*/
protected function initIndexes()
{
if ($this->indexes === NULL) {
$this->initColumns();
$this->indexes = array();
foreach ($this->reflector->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)];
}
$this->indexes[strtolower($info['name'])] = new DibiIndexInfo($info);
if (!empty($info['primary'])) {
$this->primaryKey = $this->indexes[strtolower($info['name'])];
}
}
}
}
/**
* @return void
*/
protected function initForeignKeys()
{
throw new DibiNotImplementedException;
}
}
/**
* Reflection metadata class for a result set.
*
* @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[strtolower($info['name'])] = new DibiColumnInfo($reflector, $info);
}
}
}
}
/**
* Reflection metadata class for a table or result set column.
*
* @package dibi\reflection
*
* @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
{
/** @var array */
private static $types;
/** @var IDibiReflector|NULL when created by DibiResultInfo */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
public function __construct(IDibiReflector $reflector = NULL, array $info)
{
$this->reflector = $reflector;
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
public function hasTable()
{
return !empty($this->info['table']);
}
/**
* @return DibiTableInfo
*/
public function getTable()
{
if (empty($this->info['table']) || !$this->reflector) {
throw new DibiException("Table is unknown or not available.");
}
return new DibiTableInfo($this->reflector, array('name' => $this->info['table']));
}
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) && $this->info['table'] != NULL ? $this->info['table'] : NULL; // intentionally ==
}
/**
* @return string
*/
public function getType()
{
return self::getTypeCache()->{$this->info['nativetype']};
}
/**
* @return mixed
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool
*/
public function isAutoIncrement()
{
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL;
}
/**
* @return mixed
*/
public function getDefault()
{
return isset($this->info['default']) ? $this->info['default'] : NULL;
}
/**
* @param string
* @return mixed
*/
public function getVendorInfo($key)
{
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL;
}
/**
* Heuristic type detection.
* @param string
* @return string
* @internal
*/
public static function detectType($type)
{
static $patterns = array(
'^_' => dibi::TEXT, // PostgreSQL arrays
'BYTEA|BLOB|BIN' => dibi::BINARY,
'TEXT|CHAR|POINT|INTERVAL' => dibi::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => dibi::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => dibi::FLOAT,
'^TIME$' => dibi::TIME,
'TIME' => dibi::DATETIME, // DATETIME, TIMESTAMP
'DATE' => dibi::DATE,
'BOOL' => dibi::BOOL,
);
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return dibi::TEXT;
}
/**
* @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.
*
* @package dibi\reflection
* @todo
*
* @property-read string $name
* @property-read array $references
*/
class DibiForeignKeyInfo extends DibiObject
{
/** @var string */
private $name;
/** @var array of array(local, foreign, onDelete, onUpdate) */
private $references;
public function __construct($name, array $references)
{
$this->name = $name;
$this->references = $references;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getReferences()
{
return $this->references;
}
}
/**
* Reflection metadata class for a index or primary key.
*
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/
class DibiIndexInfo extends DibiObject
{
/** @var array (name, columns, [unique], [primary]) */
private $info;
public function __construct(array $info)
{
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return array
*/
public function getColumns()
{
return $this->info['columns'];
}
/**
* @return bool
*/
public function isUnique()
{
return !empty($this->info['unique']);
}
/**
* @return bool
*/
public function isPrimary()
{
return !empty($this->info['primary']);
}
}

View File

@@ -0,0 +1,88 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
/**
* DateTime with serialization and timestamp support for PHP 5.2.
*
* @package dibi
*/
class DibiDateTime extends DateTime
{
public function __construct($time = 'now', DateTimeZone $timezone = NULL)
{
if (is_numeric($time)) {
parent::__construct('@' . $time);
$this->setTimeZone($timezone ? $timezone : new DateTimeZone(date_default_timezone_get()));
} elseif ($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 setTimestamp($timestamp)
{
$zone = PHP_VERSION_ID === 50206 ? new DateTimeZone($this->getTimezone()->getName()) : $this->getTimezone();
$this->__construct('@' . $timestamp);
$this->setTimeZone($zone);
return $this;
}
public function getTimestamp()
{
$ts = $this->format('U');
return is_float($tmp = $ts * 1) ? $ts : $tmp;
}
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
public function __sleep()
{
$zone = $this->getTimezone()->getName();
if ($zone[0] === '+') {
$this->fix = array($this->format('Y-m-d H:i:sP'));
} else {
$this->fix = array($this->format('Y-m-d H:i:s'), $zone);
}
return array('fix');
}
public function __wakeup()
{
if (isset($this->fix[1])) {
$this->__construct($this->fix[0], new DateTimeZone($this->fix[1]));
} else {
$this->__construct($this->fix[0]);
}
unset($this->fix);
}
}

View File

@@ -5,16 +5,14 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* Profiler & logger event. * Profiler & logger event.
*
* @package dibi
*/ */
class Event class DibiEvent
{ {
use Strict;
/** event type */ /** event type */
const CONNECT = 1, const CONNECT = 1,
SELECT = 4, SELECT = 4,
@@ -28,7 +26,7 @@ class Event
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023; ALL = 1023;
/** @var Connection */ /** @var DibiConnection */
public $connection; public $connection;
/** @var int */ /** @var int */
@@ -37,7 +35,7 @@ class Event
/** @var string */ /** @var string */
public $sql; public $sql;
/** @var Result|DriverException|NULL */ /** @var DibiResult|DibiDriverException|NULL */
public $result; public $result;
/** @var float */ /** @var float */
@@ -50,7 +48,7 @@ class Event
public $source; public $source;
public function __construct(Connection $connection, $type, $sql = NULL) public function __construct(DibiConnection $connection, $type, $sql = NULL)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->type = $type; $this->type = $type;
@@ -58,25 +56,25 @@ class Event
$this->time = -microtime(TRUE); $this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) { if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = [ static $types = array(
'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE, 'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE, 'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
]; );
$this->type = $types[strtoupper($matches[1])]; $this->type = $types[strtoupper($matches[1])];
} }
$rc = new \ReflectionClass('dibi'); $rc = new ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR; $dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) { foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) { if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = [$row['file'], (int) $row['line']]; $this->source = array($row['file'], (int) $row['line']);
break; break;
} }
} }
\dibi::$elapsedTime = FALSE; dibi::$elapsedTime = FALSE;
\dibi::$numOfQueries++; dibi::$numOfQueries++;
\dibi::$sql = $sql; dibi::$sql = $sql;
} }
@@ -84,14 +82,14 @@ class Event
{ {
$this->result = $result; $this->result = $result;
try { try {
$this->count = $result instanceof Result ? count($result) : NULL; $this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (Exception $e) { } catch (DibiException $e) {
$this->count = NULL; $this->count = NULL;
} }
$this->time += microtime(TRUE); $this->time += microtime(TRUE);
\dibi::$elapsedTime = $this->time; dibi::$elapsedTime = $this->time;
\dibi::$totalTime += $this->time; dibi::$totalTime += $this->time;
return $this; return $this;
} }

View File

@@ -5,30 +5,27 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* dibi common exception. * dibi common exception.
*
* @package dibi
*/ */
class Exception extends \Exception class DibiException extends Exception
{ {
use Strict; /** @var string */
/** @var string|NULL */
private $sql; private $sql;
/** /**
* Construct a dibi exception. * Construct a dibi exception.
* @param string Message describing the exception * @param string Message describing the exception
* @param mixed * @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); parent::__construct($message, (int) $code);
$this->code = $code;
$this->sql = $sql; $this->sql = $sql;
} }
@@ -55,105 +52,94 @@ class Exception extends \Exception
/** /**
* database server exception. * database server exception.
*
* @package dibi
*/ */
class DriverException extends Exception class DibiDriverException extends DibiException
{ {
/********************* error catching ****************d*g**/
/** @var string */
private static $errorMsg;
/**
* Starts catching potential errors/warnings.
* @return void
*/
public static function tryError()
{
set_error_handler(array(__CLASS__, '_errorHandler'), E_ALL);
self::$errorMsg = NULL;
}
/**
* Returns catched error/warning message.
* @param string catched message
* @return bool
*/
public static function catchError(& $message)
{
restore_error_handler();
$message = self::$errorMsg;
self::$errorMsg = NULL;
return $message !== NULL;
}
/**
* Internal error handler. Do not call directly.
* @internal
*/
public static function _errorHandler($code, $message)
{
restore_error_handler();
if (ini_get('html_errors')) {
$message = strip_tags($message);
$message = html_entity_decode($message);
}
self::$errorMsg = $message;
}
} }
/** /**
* PCRE exception. * PCRE exception.
*
* @package dibi
*/ */
class PcreException extends Exception class DibiPcreException extends Exception
{ {
use Strict;
public function __construct($message = '%msg.') public function __construct($message = '%msg.')
{ {
static $messages = [ static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error', PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted', PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted', PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data', 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 5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
]; );
$code = preg_last_error(); $code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code); parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
} }
} }
class NotImplementedException extends Exception /**
{ * @package dibi
} */
class DibiNotImplementedException extends DibiException
{}
class NotSupportedException extends Exception
{
}
/** /**
* Database procedure exception. * @package dibi
*/ */
class ProcedureException extends Exception class DibiNotSupportedException 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;
}
}
/**
* Base class for all constraint violation related exceptions.
*/
class ConstraintViolationException extends DriverException
{
}
/**
* Exception for a foreign key constraint violation.
*/
class ForeignKeyConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a NOT NULL constraint violation.
*/
class NotNullConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a unique constraint violation.
*/
class UniqueConstraintViolationException extends ConstraintViolationException
{
}

View File

@@ -5,18 +5,14 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Loggers;
use Dibi;
/** /**
* dibi file logger. * dibi file logger.
*
* @package dibi
*/ */
class FileLogger class DibiFileLogger extends DibiObject
{ {
use Dibi\Strict;
/** @var string Name of the file where SQL errors should be logged */ /** @var string Name of the file where SQL errors should be logged */
public $file; public $file;
@@ -27,7 +23,7 @@ class FileLogger
public function __construct($file, $filter = NULL) public function __construct($file, $filter = NULL)
{ {
$this->file = $file; $this->file = $file;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY; $this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
} }
@@ -35,7 +31,7 @@ class FileLogger
* After event notification. * After event notification.
* @return void * @return void
*/ */
public function logEvent(Dibi\Event $event) public function logEvent(DibiEvent $event)
{ {
if (($event->type & $this->filter) === 0) { if (($event->type & $this->filter) === 0) {
return; return;
@@ -47,7 +43,7 @@ class FileLogger
} }
flock($handle, LOCK_EX); flock($handle, LOCK_EX);
if ($event->result instanceof \Exception) { if ($event->result instanceof Exception) {
$message = $event->result->getMessage(); $message = $event->result->getMessage();
if ($code = $event->result->getCode()) { if ($code = $event->result->getCode()) {
$message = "[$code] $message"; $message = "[$code] $message";

View File

@@ -5,26 +5,22 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi\Loggers;
use Dibi;
/** /**
* dibi FirePHP logger. * dibi FirePHP logger.
*
* @package dibi
*/ */
class FirePhpLogger class DibiFirePhpLogger extends DibiObject
{ {
use Dibi\Strict;
/** maximum number of rows */ /** maximum number of rows */
public static $maxQueries = 30; static public $maxQueries = 30;
/** maximum SQL length */ /** maximum SQL length */
public static $maxLength = 1000; static public $maxLength = 1000;
/** size of json stream chunk */ /** size of json stream chunk */
public static $streamChunkSize = 4990; static public $streamChunkSize = 4990;
/** @var int */ /** @var int */
public $filter; public $filter;
@@ -36,7 +32,7 @@ class FirePhpLogger
public $numOfQueries = 0; public $numOfQueries = 0;
/** @var array */ /** @var array */
private static $fireTable = [['Time', 'SQL Statement', 'Rows', 'Connection']]; private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
/** /**
@@ -50,7 +46,7 @@ class FirePhpLogger
public function __construct($filter = NULL) public function __construct($filter = NULL)
{ {
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY; $this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
} }
@@ -58,7 +54,7 @@ class FirePhpLogger
* After event notification. * After event notification.
* @return void * @return void
*/ */
public function logEvent(Dibi\Event $event) public function logEvent(DibiEvent $event)
{ {
if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) { if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) {
return; return;
@@ -71,20 +67,20 @@ class FirePhpLogger
} }
$this->totalTime += $event->time; $this->totalTime += $event->time;
$this->numOfQueries++; $this->numOfQueries++;
self::$fireTable[] = [ self::$fireTable[] = array(
sprintf('%0.3f', $event->time * 1000), sprintf('%0.3f', $event->time * 1000),
strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql,
$event->result instanceof \Exception ? 'ERROR' : (string) $event->count, $event->result instanceof Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'), $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'),
]; );
$payload = json_encode([ $payload = json_encode(array(
[ array(
'Type' => 'TABLE', 'Type' => 'TABLE',
'Label' => 'dibi profiler (' . $this->numOfQueries . ' SQL queries took ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms)', 'Label' => 'dibi profiler (' . $this->numOfQueries . ' SQL queries took ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms)',
], ),
self::$fireTable, self::$fireTable,
]); ));
foreach (str_split($payload, self::$streamChunkSize) as $num => $s) { foreach (str_split($payload, self::$streamChunkSize) as $num => $s) {
$num++; $num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index

View File

@@ -5,46 +5,42 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL! * dibi SQL builder via fluent interfaces. EXPERIMENTAL!
* *
* @method Fluent select(...$field) * @method DibiFluent select(...$field)
* @method Fluent distinct() * @method DibiFluent distinct()
* @method Fluent from($table) * @method DibiFluent from($table)
* @method Fluent where(...$cond) * @method DibiFluent where(...$cond)
* @method Fluent groupBy(...$field) * @method DibiFluent groupBy(...$field)
* @method Fluent having(...$cond) * @method DibiFluent having(...$cond)
* @method Fluent orderBy(...$field) * @method DibiFluent orderBy(...$field)
* @method Fluent limit(int $limit) * @method DibiFluent limit(int $limit)
* @method Fluent offset(int $offset) * @method DibiFluent offset(int $offset)
* @method Fluent join(...$table) * @method DibiFluent join(...$table)
* @method Fluent leftJoin(...$table) * @method DibiFluent leftJoin(...$table)
* @method Fluent innerJoin(...$table) * @method DibiFluent innerJoin(...$table)
* @method Fluent rightJoin(...$table) * @method DibiFluent rightJoin(...$table)
* @method Fluent outerJoin(...$table) * @method DibiFluent outerJoin(...$table)
* @method Fluent on(...$cond) * @method DibiFluent on(...$cond)
* @method Fluent using(...$cond) * @method DibiFluent using(...$cond)
*/ */
class Fluent implements IDataSource class DibiFluent extends DibiObject implements IDataSource
{ {
use Strict;
const REMOVE = FALSE; const REMOVE = FALSE;
/** @var array */ /** @var array */
public static $masks = [ public static $masks = array(
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY', 'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'], 'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'),
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'], 'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'),
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'], 'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT'),
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'], 'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'),
]; );
/** @var array default modifiers for arrays */ /** @var array default modifiers for arrays */
public static $modifiers = [ public static $modifiers = array(
'SELECT' => '%n', 'SELECT' => '%n',
'FROM' => '%n', 'FROM' => '%n',
'IN' => '%in', 'IN' => '%in',
@@ -54,10 +50,10 @@ class Fluent implements IDataSource
'HAVING' => '%and', 'HAVING' => '%and',
'ORDER BY' => '%by', 'ORDER BY' => '%by',
'GROUP BY' => '%by', 'GROUP BY' => '%by',
]; );
/** @var array clauses separators */ /** @var array clauses separators */
public static $separators = [ public static $separators = array(
'SELECT' => ',', 'SELECT' => ',',
'FROM' => ',', 'FROM' => ',',
'WHERE' => 'AND', 'WHERE' => 'AND',
@@ -69,47 +65,47 @@ class Fluent implements IDataSource
'SET' => ',', 'SET' => ',',
'VALUES' => ',', 'VALUES' => ',',
'INTO' => FALSE, 'INTO' => FALSE,
]; );
/** @var array clauses */ /** @var array clauses */
public static $clauseSwitches = [ public static $clauseSwitches = array(
'JOIN' => 'FROM', 'JOIN' => 'FROM',
'INNER JOIN' => 'FROM', 'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM', 'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM', 'RIGHT JOIN' => 'FROM',
]; );
/** @var Connection */ /** @var DibiConnection */
private $connection; private $connection;
/** @var array */ /** @var array */
private $setups = []; private $setups = array();
/** @var string */ /** @var string */
private $command; private $command;
/** @var array */ /** @var array */
private $clauses = []; private $clauses = array();
/** @var array */ /** @var array */
private $flags = []; private $flags = array();
/** @var array */ /** @var array */
private $cursor; private $cursor;
/** @var HashMap normalized clauses */ /** @var DibiHashMap normalized clauses */
private static $normalizer; private static $normalizer;
/** /**
* @param Connection * @param DibiConnection
*/ */
public function __construct(Connection $connection) public function __construct(DibiConnection $connection)
{ {
$this->connection = $connection; $this->connection = $connection;
if (self::$normalizer === NULL) { if (self::$normalizer === NULL) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']); self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
} }
} }
@@ -130,7 +126,7 @@ class Fluent implements IDataSource
$this->clauses = array_fill_keys(self::$masks[$clause], NULL); $this->clauses = array_fill_keys(self::$masks[$clause], NULL);
} }
$this->cursor = & $this->clauses[$clause]; $this->cursor = & $this->clauses[$clause];
$this->cursor = []; $this->cursor = array();
$this->command = $clause; $this->command = $clause;
} }
@@ -144,7 +140,7 @@ class Fluent implements IDataSource
$this->cursor = & $this->clauses[$clause]; $this->cursor = & $this->clauses[$clause];
// TODO: really delete? // TODO: really delete?
if ($args === [self::REMOVE]) { if ($args === array(self::REMOVE)) {
$this->cursor = NULL; $this->cursor = NULL;
return $this; return $this;
} }
@@ -152,7 +148,7 @@ class Fluent implements IDataSource
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) { // means: replace
$this->cursor = []; $this->cursor = array();
} elseif (!empty($this->cursor)) { } elseif (!empty($this->cursor)) {
$this->cursor[] = $sep; $this->cursor[] = $sep;
@@ -161,7 +157,7 @@ class Fluent implements IDataSource
} else { } else {
// append to currect flow // append to currect flow
if ($args === [self::REMOVE]) { if ($args === array(self::REMOVE)) {
return $this; return $this;
} }
@@ -169,7 +165,7 @@ class Fluent implements IDataSource
} }
if ($this->cursor === NULL) { if ($this->cursor === NULL) {
$this->cursor = []; $this->cursor = array();
} }
// special types or argument // special types or argument
@@ -180,21 +176,24 @@ class Fluent implements IDataSource
return $this; return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier } elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = ['%n', $arg]; $args = array('%n', $arg);
} elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array } elseif ($arg instanceof self) {
$args = array('%SQL', $arg);
} elseif (is_array($arg) || $arg instanceof Traversable) { // any array
if (isset(self::$modifiers[$clause])) { if (isset(self::$modifiers[$clause])) {
$args = [self::$modifiers[$clause], $arg]; $args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array } elseif (is_string(key($arg))) { // associative array
$args = ['%a', $arg]; $args = array('%a', $arg);
} }
} // case $arg === FALSE is handled above } // case $arg === FALSE is handled above
} }
foreach ($args as $arg) { foreach ($args as $arg) {
if ($arg instanceof self) { if ($arg instanceof self) {
$arg = new Literal("($arg)"); $arg = "($arg)";
} }
$this->cursor[] = $arg; $this->cursor[] = $arg;
} }
@@ -212,7 +211,7 @@ class Fluent implements IDataSource
{ {
$this->cursor = & $this->clauses[self::$normalizer->$clause]; $this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) { if ($this->cursor === NULL) {
$this->cursor = []; $this->cursor = array();
} }
return $this; return $this;
@@ -272,7 +271,7 @@ class Fluent implements IDataSource
/** /**
* Returns the dibi connection. * Returns the dibi connection.
* @return Connection * @return DibiConnection
*/ */
final public function getConnection() final public function getConnection()
{ {
@@ -281,7 +280,7 @@ class Fluent implements IDataSource
/** /**
* Adds Result setup. * Adds DibiResult setup.
* @param string method * @param string method
* @param mixed args * @param mixed args
* @return self * @return self
@@ -299,16 +298,16 @@ class Fluent implements IDataSource
/** /**
* Generates and executes SQL query. * Generates and executes SQL query.
* @param mixed what to return? * @param mixed what to return?
* @return Result|int result set object (if any) * @return DibiResult|int result set object (if any)
* @throws Exception * @throws DibiException
*/ */
public function execute($return = NULL) public function execute($return = NULL)
{ {
$res = $this->query($this->_export()); $res = $this->query($this->_export());
switch ($return) { switch ($return) {
case \dibi::IDENTIFIER: case dibi::IDENTIFIER:
return $this->connection->getInsertId(); return $this->connection->getInsertId();
case \dibi::AFFECTED_ROWS: case dibi::AFFECTED_ROWS:
return $this->connection->getAffectedRows(); return $this->connection->getAffectedRows();
default: default:
return $res; return $res;
@@ -318,12 +317,12 @@ class Fluent implements IDataSource
/** /**
* Generates, executes SQL query and fetches the single row. * Generates, executes SQL query and fetches the single row.
* @return Row|FALSE array on success, FALSE if no next record * @return DibiRow|FALSE array on success, FALSE if no next record
*/ */
public function fetch() public function fetch()
{ {
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) { if ($this->command === 'SELECT' && !$this->clauses['LIMIT'] && !$this->clauses['OFFSET']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch(); return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
} else { } else {
return $this->query($this->_export())->fetch(); return $this->query($this->_export())->fetch();
} }
@@ -336,8 +335,8 @@ class Fluent implements IDataSource
*/ */
public function fetchSingle() public function fetchSingle()
{ {
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) { if ($this->command === 'SELECT' && !$this->clauses['LIMIT'] && !$this->clauses['OFFSET']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle(); return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
} else { } else {
return $this->query($this->_export())->fetchSingle(); return $this->query($this->_export())->fetchSingle();
} }
@@ -352,7 +351,7 @@ class Fluent implements IDataSource
*/ */
public function fetchAll($offset = NULL, $limit = NULL) public function fetchAll($offset = NULL, $limit = NULL)
{ {
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll(); return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
} }
@@ -383,11 +382,11 @@ class Fluent implements IDataSource
* Required by the IteratorAggregate interface. * Required by the IteratorAggregate interface.
* @param int offset * @param int offset
* @param int limit * @param int limit
* @return ResultIterator * @return DibiResultIterator
*/ */
public function getIterator($offset = NULL, $limit = NULL) public function getIterator($offset = NULL, $limit = NULL)
{ {
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator(); return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
} }
@@ -407,20 +406,20 @@ class Fluent implements IDataSource
*/ */
public function count() public function count()
{ {
return (int) $this->query([ return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]', 'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle(); ))->fetchSingle();
} }
/** /**
* @return Result * @return DibiResult
*/ */
private function query($args) private function query($args)
{ {
$res = $this->connection->query($args); $res = $this->connection->query($args);
foreach ($this->setups as $setup) { foreach ($this->setups as $setup) {
call_user_func_array([$res, array_shift($setup)], $setup); call_user_func_array(array($res, array_shift($setup)), $setup);
} }
return $res; return $res;
} }
@@ -430,11 +429,11 @@ class Fluent implements IDataSource
/** /**
* @return DataSource * @return DibiDataSource
*/ */
public function toDataSource() public function toDataSource()
{ {
return new DataSource($this->connection->translate($this->_export()), $this->connection); return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
} }
@@ -446,32 +445,28 @@ class Fluent implements IDataSource
{ {
try { try {
return $this->connection->translate($this->_export()); return $this->connection->translate($this->_export());
} catch (\Exception $e) { } catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR); trigger_error($e->getMessage(), E_USER_ERROR);
} }
} }
/** /**
* Generates parameters for Translator. * Generates parameters for DibiTranslator.
* @param string clause name * @param string clause name
* @return array * @return array
*/ */
protected function _export($clause = NULL, $args = []) protected function _export($clause = NULL, $args = array())
{ {
if ($clause === NULL) { if ($clause === NULL) {
$data = $this->clauses; $data = $this->clauses;
if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) {
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args);
unset($data['LIMIT'], $data['OFFSET']);
}
} else { } else {
$clause = self::$normalizer->$clause; $clause = self::$normalizer->$clause;
if (array_key_exists($clause, $this->clauses)) { if (array_key_exists($clause, $this->clauses)) {
$data = [$clause => $this->clauses[$clause]]; $data = array($clause => $this->clauses[$clause]);
} else { } else {
return []; return array();
} }
} }

View File

@@ -5,26 +5,30 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* Lazy cached storage. * Lazy cached storage.
*
* @package dibi
* @internal * @internal
*/ */
abstract class HashMapBase abstract class DibiHashMapBase
{ {
private $callback; private $callback;
public function __construct(callable $callback) public function __construct($callback)
{ {
$this->callback = $callback; $this->setCallback($callback);
} }
public function setCallback(callable $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; $this->callback = $callback;
} }
@@ -42,7 +46,7 @@ abstract class HashMapBase
* *
* @internal * @internal
*/ */
final class HashMap extends HashMapBase final class DibiHashMap extends DibiHashMapBase
{ {
public function __set($nm, $val) public function __set($nm, $val)

View File

@@ -5,16 +5,14 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* SQL literal value. * SQL literal value.
*
* @package dibi
*/ */
class Literal class DibiLiteral extends DibiObject
{ {
use Strict;
/** @var string */ /** @var string */
private $value; private $value;

307
dibi/libs/DibiObject.php Normal file
View File

@@ -0,0 +1,307 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
/**
* DibiObject is the ultimate ancestor of all instantiable classes.
*
* DibiObject is copy of Nette\Object from Nette Framework (https://nette.org).
*
* It defines some handful methods and enhances object core of PHP:
* - access to undeclared members throws exceptions
* - support for conventional properties with getters and setters
* - support for event raising functionality
* - ability to add new methods to class (extension methods)
*
* Properties is a syntactic sugar which allows access public getter and setter
* methods as normal object variables. A property is defined by a getter method
* and optional setter method (no setter method means read-only property).
* <code>
* $val = $obj->label; // equivalent to $val = $obj->getLabel();
* $obj->label = 'Nette'; // equivalent to $obj->setLabel('Nette');
* </code>
* Property names are case-sensitive, and they are written in the camelCaps
* or PascalCaps.
*
* Event functionality is provided by declaration of property named 'on{Something}'
* Multiple handlers are allowed.
* <code>
* public $onClick; // declaration in class
* $this->onClick[] = 'callback'; // attaching event handler
* if (!empty($this->onClick)) ... // are there any handlers?
* $this->onClick($sender, $arg); // raises the event with arguments
* </code>
*
* Adding method to class (i.e. to all instances) works similar to JavaScript
* prototype property. The syntax for adding a new method is:
* <code>
* MyClass::extensionMethod('newMethod', function (MyClass $obj, $arg, ...) { ... });
* $obj = new MyClass;
* $obj->newMethod($x);
* </code>
*
* @package dibi
*/
abstract class DibiObject
{
/** @var array (method => array(type => callback)) */
private static $extMethods;
/**
* Returns the name of the class of this object.
* @return string
*/
final public /*static*/ function getClass()
{
return /*get_called_class()*/ /**/get_class($this)/**/;
}
/**
* Access to reflection.
* @return \ReflectionObject
*/
final public function getReflection()
{
return new ReflectionObject($this);
}
/**
* Call to undefined method.
* @param string method name
* @param array arguments
* @return mixed
* @throws \LogicException
*/
public function __call($name, $args)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Call to class '$class' method without name.");
}
// event functionality
if (preg_match('#^on[A-Z]#', $name)) {
$rp = new ReflectionProperty($class, $name);
if ($rp->isPublic() && !$rp->isStatic()) {
$list = $this->$name;
if (is_array($list) || $list instanceof Traversable) {
foreach ($list as $handler) {
/**/if (is_object($handler)) {
call_user_func_array(array($handler, '__invoke'), $args);
} else /**/{
call_user_func_array($handler, $args);
}
}
}
return NULL;
}
}
// extension methods
if ($cb = self::extensionMethod("$class::$name")) {
array_unshift($args, $this);
return call_user_func_array($cb, $args);
}
throw new LogicException("Call to undefined method $class::$name().");
}
/**
* Call to undefined static method.
* @param string method name (in lower case!)
* @param array arguments
* @return mixed
* @throws \LogicException
*/
public static function __callStatic($name, $args)
{
$class = get_called_class();
throw new LogicException("Call to undefined static method $class::$name().");
}
/**
* Adding method to class.
* @param string method name
* @param mixed callback or closure
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (self::$extMethods === NULL || $name === NULL) { // for backwards compatibility
$list = get_defined_functions();
foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce);
if (count($pair) === 2) {
self::$extMethods[$pair[1]][$pair[0]] = $fce;
self::$extMethods[$pair[1]][''] = NULL;
}
}
if ($name === NULL) {
return NULL;
}
}
$name = strtolower($name);
$a = strrpos($name, ':'); // search ::
if ($a === FALSE) {
$class = strtolower(get_called_class());
$l = & self::$extMethods[$name];
} else {
$class = substr($name, 0, $a - 1);
$l = & self::$extMethods[substr($name, $a + 1)];
}
if ($callback !== NULL) { // works as setter
$l[$class] = $callback;
$l[''] = NULL;
return NULL;
}
// works as getter
if (empty($l)) {
return FALSE;
} elseif (isset($l[''][$class])) { // cached value
return $l[''][$class];
}
$cl = $class;
do {
$cl = strtolower($cl);
if (isset($l[$cl])) {
return $l[''][$class] = $l[$cl];
}
} while (($cl = get_parent_class($cl)) !== FALSE);
foreach (class_implements($class) as $cl) {
$cl = strtolower($cl);
if (isset($l[$cl])) {
return $l[''][$class] = $l[$cl];
}
}
return $l[''][$class] = FALSE;
}
/**
* Returns property value. Do not call directly.
* @param string property name
* @return mixed property value
* @throws \LogicException if the property is not defined.
*/
public function & __get($name)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Cannot read a class '$class' property without name.");
}
// property getter support
$uname = ucfirst($name);
$m = 'get' . $uname;
if (self::hasAccessor($class, $m)) {
// ampersands:
// - 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';
$val = $this->$m();
return $val;
}
$m = 'is' . $uname;
if (self::hasAccessor($class, $m)) {
$val = $this->$m();
return $val;
}
throw new LogicException("Cannot read an undeclared property $class::\$$name.");
}
/**
* Sets value of a property. Do not call directly.
* @param string property name
* @param mixed property value
* @return void
* @throws \LogicException if the property is not defined or is read-only
*/
public function __set($name, $value)
{
$class = get_class($this);
if ($name === '') {
throw new LogicException("Cannot assign to a class '$class' property without name.");
}
// property setter support
$uname = ucfirst($name);
if (self::hasAccessor($class, 'get' . $uname) || self::hasAccessor($class, 'is' . $uname)) {
$m = 'set' . $name;
if (self::hasAccessor($class, $m)) {
$this->$m($value);
return;
} else {
throw new LogicException("Cannot assign to a read-only property $class::\$$name.");
}
}
throw new LogicException("Cannot assign to an undeclared property $class::\$$name.");
}
/**
* Is property defined?
* @param string property name
* @return bool
*/
public function __isset($name)
{
return $name !== '' && self::hasAccessor(get_class($this), 'get' . ucfirst($name));
}
/**
* Access to undeclared property.
* @param string property name
* @return void
* @throws \LogicException
*/
public function __unset($name)
{
$class = get_class($this);
throw new LogicException("Cannot unset the property $class::\$$name.");
}
/**
* Has property an accessor?
* @param string class name
* @param string method name
* @return bool
*/
private static function hasAccessor($c, $m)
{
static $cache;
if (!isset($cache[$c])) {
// get_class_methods returns private, protected and public methods of Object (doesn't matter)
// and ONLY PUBLIC methods of descendants (perfect!)
// but returns static methods too (nothing doing...)
// and is much faster than reflection
// (works good since 5.0.4)
$cache[$c] = array_flip(get_class_methods($c));
}
return isset($cache[$c][$m]);
}
}

View File

@@ -5,8 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* dibi result set. * dibi result set.
@@ -18,42 +16,42 @@ namespace Dibi;
* $value = $result->fetchSingle(); * $value = $result->fetchSingle();
* $table = $result->fetchAll(); * $table = $result->fetchAll();
* $pairs = $result->fetchPairs(); * $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('col1'); * $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('col1[]col2->col3'); * $assoc = $result->fetchAssoc('active,#,id');
* *
* unset($result); * unset($result);
* </code> * </code>
* *
* @package dibi
*
* @property-read int $rowCount * @property-read int $rowCount
*/ */
class Result implements IDataSource class DibiResult extends DibiObject implements IDataSource
{ {
use Strict; /** @var array IDibiResultDriver */
/** @var array ResultDriver */
private $driver; private $driver;
/** @var array Translate table */ /** @var array Translate table */
private $types = []; private $types = array();
/** @var Reflection\Result */ /** @var DibiResultInfo */
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 string returned object class */
private $rowClass = 'Dibi\Row'; private $rowClass = 'DibiRow';
/** @var callable returned object factory*/ /** @var Callback returned object factory*/
private $rowFactory; private $rowFactory;
/** @var array format */ /** @var array format */
private $formats = []; private $formats = array();
/** /**
* @param ResultDriver * @param IDibiResultDriver
*/ */
public function __construct($driver) public function __construct($driver)
{ {
@@ -67,7 +65,6 @@ class Result implements IDataSource
*/ */
final public function getResource() final public function getResource()
{ {
trigger_error(__METHOD__ . '() is deprecated, use getResultDriver()->getResultResource().', E_USER_DEPRECATED);
return $this->getResultDriver()->getResultResource(); return $this->getResultDriver()->getResultResource();
} }
@@ -87,13 +84,13 @@ class Result implements IDataSource
/** /**
* Safe access to property $driver. * Safe access to property $driver.
* @return ResultDriver * @return IDibiResultDriver
* @throws \RuntimeException * @throws RuntimeException
*/ */
final public function getResultDriver() final public function getResultDriver()
{ {
if ($this->driver === NULL) { if ($this->driver === NULL) {
throw new \RuntimeException('Result-set was released from memory.'); throw new RuntimeException('Result-set was released from memory.');
} }
return $this->driver; return $this->driver;
@@ -107,7 +104,7 @@ class Result implements IDataSource
* 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 bool TRUE on success, FALSE if unable to seek to specified record * @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Exception * @throws DibiException
*/ */
final public function seek($row) final public function seek($row)
{ {
@@ -137,11 +134,11 @@ class Result implements IDataSource
/** /**
* Required by the IteratorAggregate interface. * Required by the IteratorAggregate interface.
* @return ResultIterator * @return DibiResultIterator
*/ */
final public function getIterator() final public function getIterator()
{ {
return new ResultIterator($this); return new DibiResultIterator($this);
} }
@@ -149,7 +146,7 @@ class Result implements IDataSource
/** /**
* Set fetched object class. This class should extend the Row class. * Set fetched object class. This class should extend the DibiRow class.
* @param string * @param string
* @return self * @return self
*/ */
@@ -171,10 +168,11 @@ class Result implements IDataSource
/** /**
* Set a factory to create fetched object instances. These should extend the Row class. * Set a factory to create fetched object instances. These should extend the DibiRow class.
* @param callback
* @return self * @return self
*/ */
public function setRowFactory(callable $callback) public function setRowFactory($callback)
{ {
$this->rowFactory = $callback; $this->rowFactory = $callback;
return $this; return $this;
@@ -184,7 +182,7 @@ class Result implements IDataSource
/** /**
* 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
* @return Row|FALSE array on success, FALSE if no next record * @return DibiRow|FALSE array on success, FALSE if no next record
*/ */
final public function fetch() final public function fetch()
{ {
@@ -223,7 +221,7 @@ class Result implements IDataSource
* Fetches all records from table. * Fetches all records from table.
* @param int offset * @param int offset
* @param int limit * @param int limit
* @return Row[] * @return DibiRow[]
*/ */
final public function fetchAll($offset = NULL, $limit = NULL) final public function fetchAll($offset = NULL, $limit = NULL)
{ {
@@ -231,10 +229,10 @@ class Result implements IDataSource
$this->seek((int) $offset); $this->seek((int) $offset);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) {
return []; // empty result set return array(); // empty result set
} }
$data = []; $data = array();
do { do {
if ($limit === 0) { if ($limit === 0) {
break; break;
@@ -256,7 +254,7 @@ class Result implements IDataSource
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4 * builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor * @param string associative descriptor
* @return array * @return array
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
final public function fetchAssoc($assoc) final public function fetchAssoc($assoc)
{ {
@@ -267,7 +265,7 @@ class Result implements IDataSource
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) {
return []; // empty result set return array(); // empty result set
} }
$data = NULL; $data = NULL;
@@ -277,7 +275,7 @@ class Result implements IDataSource
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 !== '->' && $as !== '|' && !property_exists($row, $as)) {
throw new \InvalidArgumentException("Unknown column '$as' in associative descriptor."); throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
} }
} }
@@ -335,7 +333,7 @@ class Result implements IDataSource
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) {
return []; // empty result set return array(); // empty result set
} }
$data = NULL; $data = NULL;
@@ -405,21 +403,21 @@ class Result implements IDataSource
* @param string associative key * @param string associative key
* @param string value * @param string value
* @return array * @return array
* @throws \InvalidArgumentException * @throws InvalidArgumentException
*/ */
final public function fetchPairs($key = NULL, $value = NULL) final public function fetchPairs($key = NULL, $value = NULL)
{ {
$this->seek(0); $this->seek(0);
$row = $this->fetch(); $row = $this->fetch();
if (!$row) { if (!$row) {
return []; // empty result set return array(); // empty result set
} }
$data = []; $data = array();
if ($value === NULL) { if ($value === NULL) {
if ($key !== NULL) { if ($key !== NULL) {
throw new \InvalidArgumentException('Either none or both columns must be specified.'); throw new InvalidArgumentException('Either none or both columns must be specified.');
} }
// autodetect // autodetect
@@ -436,7 +434,7 @@ class Result implements IDataSource
} else { } else {
if (!property_exists($row, $value)) { if (!property_exists($row, $value)) {
throw new \InvalidArgumentException("Unknown value column '$value'."); throw new InvalidArgumentException("Unknown value column '$value'.");
} }
if ($key === NULL) { // indexed-array if ($key === NULL) { // indexed-array
@@ -447,7 +445,7 @@ class Result implements IDataSource
} }
if (!property_exists($row, $key)) { if (!property_exists($row, $key)) {
throw new \InvalidArgumentException("Unknown key column '$key'."); throw new InvalidArgumentException("Unknown key column '$key'.");
} }
} }
@@ -468,12 +466,12 @@ class Result implements IDataSource
*/ */
private function detectTypes() private function detectTypes()
{ {
$cache = Helpers::getTypeCache(); $cache = DibiColumnInfo::getTypeCache();
try { try {
foreach ($this->getResultDriver()->getResultColumns() as $col) { foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = isset($col['type']) ? $col['type'] : $cache->{$col['nativetype']}; $this->types[$col['name']] = $cache->{$col['nativetype']};
} }
} catch (NotSupportedException $e) { } catch (DibiNotSupportedException $e) {
} }
} }
@@ -490,15 +488,12 @@ class Result implements IDataSource
continue; continue;
} }
$value = $row[$key]; $value = $row[$key];
if ($type === Type::TEXT) { if ($type === dibi::TEXT) {
$row[$key] = (string) $value;
} elseif ($type === Type::INTEGER) { } elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) $row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === Type::FLOAT) { } elseif ($type === dibi::FLOAT) {
$value = ltrim($value, '0'); $value = ltrim($value, '0');
$p = strpos($value, '.'); $p = strpos($value, '.');
if ($p !== FALSE) { if ($p !== FALSE) {
@@ -511,24 +506,17 @@ class Result implements IDataSource
? $float ? $float
: $value; : $value;
} elseif ($type === Type::BOOL) { } elseif ($type === dibi::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F'; $row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) { } elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', NULL, FALSE, '0000-00-00', ... if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', NULL, FALSE, '0000-00-00', ...
$value = new DateTime($value); $value = new DibiDateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]); $row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
} else {
$row[$key] = NULL;
} }
} elseif ($type === Type::TIME_INTERVAL) { } elseif ($type === dibi::BINARY) {
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m); $row[$key] = $this->getResultDriver()->unescape($value, $type);
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
$row[$key]->invert = (int) (bool) $m[1];
} elseif ($type === Type::BINARY) {
$row[$key] = $this->getResultDriver()->unescapeBinary($value);
} }
} }
} }
@@ -537,7 +525,7 @@ class Result implements IDataSource
/** /**
* Define column type. * Define column type.
* @param string column * @param string column
* @param string type (use constant Type::*) * @param string type (use constant Dibi::*)
* @return self * @return self
*/ */
final public function setType($col, $type) final public function setType($col, $type)
@@ -559,7 +547,7 @@ class Result implements IDataSource
/** /**
* Sets data format. * Sets data format.
* @param string type (use constant Type::*) * @param string type (use constant Dibi::*)
* @param string format * @param string format
* @return self * @return self
*/ */
@@ -585,19 +573,19 @@ class Result implements IDataSource
/** /**
* Returns a meta information about the current result set. * Returns a meta information about the current result set.
* @return Reflection\Result * @return DibiResultInfo
*/ */
public function getInfo() public function getInfo()
{ {
if ($this->meta === NULL) { if ($this->meta === NULL) {
$this->meta = new Reflection\Result($this->getResultDriver()); $this->meta = new DibiResultInfo($this->getResultDriver());
} }
return $this->meta; return $this->meta;
} }
/** /**
* @return Reflection\Column[] * @deprecated
*/ */
final public function getColumns() final public function getColumns()
{ {
@@ -614,7 +602,66 @@ class Result implements IDataSource
*/ */
final public function dump() final public function dump()
{ {
echo Helpers::dump($this); $i = 0;
$this->seek(0);
if (PHP_SAPI === 'cli') {
$hasColors = (substr(getenv('TERM'), 0, 5) === 'xterm');
$maxLen = 0;
while ($row = $this->fetch()) {
if ($i === 0) {
foreach ($row as $col => $foo) {
$len = mb_strlen($col);
$maxLen = max($len, $maxLen);
}
}
if ($hasColors) {
echo "\033[1;37m#row: $i\033[0m\n";
} else {
echo "#row: $i\n";
}
foreach ($row as $col => $val) {
$spaces = $maxLen - mb_strlen($col) + 2;
echo "$col" . str_repeat(' ', $spaces) . "$val\n";
}
echo "\n";
$i++;
}
if ($i === 0) {
echo "empty result set\n";
}
echo "\n";
} else {
while ($row = $this->fetch()) {
if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
}
echo "\t</tr>\n</thead>\n<tbody>\n";
}
echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) {
//if (is_object($col)) $col = $col->__toString();
echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
}
echo "\t</tr>\n";
$i++;
}
if ($i === 0) {
echo '<p><em>empty result set</em></p>';
} else {
echo "</tbody>\n</table>\n";
}
}
} }
} }

View File

@@ -5,13 +5,11 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* External result set iterator. * External result set iterator.
* *
* This can be returned by Result::getIterator() method or using foreach * This can be returned by DibiResult::getIterator() method or using foreach
* <code> * <code>
* $result = dibi::query('SELECT * FROM table'); * $result = dibi::query('SELECT * FROM table');
* foreach ($result as $row) { * foreach ($result as $row) {
@@ -19,12 +17,12 @@ namespace Dibi;
* } * }
* unset($result); * unset($result);
* </code> * </code>
*
* @package dibi
*/ */
class ResultIterator implements \Iterator, \Countable class DibiResultIterator implements Iterator, Countable
{ {
use Strict; /** @var DibiResult */
/** @var Result */
private $result; private $result;
/** @var int */ /** @var int */
@@ -35,9 +33,9 @@ class ResultIterator implements \Iterator, \Countable
/** /**
* @param Result * @param DibiResult
*/ */
public function __construct(Result $result) public function __construct(DibiResult $result)
{ {
$this->result = $result; $this->result = $result;
} }

View File

@@ -5,13 +5,13 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* Result set single row. * Result set single row.
*
* @package dibi
*/ */
class Row implements \ArrayAccess, \IteratorAggregate, \Countable class DibiRow implements ArrayAccess, IteratorAggregate, Countable
{ {
public function __construct($arr) public function __construct($arr)
@@ -32,28 +32,21 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
* Converts value to DateTime object. * Converts value to DateTime object.
* @param string key * @param string key
* @param string format * @param string format
* @return \DateTime * @return DateTime
*/ */
public function asDateTime($key, $format = NULL) public function asDateTime($key, $format = NULL)
{ {
$time = $this[$key]; $time = $this[$key];
if (!$time instanceof DateTime) { if (!$time instanceof DibiDateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ... if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
return NULL; return NULL;
} }
$time = new DateTime($time); $time = new DibiDateTime($time);
} }
return $format === NULL ? $time : $time->format($format); return $format === NULL ? $time : $time->format($format);
} }
public function __get($key)
{
$hint = Helpers::getSuggestion(array_keys((array) $this), $key);
trigger_error("Attempt to read missing column '$key'" . ($hint ? ", did you mean '$hint'?" : '.'), E_USER_NOTICE);
}
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/ /********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
@@ -65,7 +58,7 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
final public function getIterator() final public function getIterator()
{ {
return new \ArrayIterator($this); return new ArrayIterator($this);
} }

View File

@@ -5,39 +5,37 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* dibi SQL translator. * dibi SQL translator.
*
* @package dibi
*/ */
final class Translator final class DibiTranslator extends DibiObject
{ {
use Strict; /** @var DibiConnection */
/** @var Connection */
private $connection; private $connection;
/** @var Driver */ /** @var IDibiDriver */
private $driver; private $driver;
/** @var int */ /** @var int */
private $cursor = 0; private $cursor;
/** @var array */ /** @var array */
private $args; private $args;
/** @var string[] */ /** @var bool */
private $errors; private $hasError;
/** @var bool */ /** @var bool */
private $comment = FALSE; private $comment;
/** @var int */ /** @var int */
private $ifLevel = 0; private $ifLevel;
/** @var int */ /** @var int */
private $ifLevelStart = 0; private $ifLevelStart;
/** @var int */ /** @var int */
private $limit; private $limit;
@@ -45,39 +43,51 @@ final class Translator
/** @var int */ /** @var int */
private $offset; private $offset;
/** @var HashMap */ /** @var DibiHashMap */
private $identifiers; private $identifiers;
public function __construct(Connection $connection) public function __construct(DibiConnection $connection)
{ {
$this->connection = $connection; $this->connection = $connection;
$this->driver = $connection->getDriver(); $this->identifiers = new DibiHashMap(array($this, 'delimite'));
$this->identifiers = new HashMap([$this, 'delimite']);
} }
/** /**
* Generates SQL. Can be called only once. * Generates SQL.
* @param array * @param array
* @return string * @return string
* @throws Exception * @throws DibiException
*/ */
public function translate(array $args) public function translate(array $args)
{ {
if (!$this->driver) {
$this->driver = $this->connection->getDriver();
}
$args = array_values($args); $args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]); $args = array_values($args[0]);
} }
$this->args = $args; $this->args = $args;
$this->limit = -1;
$this->offset = 0;
$this->hasError = FALSE;
$commandIns = NULL; $commandIns = NULL;
$lastArr = NULL; $lastArr = NULL;
// shortcuts
$cursor = & $this->cursor; $cursor = & $this->cursor;
$cursor = 0;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
$comment = & $this->comment; $comment = & $this->comment;
$comment = FALSE;
// iterate // iterate
$sql = []; $sql = array();
while ($cursor < count($this->args)) { while ($cursor < count($this->args)) {
$arg = $this->args[$cursor]; $arg = $this->args[$cursor];
$cursor++; $cursor++;
@@ -106,11 +116,11 @@ final class Translator
)/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('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
[$this, 'cb'], array($this, 'cb'),
substr($arg, $toSkip) substr($arg, $toSkip)
); );
if (preg_last_error()) { if (preg_last_error()) {
throw new PcreException; throw new DibiPcreException;
} }
} }
continue; continue;
@@ -121,24 +131,26 @@ final class Translator
continue; continue;
} }
if ($arg instanceof \Traversable) { if ($arg instanceof Traversable) {
$arg = iterator_to_array($arg); $arg = iterator_to_array($arg);
} }
if (is_array($arg) && is_string(key($arg))) { if (is_array($arg)) {
// associative array -> autoselect between SET or VALUES & LIST if (is_string(key($arg))) {
if ($commandIns === NULL) { // associative array -> autoselect between SET or VALUES & LIST
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6)); if ($commandIns === NULL) {
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC'; $commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a'); $commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
} else { $sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
if ($lastArr === $cursor - 1) { } else {
$sql[] = ','; if ($lastArr === $cursor - 1) {
$sql[] = ',';
}
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
} }
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a'); $lastArr = $cursor;
continue;
} }
$lastArr = $cursor;
continue;
} }
// default processing // default processing
@@ -152,12 +164,12 @@ final class Translator
$sql = implode(' ', $sql); $sql = implode(' ', $sql);
if ($this->errors) { if ($this->hasError) {
throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql); throw new DibiException('SQL translate error', 0, $sql);
} }
// apply limit // apply limit
if ($this->limit !== NULL || $this->offset !== NULL) { if ($this->limit > -1 || $this->offset > 0) {
$this->driver->applyLimit($sql, $this->limit, $this->offset); $this->driver->applyLimit($sql, $this->limit, $this->offset);
} }
@@ -177,13 +189,17 @@ final class Translator
return '...'; return '...';
} }
if (!$this->driver) {
$this->driver = $this->connection->getDriver();
}
// array processing (with or without modifier) // array processing (with or without modifier)
if ($value instanceof \Traversable) { if ($value instanceof Traversable) {
$value = iterator_to_array($value); $value = iterator_to_array($value);
} }
if (is_array($value)) { if (is_array($value)) {
$vx = $kx = []; $vx = $kx = array();
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 ...
@@ -265,13 +281,15 @@ final class Translator
if (is_array($v)) { if (is_array($v)) {
if (isset($proto)) { if (isset($proto)) {
if ($proto !== array_keys($v)) { if ($proto !== array_keys($v)) {
return $this->errors[] = '**Multi-insert array "' . $k . '" is different**'; $this->hasError = TRUE;
return '**Multi-insert array "' . $k . '" is different.**';
} }
} else { } else {
$proto = array_keys($v); $proto = array_keys($v);
} }
} else { } else {
return $this->errors[] = '**Unexpected type ' . (is_object($v) ? get_class($v) : gettype($v)) . '**'; $this->hasError = TRUE;
return '**Unexpected type ' . gettype($v) . '**';
} }
$pair = explode('%', $k, 2); // split into identifier & modifier $pair = explode('%', $k, 2); // split into identifier & modifier
@@ -300,7 +318,8 @@ final class Translator
case 'ex': case 'ex':
case 'sql': case 'sql':
return call_user_func_array([$this->connection, 'translate'], $value); $translator = new self($this->connection);
return $translator->translate($value);
default: // value, value, value - all with the same modifier default: // value, value, value - all with the same modifier
foreach ($value as $v) { foreach ($value as $v) {
@@ -313,36 +332,23 @@ final class Translator
// with modifier procession // with modifier procession
if ($modifier) { if ($modifier) {
if ($value !== NULL && !is_scalar($value)) { // array is already processed if ($value !== NULL && !is_scalar($value) && !$value instanceof DateTime && !$value instanceof DateTimeInterface) { // array is already processed
if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) { $this->hasError = TRUE;
$modifier = 'SQL'; return '**Unexpected type ' . gettype($value) . '**';
} elseif (($value instanceof \DateTime || $value instanceof \DateTimeInterface) && ($modifier === 'd' || $modifier === 't')) {
// continue
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
}
} }
switch ($modifier) { switch ($modifier) {
case 's': // string case 's': // string
return $value === NULL ? 'NULL' : $this->driver->escapeText($value);
case 'bin':// binary case 'bin':// binary
return $value === NULL ? 'NULL' : $this->driver->escapeBinary($value);
case 'b': // boolean case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escapeBool($value); return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
case 'sN': // string or NULL case 'sN': // string or NULL
case 'sn': case 'sn':
return $value == '' ? 'NULL' : $this->driver->escapeText($value); // notice two equal signs return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
case 'in': // deprecated
trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED);
// intentionally break omitted
case 'iN': // signed int or NULL case 'iN': // signed int or NULL
case 'in': // deprecated
if ($value == '') { if ($value == '') {
$value = NULL; $value = NULL;
} }
@@ -375,7 +381,13 @@ final class Translator
if ($value === NULL) { if ($value === NULL) {
return 'NULL'; return 'NULL';
} else { } else {
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value); if (is_numeric($value)) {
$value = (int) $value; // timestamp
} elseif (is_string($value)) {
$value = new DateTime($value);
}
return $this->driver->escape($value, $modifier);
} }
case 'by': case 'by':
@@ -391,11 +403,11 @@ final class Translator
$value = substr($value, 0, $toSkip) $value = substr($value, 0, $toSkip)
. preg_replace_callback( . preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s', '/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
[$this, 'cb'], array($this, 'cb'),
substr($value, $toSkip) substr($value, $toSkip)
); );
if (preg_last_error()) { if (preg_last_error()) {
throw new PcreException; throw new DibiPcreException;
} }
} }
return $value; return $value;
@@ -417,18 +429,19 @@ final class Translator
case 'a': case 'a':
case 'l': case 'l':
case 'v': case 'v':
$type = gettype($value); $this->hasError = TRUE;
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**"; return '**Unexpected type ' . gettype($value) . '**';
default: default:
return $this->errors[] = "**Unknown or unexpected modifier %$modifier**"; $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->escapeText($value); return $this->driver->escape($value, dibi::TEXT);
} elseif (is_int($value)) { } elseif (is_int($value)) {
return (string) $value; return (string) $value;
@@ -437,20 +450,20 @@ final class Translator
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
} elseif (is_bool($value)) { } elseif (is_bool($value)) {
return $this->driver->escapeBool($value); return $this->driver->escape($value, dibi::BOOL);
} elseif ($value === NULL) { } elseif ($value === NULL) {
return 'NULL'; return 'NULL';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { } elseif ($value instanceof DateTime || $value instanceof DateTimeInterface) {
return $this->driver->escapeDateTime($value); return $this->driver->escape($value, dibi::DATETIME);
} elseif ($value instanceof Literal) { } elseif ($value instanceof DibiLiteral) {
return (string) $value; return (string) $value;
} else { } else {
$type = is_object($value) ? get_class($value) : gettype($value); $this->hasError = TRUE;
return $this->errors[] = "**Unexpected $type**"; return '**Unexpected ' . gettype($value) . '**';
} }
} }
@@ -479,7 +492,8 @@ final class Translator
$cursor = & $this->cursor; $cursor = & $this->cursor;
if ($cursor >= count($this->args)) { if ($cursor >= count($this->args)) {
return $this->errors[] = '**Extra placeholder**'; $this->hasError = TRUE;
return '**Extra placeholder**';
} }
$cursor++; $cursor++;
@@ -491,7 +505,8 @@ final class Translator
$cursor = & $this->cursor; $cursor = & $this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') { if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
return $this->errors[] = "**Extra modifier %$mod**"; $this->hasError = TRUE;
return "**Extra modifier %$mod**";
} }
if ($mod === 'if') { if ($mod === 'if') {
@@ -567,13 +582,14 @@ final class Translator
return $this->identifiers->{$matches[2]}; return $this->identifiers->{$matches[2]};
} elseif ($matches[3]) { // SQL strings: '...' } elseif ($matches[3]) { // SQL strings: '...'
return $this->driver->escapeText(str_replace("''", "'", $matches[4])); return $this->driver->escape(str_replace("''", "'", $matches[4]), dibi::TEXT);
} elseif ($matches[5]) { // SQL strings: "..." } elseif ($matches[5]) { // SQL strings: "..."
return $this->driver->escapeText(str_replace('""', '"', $matches[6])); return $this->driver->escape(str_replace('""', '"', $matches[6]), dibi::TEXT);
} elseif ($matches[7]) { // string quote } elseif ($matches[7]) { // string quote
return $this->errors[] = '**Alone quote**'; $this->hasError = TRUE;
return '**Alone quote**';
} }
if ($matches[8]) { // SQL identifier substitution if ($matches[8]) { // SQL identifier substitution
@@ -582,7 +598,7 @@ final class Translator
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
} }
throw new \Exception('this should be never executed'); throw new Exception('this should be never executed');
} }
@@ -598,7 +614,7 @@ final class Translator
$parts = explode('.', $value); $parts = explode('.', $value);
foreach ($parts as & $v) { foreach ($parts as & $v) {
if ($v !== '*') { if ($v !== '*') {
$v = $this->driver->escapeIdentifier($v); $v = $this->driver->escape($v, dibi::IDENTIFIER);
} }
} }
return implode('.', $parts); return implode('.', $parts);

View File

@@ -5,45 +5,45 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com) * Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/ */
namespace Dibi;
/** /**
* Provides an interface between a dataset and data-aware components. * Provides an interface between a dataset and data-aware components.
* @package dibi
*/ */
interface IDataSource extends \Countable, \IteratorAggregate interface IDataSource extends Countable, IteratorAggregate
{ {
//function \IteratorAggregate::getIterator(); //function IteratorAggregate::getIterator();
//function \Countable::count(); //function Countable::count();
} }
/** /**
* dibi driver interface. * dibi driver interface.
* @package dibi
*/ */
interface Driver interface IDibiDriver
{ {
/** /**
* Connects to a database. * Connects to a database.
* @param array * @param array
* @return void * @return void
* @throws Exception * @throws DibiException
*/ */
function connect(array & $config); function connect(array & $config);
/** /**
* Disconnects from a database. * Disconnects from a database.
* @return void * @return void
* @throws Exception * @throws DibiException
*/ */
function disconnect(); function disconnect();
/** /**
* Internal: Executes the SQL query. * Internal: Executes the SQL query.
* @param string SQL statement. * @param string SQL statement.
* @return ResultDriver|NULL * @return IDibiResultDriver|NULL
* @throws DriverException * @throws DibiDriverException
*/ */
function query($sql); function query($sql);
@@ -63,7 +63,7 @@ interface Driver
* Begins a transaction (if supported). * Begins a transaction (if supported).
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws DriverException * @throws DibiDriverException
*/ */
function begin($savepoint = NULL); function begin($savepoint = NULL);
@@ -71,7 +71,7 @@ interface Driver
* Commits statements in a transaction. * Commits statements in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws DriverException * @throws DibiDriverException
*/ */
function commit($savepoint = NULL); function commit($savepoint = NULL);
@@ -79,7 +79,7 @@ interface Driver
* Rollback changes in a transaction. * Rollback changes in a transaction.
* @param string optional savepoint name * @param string optional savepoint name
* @return void * @return void
* @throws DriverException * @throws DibiDriverException
*/ */
function rollback($savepoint = NULL); function rollback($savepoint = NULL);
@@ -91,26 +91,18 @@ interface Driver
/** /**
* Returns the connection reflector. * Returns the connection reflector.
* @return Reflector * @return IDibiReflector
*/ */
function getReflector(); function getReflector();
/** /**
* Encodes data for use in a SQL statement. * Encodes data for use in a SQL statement.
* @param mixed value * @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value * @return string encoded value
* @throws InvalidArgumentException
*/ */
function escapeText($value); function escape($value, $type);
function escapeBinary($value);
function escapeIdentifier($value);
function escapeBool($value);
function escapeDate($value);
function escapeDateTime($value);
/** /**
* Encodes string for use in a LIKE statement. * Encodes string for use in a LIKE statement.
@@ -131,8 +123,9 @@ interface Driver
/** /**
* dibi result set driver interface. * dibi result set driver interface.
* @package dibi
*/ */
interface ResultDriver interface IDibiResultDriver
{ {
/** /**
@@ -145,7 +138,7 @@ interface ResultDriver
* 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 Exception * @throws DibiException
*/ */
function seek($row); function seek($row);
@@ -178,18 +171,22 @@ interface ResultDriver
/** /**
* Decodes data from result set. * Decodes data from result set.
* @param string * @param string value
* @return string * @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/ */
function unescapeBinary($value); function unescape($value, $type);
} }
/** /**
* dibi driver reflection. * dibi driver reflection.
*
* @package dibi
*/ */
interface Reflector interface IDibiReflector
{ {
/** /**

View File

@@ -4,32 +4,32 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
// connects to SQlite using dibi class // connects to SQlite using dibi class
echo '<p>Connecting to Sqlite: '; echo '<p>Connecting to Sqlite: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
// connects to SQlite using Dibi\Connection object // connects to SQlite using DibiConnection object
echo '<p>Connecting to Sqlite: '; echo '<p>Connecting to Sqlite: ';
try { try {
$connection = new Dibi\Connection([ $connection = new DibiConnection(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -40,7 +40,7 @@ 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=cp1250');
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -49,19 +49,19 @@ echo "</p>\n";
// connects to MySQLi using array // connects to MySQLi using array
echo '<p>Connecting to MySQLi: '; echo '<p>Connecting to MySQLi: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'mysqli', 'driver' => 'mysqli',
'host' => 'localhost', 'host' => 'localhost',
'username' => 'root', 'username' => 'root',
'password' => 'xxx', 'password' => 'xxx',
'database' => 'dibi', 'database' => 'dibi',
'options' => [ 'options' => array(
MYSQLI_OPT_CONNECT_TIMEOUT => 30, MYSQLI_OPT_CONNECT_TIMEOUT => 30,
], ),
'flags' => MYSQLI_CLIENT_COMPRESS, 'flags' => MYSQLI_CLIENT_COMPRESS,
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -70,14 +70,14 @@ echo "</p>\n";
// connects to ODBC // connects to ODBC
echo '<p>Connecting to ODBC: '; echo '<p>Connecting to ODBC: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'odbc', 'driver' => 'odbc',
'username' => 'root', 'username' => 'root',
'password' => '***', 'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.__DIR__.'/data/sample.mdb', 'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.__DIR__.'/data/sample.mdb',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -86,13 +86,13 @@ echo "</p>\n";
// connects to PostgreSql // connects to PostgreSql
echo '<p>Connecting to PostgreSql: '; echo '<p>Connecting to PostgreSql: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'postgre', 'driver' => 'postgre',
'string' => 'host=localhost port=5432 dbname=mary', 'string' => 'host=localhost port=5432 dbname=mary',
'persistent' => TRUE, 'persistent' => TRUE,
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -101,29 +101,45 @@ 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 {
dibi::connect([ dibi::connect(array(
'driver' => 'pdo', 'driver' => 'pdo',
'dsn' => 'sqlite::memory:', 'dsn' => 'sqlite2::memory:',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
// connects to SQLSRV // connects to MS SQL
echo '<p>Connecting to Microsoft SQL Server: '; echo '<p>Connecting to MS SQL: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'sqlsrv', 'driver' => 'mssql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
));
echo 'OK';
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to MS SQL 2005
echo '<p>Connecting to MS SQL 2005: ';
try {
dibi::connect(array(
'driver' => 'mssql2005',
'host' => '(local)', 'host' => '(local)',
'username' => 'Administrator', 'username' => 'Administrator',
'password' => 'xxx', 'password' => 'xxx',
'database' => 'main', 'database' => 'main',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";
@@ -132,14 +148,14 @@ echo "</p>\n";
// connects to Oracle // connects to Oracle
echo '<p>Connecting to Oracle: '; echo '<p>Connecting to Oracle: ';
try { try {
dibi::connect([ dibi::connect(array(
'driver' => 'oracle', 'driver' => 'oracle',
'username' => 'root', 'username' => 'root',
'password' => 'xxx', 'password' => 'xxx',
'database' => 'db', 'database' => 'db',
]); ));
echo 'OK'; echo 'OK';
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n"; echo get_class($e), ': ', $e->getMessage(), "\n";
} }
echo "</p>\n"; echo "</p>\n";

View File

@@ -4,22 +4,22 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// retrieve database reflection // retrieve database reflection
$database = dibi::getDatabaseInfo(); $database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->getName()}'</h2>\n"; echo "<h2>Database '{$database->name}'</h2>\n";
echo "<ul>\n"; echo "<ul>\n";
foreach ($database->getTables() as $table) { foreach ($database->getTables() as $table) {
echo '<li>', ($table->isView() ? 'view' : 'table') . " {$table->getName()}</li>\n"; echo '<li>', ($table->view ? 'view' : 'table') . " $table->name</li>\n";
} }
echo "</ul>\n"; echo "</ul>\n";
@@ -27,12 +27,12 @@ echo "</ul>\n";
// table reflection // table reflection
$table = $database->getTable('products'); $table = $database->getTable('products');
echo "<h2>Table '{$table->getName()}'</h2>\n"; echo "<h2>Table '{$table->name}'</h2>\n";
echo "Columns\n"; echo "Columns\n";
echo "<ul>\n"; echo "<ul>\n";
foreach ($table->getColumns() as $column) { foreach ($table->getColumns() as $column) {
echo "<li>{$column->getName()} <i>{$column->getNativeType()}</i> <code>{$column->getDefault()}</code></li>\n"; echo "<li>{$column->name} <i>{$column->nativeType}</i> <code>{$column->default}</code></li>\n";
} }
echo "</ul>\n"; echo "</ul>\n";
@@ -40,9 +40,9 @@ echo "</ul>\n";
echo 'Indexes'; echo 'Indexes';
echo "<ul>\n"; echo "<ul>\n";
foreach ($table->getIndexes() as $index) { foreach ($table->getIndexes() as $index) {
echo "<li>{$index->getName()} " . ($index->isPrimary() ? 'primary ' : '') . ($index->isUnique() ? 'unique' : '') . ' ('; echo "<li>{$index->name} " . ($index->primary ? 'primary ' : '') . ($index->unique ? 'unique' : '') . ' (';
foreach ($index->getColumns() as $column) { foreach ($index->getColumns() as $column) {
echo $column->getName(), ', '; echo "$column->name, ";
} }
echo ")</li>\n"; echo ")</li>\n";
} }

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
$res = dibi::query(' $res = dibi::query('
@@ -27,6 +27,6 @@ dibi::dump();
// dump result table // dump result table
echo '<h2>Dibi\Result::dump()</h2>'; echo '<h2>DibiResult::dump()</h2>';
$res->dump(); $res->dump();

View File

@@ -11,10 +11,10 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
/* /*

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
$count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz'); $count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz');

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// some variables // some variables
@@ -55,7 +55,7 @@ dibi::test('
// IF() // IF()
dibi::test('UPDATE products SET', [ dibi::test('UPDATE products SET', array(
'price' => ['IF(price_fixed, price, ?)', 123], 'price' => array('IF(price_fixed, price, ?)', 123),
]); ));
// -> SELECT * FROM customers WHERE LIMIT 10 // -> SELECT * FROM customers WHERE LIMIT 10

View File

@@ -4,15 +4,15 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// SELECT // SELECT
@@ -23,44 +23,44 @@ dibi::test('
SELECT COUNT(*) as [count] SELECT COUNT(*) as [count]
FROM [comments] FROM [comments]
WHERE [ip] LIKE ?', $ipMask, ' WHERE [ip] LIKE ?', $ipMask, '
AND [date] > ', new Dibi\DateTime($timestamp) AND [date] > ', new DibiDateTime($timestamp)
); );
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600 // -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// dibi detects INSERT or REPLACE command // dibi detects INSERT or REPLACE command
dibi::test(' dibi::test('
REPLACE INTO products', [ REPLACE INTO products', array(
'title' => 'Super product', 'title' => 'Super product',
'price' => 318, 'price' => 318,
'active' => TRUE, 'active' => TRUE,
]); ));
// -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1) // -> REPLACE INTO products ([title], [price], [active]) VALUES ('Super product', 318, 1)
// multiple INSERT command // multiple INSERT command
$array = [ $array = array(
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => NULL,
'created' => new DateTime, 'created' => new DateTime,
]; );
dibi::test('INSERT INTO products', $array, $array, $array); dibi::test('INSERT INTO products', $array, $array, $array);
// -> INSERT INTO products ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...) // -> INSERT INTO products ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...)
// dibi detects UPDATE command // dibi detects UPDATE command
dibi::test(' dibi::test('
UPDATE colors SET', [ UPDATE colors SET', array(
'color' => 'blue', 'color' => 'blue',
'order' => 12, 'order' => 12,
], ' ), '
WHERE id=?', 123); WHERE id=?', 123);
// -> UPDATE colors SET [color]='blue', [order]=12 WHERE id=123 // -> UPDATE colors SET [color]='blue', [order]=12 WHERE id=123
// modifier applied to array // modifier applied to array
$array = [1, 2, 3]; $array = array(1, 2, 3);
dibi::test(' dibi::test('
SELECT * SELECT *
FROM people FROM people
@@ -70,10 +70,10 @@ dibi::test('
// modifier %by for ORDER BY // modifier %by for ORDER BY
$order = [ $order = array(
'field1' => 'asc', 'field1' => 'asc',
'field2' => 'desc', 'field2' => 'desc',
]; );
dibi::test(' dibi::test('
SELECT * SELECT *
FROM people FROM people

View File

@@ -4,8 +4,6 @@
<?php <?php
use Dibi\Type;
if (@!include __DIR__ . '/../vendor/autoload.php') { if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`'); die('Install dependencies using `composer install --dev`');
} }
@@ -15,23 +13,23 @@ Tracy\Debugger::enable();
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// using manual hints // using manual hints
$res = dibi::query('SELECT * FROM [customers]'); $res = dibi::query('SELECT * FROM [customers]');
$res->setType('customer_id', Type::INTEGER) $res->setType('customer_id', Dibi::INTEGER)
->setType('added', Type::DATETIME) ->setType('added', Dibi::DATETIME)
->setFormat(Type::DATETIME, 'Y-m-d H:i:s'); ->setFormat(dibi::DATETIME, 'Y-m-d H:i:s');
Tracy\Dumper::dump($res->fetch()); Tracy\Dumper::dump($res->fetch());
// outputs: // outputs:
// Dibi\Row(3) { // DibiRow(3) {
// customer_id => 1 // customer_id => 1
// name => "Dave Lister" (11) // name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19) // added => "2007-03-11 17:20:03" (19)
@@ -42,7 +40,7 @@ $res = dibi::query('SELECT * FROM [customers]');
Tracy\Dumper::dump($res->fetch()); Tracy\Dumper::dump($res->fetch());
// outputs: // outputs:
// Dibi\Row(3) { // DibiRow(3) {
// customer_id => 1 // customer_id => 1
// name => "Dave Lister" (11) // name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19) // added => "2007-03-11 17:20:03" (19)

View File

@@ -15,13 +15,13 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
$connection = dibi::connect([ $connection = dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => array(
'run' => TRUE, 'run' => TRUE,
], ),
]); ));
// add panel to debug bar // add panel to debug bar

View File

@@ -17,13 +17,13 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
$connection = dibi::connect([ $connection = dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => array(
'run' => TRUE, 'run' => TRUE,
], ),
]); ));
// add panel to debug bar // add panel to debug bar

View File

@@ -4,26 +4,26 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS! // CHANGE TO REAL PARAMETERS!
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'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]', [ INSERT INTO [mytable]', array(
'id' => 123, 'id' => 123,
'date' => new DateTime('12.3.2007'), 'date' => new DateTime('12.3.2007'),
'stamp' => new DateTime('23.1.2007 10:23'), 'stamp' => new 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')

View File

@@ -11,14 +11,14 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable(); Tracy\Debugger::enable();
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// using the "prototype" to add custom method to class Dibi\Result // using the "prototype" to add custom method to class DibiResult
Dibi\Result::extensionMethod('fetchShuffle', function (Dibi\Result $obj) { DibiResult::extensionMethod('fetchShuffle', function (DibiResult $obj) {
$all = $obj->fetchAll(); $all = $obj->fetchAll();
shuffle($all); shuffle($all);
return $all; return $all;

View File

@@ -4,23 +4,23 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
$id = 10; $id = 10;
$record = [ $record = array(
'title' => 'Super product', 'title' => 'Super product',
'price' => 318, 'price' => 318,
'active' => TRUE, 'active' => TRUE,
]; );
// SELECT ... // SELECT ...
dibi::select('product_id')->as('id') dibi::select('product_id')->as('id')

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// no limit // no limit

View File

@@ -4,20 +4,20 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague'); date_default_timezone_set('Europe/Prague');
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
// enable query logging to this file // enable query logging to this file
'profiler' => [ 'profiler' => array(
'run' => TRUE, 'run' => TRUE,
'file' => 'data/log.sql', 'file' => 'data/log.sql',
], ),
]); ));
try { try {
@@ -26,7 +26,7 @@ try {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', 5); $res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', 5);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < ?', 38); $res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < ?', 38);
} catch (Dibi\Exception $e) { } catch (DibiException $e) {
echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>'; echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>';
} }

View File

@@ -6,16 +6,16 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
'profiler' => [ 'profiler' => array(
'run' => TRUE, 'run' => TRUE,
], ),
]); ));
// execute some queries... // execute some queries...

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
// create new substitution :blog: ==> wp_ // create new substitution :blog: ==> wp_

View File

@@ -4,13 +4,13 @@
<?php <?php
require __DIR__ . '/../src/loader.php'; require __DIR__ . '/../dibi/dibi.php';
dibi::connect([ dibi::connect(array(
'driver' => 'sqlite3', 'driver' => 'sqlite3',
'database' => 'data/sample.s3db', 'database' => 'data/sample.s3db',
]); ));
echo "<h2>Before</h2>\n"; echo "<h2>Before</h2>\n";
@@ -19,9 +19,9 @@ dibi::query('SELECT * FROM [products]')->dump();
dibi::begin(); dibi::begin();
dibi::query('INSERT INTO [products]', [ dibi::query('INSERT INTO [products]', array(
'title' => 'Test product', 'title' => 'Test product',
]); ));
echo "<h2>After INSERT</h2>\n"; echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump(); dibi::query('SELECT * FROM [products]')->dump();

View File

@@ -1,9 +1,8 @@
[Dibi](http://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN) [Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
========================================================= =========================================================
[![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi) [![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi)
[![Build Status](https://travis-ci.org/dg/dibi.svg?branch=master)](https://travis-ci.org/dg/dibi) [![Build Status](https://travis-ci.org/dg/dibi.svg?branch=master)](https://travis-ci.org/dg/dibi)
[![Build Status Windows](https://ci.appveyor.com/api/projects/status/github/dg/dibi?branch=master&svg=true)](https://ci.appveyor.com/project/dg/dibi/branch/master)
[![Latest Stable Version](https://poser.pugx.org/dibi/dibi/v/stable)](https://github.com/dg/dibi/releases) [![Latest Stable Version](https://poser.pugx.org/dibi/dibi/v/stable)](https://github.com/dg/dibi/releases)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/dg/dibi/blob/master/license.md) [![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/dg/dibi/blob/master/license.md)
@@ -14,29 +13,29 @@ The best way to install Dibi is to use a [Composer](https://getcomposer.org/down
php composer.phar require dibi/dibi php composer.phar require dibi/dibi
Or you can download the latest package from http://dibiphp.com. In this Or you can download the latest package from https://dibiphp.com. In this
package is also `Dibi.minified`, shrinked single-file version of whole Dibi, package is also `Dibi.minified`, shrinked single-file version of whole Dibi,
useful when you don't want to modify the library, but just use it. useful when you don't want to modify the library, but just use it.
Dibi requires PHP 5.4.4 or later. It has been tested with PHP 7 too. Dibi requires PHP 5.2.0 or later. It has been tested with PHP 5.5 too.
Examples Examples
-------- --------
Refer to the `examples` directory for examples. Dibi documentation is Refer to the `examples` directory for examples. Dibi documentation is
available on the [homepage](http://dibiphp.com). available on the [homepage](https://dibiphp.com).
Connect to database: Connect to database:
```php ```php
// connect to database (static way) // connect to database (static way)
dibi::connect([ dibi::connect(array(
'driver' => 'mysql', 'driver' => 'mysql',
'host' => 'localhost', 'host' => 'localhost',
'username' => 'root', 'username' => 'root',
'password' => '***', 'password' => '***',
]); ));
// or object way; in all other examples use $connection-> instead of dibi:: // or object way; in all other examples use $connection-> instead of dibi::
$connection = new DibiConnection($options); $connection = new DibiConnection($options);
@@ -47,19 +46,19 @@ SELECT, INSERT, UPDATE
```php ```php
dibi::query('SELECT * FROM users WHERE id = ?', $id); dibi::query('SELECT * FROM users WHERE id = ?', $id);
$arr = [ $arr = array(
'name' => 'John', 'name' => 'John',
'is_admin' => TRUE, 'is_admin' => TRUE,
]; );
dibi::query('INSERT INTO users', $arr); dibi::query('INSERT INTO users', $arr);
// INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1) // INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1)
dibi::query('UPDATE users SET', $arr, 'WHERE `id`=?', $x); dibi::query('UPDATE users SET', $arr, 'WHERE `id`=?', $x);
// UPDATE users SET `name`='John', `is_admin`=1 WHERE `id` = 123 // UPDATE users SET `name`='John', `is_admin`=1 WHERE `id` = 123
dibi::query('UPDATE users SET', [ dibi::query('UPDATE users SET', array(
'title' => array('SHA1(?)', 'tajneheslo'), 'title' => array('SHA1(?)', 'tajneheslo'),
]); ));
// UPDATE users SET 'title' = SHA1('tajneheslo') // UPDATE users SET 'title' = SHA1('tajneheslo')
``` ```
@@ -82,10 +81,10 @@ foreach ($result as $n => $row) {
Modifiers for arrays: Modifiers for arrays:
```php ```php
dibi::query('SELECT * FROM users WHERE %and', [ dibi::query('SELECT * FROM users WHERE %and', array(
array('number > ?', 10), array('number > ?', 10),
array('number < ?', 100), array('number < ?', 100),
]); ));
// SELECT * FROM users WHERE (number > 10) AND (number < 100) // SELECT * FROM users WHERE (number > 10) AND (number < 100)
``` ```
@@ -117,9 +116,9 @@ dibi::query("SELECT * FROM table WHERE name LIKE %like~", $query);
DateTime: DateTime:
```php ```php
dibi::query('UPDATE users SET', [ dibi::query('UPDATE users SET', array(
'time' => new DateTime, 'time' => new DateTime,
]); ));
// UPDATE users SET ('2008-01-01 01:08:10') // UPDATE users SET ('2008-01-01 01:08:10')
``` ```

View File

@@ -1,58 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* DateTime.
*/
class DateTime extends \DateTime
{
use Strict;
public function __construct($time = 'now', \DateTimeZone $timezone = NULL)
{
if (is_numeric($time)) {
parent::__construct('@' . $time);
$this->setTimeZone($timezone ? $timezone : new \DateTimeZone(date_default_timezone_get()));
} elseif ($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 setTimestamp($timestamp)
{
$zone = $this->getTimezone();
$this->__construct('@' . $timestamp);
return $this->setTimeZone($zone);
}
public function getTimestamp()
{
$ts = $this->format('U');
return is_float($tmp = $ts * 1) ? $ts : $tmp;
}
public function __toString()
{
return $this->format('Y-m-d H:i:s');
}
}

View File

@@ -1,277 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
class Helpers
{
use Strict;
/** @var array */
private static $types;
/**
* Prints out a syntax highlighted version of the SQL command or Result.
* @param string|Result
* @param bool return output instead of printing it?
* @return string
*/
public static function dump($sql = NULL, $return = FALSE)
{
ob_start();
if ($sql instanceof Result && PHP_SAPI === 'cli') {
$hasColors = (substr(getenv('TERM'), 0, 5) === 'xterm');
$maxLen = 0;
foreach ($sql as $i => $row) {
if ($i === 0) {
foreach ($row as $col => $foo) {
$len = mb_strlen($col);
$maxLen = max($len, $maxLen);
}
}
echo $hasColors ? "\033[1;37m#row: $i\033[0m\n" : "#row: $i\n";
foreach ($row as $col => $val) {
$spaces = $maxLen - mb_strlen($col) + 2;
echo "$col" . str_repeat(' ', $spaces) . "$val\n";
}
echo "\n";
}
echo empty($row) ? "empty result set\n\n" : "\n";
} elseif ($sql instanceof Result) {
foreach ($sql as $i => $row) {
if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
}
echo "\t</tr>\n</thead>\n<tbody>\n";
}
echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) {
echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
}
echo "\t</tr>\n";
}
echo empty($row)
? '<p><em>empty result set</em></p>'
: "</tbody>\n</table>\n";
} else {
if ($sql === NULL) {
$sql = \dibi::$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|FETCH\s+NEXT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE|START\s+TRANSACTION|BEGIN|COMMIT|ROLLBACK(?:\s+TO\s+SAVEPOINT)?|(?:RELEASE\s+)?SAVEPOINT';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// insert new lines
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces
$sql = preg_replace('#[ \t]{2,}#', ' ', $sql);
$sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
// syntax highlight
$highlighter = "#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is";
if (PHP_SAPI === 'cli') {
if (substr(getenv('TERM'), 0, 5) === 'xterm') {
$sql = preg_replace_callback($highlighter, function ($m) {
if (!empty($m[1])) { // comment
return "\033[1;30m" . $m[1] . "\033[0m";
} elseif (!empty($m[2])) { // error
return "\033[1;31m" . $m[2] . "\033[0m";
} elseif (!empty($m[3])) { // most important keywords
return "\033[1;34m" . $m[3] . "\033[0m";
} elseif (!empty($m[4])) { // other keywords
return "\033[1;32m" . $m[4] . "\033[0m";
}
}, $sql);
}
echo trim($sql) . "\n\n";
} else {
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback($highlighter, function ($m) {
if (!empty($m[1])) { // comment
return '<em style="color:gray">' . $m[1] . '</em>';
} elseif (!empty($m[2])) { // error
return '<strong style="color:red">' . $m[2] . '</strong>';
} elseif (!empty($m[3])) { // most important keywords
return '<strong style="color:blue">' . $m[3] . '</strong>';
} elseif (!empty($m[4])) { // other keywords
return '<strong style="color:green">' . $m[4] . '</strong>';
}
}, $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n\n";
}
}
if ($return) {
return ob_get_clean();
} else {
ob_end_flush();
}
}
/**
* Finds the best suggestion.
* @return string|NULL
* @internal
*/
public static function getSuggestion(array $items, $value)
{
$best = NULL;
$min = (strlen($value) / 4 + 1) * 10 + .1;
foreach (array_unique($items, SORT_REGULAR) as $item) {
$item = is_object($item) ? $item->getName() : $item;
if (($len = levenshtein($item, $value, 10, 11, 10)) > 0 && $len < $min) {
$min = $len;
$best = $item;
}
}
return $best;
}
/** @internal */
public static function escape(Driver $driver, $value, $type)
{
static $types = [
Type::TEXT => 'text',
Type::BINARY => 'binary',
Type::BOOL => 'bool',
Type::DATE => 'date',
Type::DATETIME => 'datetime',
\dibi::IDENTIFIER => 'identifier',
];
if (isset($types[$type])) {
return $driver->{'escape' . $types[$type]}($value);
} else {
throw new \InvalidArgumentException('Unsupported type.');
}
}
/**
* Heuristic type detection.
* @param string
* @return string|NULL
* @internal
*/
public static function detectType($type)
{
static $patterns = [
'^_' => Type::TEXT, // PostgreSQL arrays
'BYTEA|BLOB|BIN' => Type::BINARY,
'TEXT|CHAR|POINT|INTERVAL' => Type::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT' => Type::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
'^TIME$' => Type::TIME,
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
'DATE' => Type::DATE,
'BOOL' => Type::BOOL,
];
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return NULL;
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new HashMap([__CLASS__, 'detectType']);
}
return self::$types;
}
/**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(& $config, $key, $alias)
{
$foo = & $config;
foreach (explode('|', $key) as $key) {
$foo = & $foo[$key];
}
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
}
/**
* Import SQL dump from file.
* @return int count of sql commands
*/
public static function loadFromFile(Connection $connection, $file)
{
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new \RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$delimiter = ';';
$sql = '';
$driver = $connection->getDriver();
while (!feof($handle)) {
$s = rtrim(fgets($handle));
if (substr($s, 0, 10) === 'DELIMITER ') {
$delimiter = substr($s, 10);
} elseif (substr($s, -strlen($delimiter)) === $delimiter) {
$sql .= substr($s, 0, -strlen($delimiter));
$driver->query($sql);
$sql = '';
$count++;
} else {
$sql .= $s . "\n";
}
}
if (trim($sql) !== '') {
$driver->query($sql);
$count++;
}
fclose($handle);
return $count;
}
}

View File

@@ -1,166 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
use Dibi\Type;
/**
* Reflection metadata class for a table or result set column.
*
* @property-read string $name
* @property-read string $fullName
* @property-read Table $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 Column
{
use Dibi\Strict;
/** @var Dibi\Reflector|NULL when created by Result */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
public function __construct(Dibi\Reflector $reflector = NULL, array $info)
{
$this->reflector = $reflector;
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
public function hasTable()
{
return !empty($this->info['table']);
}
/**
* @return Table
*/
public function getTable()
{
if (empty($this->info['table']) || !$this->reflector) {
throw new Dibi\Exception("Table is unknown or not available.");
}
return new Table($this->reflector, ['name' => $this->info['table']]);
}
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) && $this->info['table'] != NULL ? $this->info['table'] : NULL; // intentionally ==
}
/**
* @return string
*/
public function getType()
{
return Dibi\Helpers::getTypeCache()->{$this->info['nativetype']};
}
/**
* @return mixed
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool
*/
public function isAutoIncrement()
{
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL;
}
/**
* @return mixed
*/
public function getDefault()
{
return isset($this->info['default']) ? $this->info['default'] : NULL;
}
/**
* @param string
* @return mixed
*/
public function getVendorInfo($key)
{
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL;
}
}

View File

@@ -1,115 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a database.
*
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/
class Database
{
use Dibi\Strict;
/** @var Dibi\Reflector */
private $reflector;
/** @var string */
private $name;
/** @var array */
private $tables;
public function __construct(Dibi\Reflector $reflector, $name)
{
$this->reflector = $reflector;
$this->name = $name;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return Table[]
*/
public function getTables()
{
$this->init();
return array_values($this->tables);
}
/**
* @return string[]
*/
public function getTableNames()
{
$this->init();
$res = [];
foreach ($this->tables as $table) {
$res[] = $table->getName();
}
return $res;
}
/**
* @param string
* @return Table
*/
public function getTable($name)
{
$this->init();
$l = strtolower($name);
if (isset($this->tables[$l])) {
return $this->tables[$l];
} else {
throw new Dibi\Exception("Database '$this->name' has no table '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasTable($name)
{
$this->init();
return isset($this->tables[strtolower($name)]);
}
/**
* @return void
*/
protected function init()
{
if ($this->tables === NULL) {
$this->tables = [];
foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new Table($this->reflector, $info);
}
}
}
}

View File

@@ -1,54 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a foreign key.
*
* @property-read string $name
* @property-read array $references
*/
class ForeignKey
{
use Dibi\Strict;
/** @var string */
private $name;
/** @var array of [local, foreign, onDelete, onUpdate] */
private $references;
public function __construct($name, array $references)
{
$this->name = $name;
$this->references = $references;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return array
*/
public function getReferences()
{
return $this->references;
}
}

View File

@@ -1,70 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a index or primary key.
*
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/
class Index
{
use Dibi\Strict;
/** @var array (name, columns, [unique], [primary]) */
private $info;
public function __construct(array $info)
{
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return array
*/
public function getColumns()
{
return $this->info['columns'];
}
/**
* @return bool
*/
public function isUnique()
{
return !empty($this->info['unique']);
}
/**
* @return bool
*/
public function isPrimary()
{
return !empty($this->info['primary']);
}
}

View File

@@ -1,106 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a result set.
*
* @property-read array $columns
* @property-read array $columnNames
*/
class Result
{
use Dibi\Strict;
/** @var Dibi\ResultDriver */
private $driver;
/** @var array */
private $columns;
/** @var array */
private $names;
public function __construct(Dibi\ResultDriver $driver)
{
$this->driver = $driver;
}
/**
* @return Column[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @param bool
* @return string[]
*/
public function getColumnNames($fullNames = FALSE)
{
$this->initColumns();
$res = [];
foreach ($this->columns as $column) {
$res[] = $fullNames ? $column->getFullName() : $column->getName();
}
return $res;
}
/**
* @param string
* @return Column
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->names[$l])) {
return $this->names[$l];
} else {
throw new Dibi\Exception("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 = [];
$reflector = $this->driver instanceof Dibi\Reflector ? $this->driver : NULL;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[strtolower($info['name'])] = new Column($reflector, $info);
}
}
}
}

View File

@@ -1,201 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Reflection;
use Dibi;
/**
* Reflection metadata class for a database table.
*
* @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 Index $primaryKey
*/
class Table
{
use Dibi\Strict;
/** @var Dibi\Reflector */
private $reflector;
/** @var string */
private $name;
/** @var bool */
private $view;
/** @var array */
private $columns;
/** @var array */
private $foreignKeys;
/** @var array */
private $indexes;
/** @var Index */
private $primaryKey;
public function __construct(Dibi\Reflector $reflector, array $info)
{
$this->reflector = $reflector;
$this->name = $info['name'];
$this->view = !empty($info['view']);
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return bool
*/
public function isView()
{
return $this->view;
}
/**
* @return Column[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @return string[]
*/
public function getColumnNames()
{
$this->initColumns();
$res = [];
foreach ($this->columns as $column) {
$res[] = $column->getName();
}
return $res;
}
/**
* @param string
* @return Column
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->columns[$l])) {
return $this->columns[$l];
} else {
throw new Dibi\Exception("Table '$this->name' has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->columns[strtolower($name)]);
}
/**
* @return ForeignKey[]
*/
public function getForeignKeys()
{
$this->initForeignKeys();
return $this->foreignKeys;
}
/**
* @return Index[]
*/
public function getIndexes()
{
$this->initIndexes();
return $this->indexes;
}
/**
* @return Index
*/
public function getPrimaryKey()
{
$this->initIndexes();
return $this->primaryKey;
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = [];
foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new Column($this->reflector, $info);
}
}
}
/**
* @return void
*/
protected function initIndexes()
{
if ($this->indexes === NULL) {
$this->initColumns();
$this->indexes = [];
foreach ($this->reflector->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)];
}
$this->indexes[strtolower($info['name'])] = new Index($info);
if (!empty($info['primary'])) {
$this->primaryKey = $this->indexes[strtolower($info['name'])];
}
}
}
}
/**
* @return void
*/
protected function initForeignKeys()
{
throw new Dibi\NotImplementedException;
}
}

View File

@@ -1,139 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
/**
* Better OOP experience.
*/
trait Strict
{
/** @var array [method => [type => callback]] */
private static $extMethods;
/**
* Call to undefined method.
* @throws \LogicException
*/
public function __call($name, $args)
{
if ($cb = self::extensionMethod(get_class($this) . '::' . $name)) { // back compatiblity
array_unshift($args, $this);
return call_user_func_array($cb, $args);
}
$class = method_exists($this, $name) ? 'parent' : get_class($this);
$items = (new ReflectionClass($this))->getMethods(ReflectionMethod::IS_PUBLIC);
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
throw new \LogicException("Call to undefined method $class::$name()$hint");
}
/**
* Call to undefined static method.
* @throws \LogicException
*/
public static function __callStatic($name, $args)
{
$rc = new ReflectionClass(get_called_class());
$items = array_intersect($rc->getMethods(ReflectionMethod::IS_PUBLIC), $rc->getMethods(ReflectionMethod::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $t()?" : '.';
throw new \LogicException("Call to undefined static method {$rc->getName()}::$name()$hint");
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function &__get($name)
{
if ((method_exists($this, $m = 'get' . $name) || method_exists($this, $m = 'is' . $name))
&& (new ReflectionMethod($this, $m))->isPublic()
) { // back compatiblity
$ret = $this->$m();
return $ret;
}
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
throw new \LogicException("Attempt to read undeclared property {$rc->getName()}::$$name$hint");
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function __set($name, $value)
{
$rc = new ReflectionClass($this);
$items = array_diff($rc->getProperties(ReflectionProperty::IS_PUBLIC), $rc->getProperties(ReflectionProperty::IS_STATIC));
$hint = ($t = Helpers::getSuggestion($items, $name)) ? ", did you mean $$t?" : '.';
throw new \LogicException("Attempt to write to undeclared property {$rc->getName()}::$$name$hint");
}
/**
* @return bool
*/
public function __isset($name)
{
return FALSE;
}
/**
* Access to undeclared property.
* @throws \LogicException
*/
public function __unset($name)
{
$class = get_class($this);
throw new \LogicException("Attempt to unset undeclared property $class::$$name.");
}
/**
* @param string method name
* @param callabke
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (strpos($name, '::') === FALSE) {
$class = get_called_class();
} else {
list($class, $name) = explode('::', $name);
$class = (new ReflectionClass($class))->getName();
}
$list = & self::$extMethods[strtolower($name)];
if ($callback === NULL) { // getter
$cache = & $list[''][$class];
if (isset($cache)) {
return $cache;
}
foreach ([$class] + class_parents($class) + class_implements($class) as $cl) {
if (isset($list[$cl])) {
return $cache = $list[$cl];
}
}
return $cache = FALSE;
} else { // setter
$list[$class] = $callback;
$list[''] = NULL;
}
}
}

View File

@@ -1,32 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Data types.
*/
class Type
{
const
TEXT = 's', // as 'string'
BINARY = 'bin',
BOOL = 'b',
INTEGER = 'i',
FLOAT = 'f',
DATE = 'd',
DATETIME = 'dt',
TIME = 't',
TIME_INTERVAL = 'ti';
final public function __construct()
{
throw new \LogicException('Cannot instantiate static class ' . __CLASS__);
}
}

View File

@@ -1,140 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
if (PHP_VERSION_ID < 50404) {
throw new Exception('Dibi requires PHP 5.4.4 or newer.');
}
spl_autoload_register(function ($class) {
static $map = [
'dibi' => 'dibi.php',
'Dibi\Bridges\Nette\DibiExtension22' => 'Bridges/Nette/DibiExtension22.php',
'Dibi\Bridges\Nette\DibiExtension21' => 'Bridges/Nette/DibiExtension21.php',
'Dibi\Bridges\Nette\Panel' => 'Bridges/Nette/Panel.php',
'Dibi\Bridges\Tracy\Panel' => 'Bridges/Tracy/Panel.php',
'Dibi\Connection' => 'Connection.php',
'Dibi\DataSource' => 'DataSource.php',
'Dibi\DateTime' => 'DateTime.php',
'Dibi\Driver' => 'interfaces.php',
'Dibi\DriverException' => 'exceptions.php',
'Dibi\Drivers\FirebirdDriver' => 'Drivers/FirebirdDriver.php',
'Dibi\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php',
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php',
'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php',
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.php',
'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php',
'Dibi\Drivers\OdbcDriver' => 'Drivers/OdbcDriver.php',
'Dibi\Drivers\OracleDriver' => 'Drivers/OracleDriver.php',
'Dibi\Drivers\PdoDriver' => 'Drivers/PdoDriver.php',
'Dibi\Drivers\PostgreDriver' => 'Drivers/PostgreDriver.php',
'Dibi\Drivers\Sqlite3Driver' => 'Drivers/Sqlite3Driver.php',
'Dibi\Drivers\SqliteReflector' => 'Drivers/SqliteReflector.php',
'Dibi\Event' => 'Event.php',
'Dibi\Exception' => 'exceptions.php',
'Dibi\Fluent' => 'Fluent.php',
'Dibi\HashMap' => 'HashMap.php',
'Dibi\HashMapBase' => 'HashMap.php',
'Dibi\Helpers' => 'Helpers.php',
'Dibi\IDataSource' => 'interfaces.php',
'Dibi\Literal' => 'Literal.php',
'Dibi\Loggers\FileLogger' => 'Loggers/FileLogger.php',
'Dibi\Loggers\FirePhpLogger' => 'Loggers/FirePhpLogger.php',
'Dibi\NotImplementedException' => 'exceptions.php',
'Dibi\NotSupportedException' => 'exceptions.php',
'Dibi\PcreException' => 'exceptions.php',
'Dibi\ProcedureException' => 'exceptions.php',
'Dibi\Reflection\Column' => 'Reflection/Column.php',
'Dibi\Reflection\Database' => 'Reflection/Database.php',
'Dibi\Reflection\ForeignKey' => 'Reflection/ForeignKey.php',
'Dibi\Reflection\Index' => 'Reflection/Index.php',
'Dibi\Reflection\Result' => 'Reflection/Result.php',
'Dibi\Reflection\Table' => 'Reflection/Table.php',
'Dibi\Reflector' => 'interfaces.php',
'Dibi\Result' => 'Result.php',
'Dibi\ResultDriver' => 'interfaces.php',
'Dibi\ResultIterator' => 'ResultIterator.php',
'Dibi\Row' => 'Row.php',
'Dibi\Strict' => 'Strict.php',
'Dibi\Translator' => 'Translator.php',
'Dibi\Type' => 'Type.php',
], $old2new = [
'Dibi' => 'dibi.php',
'DibiColumnInfo' => 'Dibi\Reflection\Column',
'DibiConnection' => 'Dibi\Connection',
'DibiDatabaseInfo' => 'Dibi\Reflection\Database',
'DibiDataSource' => 'Dibi\DataSource',
'DibiDateTime' => 'Dibi\DateTime',
'DibiDriverException' => 'Dibi\DriverException',
'DibiEvent' => 'Dibi\Event',
'DibiException' => 'Dibi\Exception',
'DibiFileLogger' => 'Dibi\Loggers\FileLogger',
'DibiFirebirdDriver' => 'Dibi\Drivers\FirebirdDriver',
'DibiFirePhpLogger' => 'Dibi\Loggers\FirePhpLogger',
'DibiFluent' => 'Dibi\Fluent',
'DibiForeignKeyInfo' => 'Dibi\Reflection\ForeignKey',
'DibiHashMap' => 'Dibi\HashMap',
'DibiHashMapBase' => 'Dibi\HashMapBase',
'DibiIndexInfo' => 'Dibi\Reflection\Index',
'DibiLiteral' => 'Dibi\Literal',
'DibiMsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
'DibiMsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
'DibiMySqlDriver' => 'Dibi\Drivers\MySqlDriver',
'DibiMySqliDriver' => 'Dibi\Drivers\MySqliDriver',
'DibiMySqlReflector' => 'Dibi\Drivers\MySqlReflector',
'DibiNette21Extension' => 'Dibi\Bridges\Nette\DibiExtension21',
'DibiNettePanel' => 'Dibi\Bridges\Nette\Panel',
'DibiNotImplementedException' => 'Dibi\NotImplementedException',
'DibiNotSupportedException' => 'Dibi\NotSupportedException',
'DibiOdbcDriver' => 'Dibi\Drivers\OdbcDriver',
'DibiOracleDriver' => 'Dibi\Drivers\OracleDriver',
'DibiPcreException' => 'Dibi\PcreException',
'DibiPdoDriver' => 'Dibi\Drivers\PdoDriver',
'DibiPostgreDriver' => 'Dibi\Drivers\PostgreDriver',
'DibiProcedureException' => 'Dibi\ProcedureException',
'DibiResult' => 'Dibi\Result',
'DibiResultInfo' => 'Dibi\Reflection\Result',
'DibiResultIterator' => 'Dibi\ResultIterator',
'DibiRow' => 'Dibi\Row',
'DibiSqlite3Driver' => 'Dibi\Drivers\Sqlite3Driver',
'DibiSqliteReflector' => 'Dibi\Drivers\SqliteReflector',
'DibiTableInfo' => 'Dibi\Reflection\Table',
'DibiTranslator' => 'Dibi\Translator',
'IDataSource' => 'Dibi\IDataSource',
'IDibiDriver' => 'Dibi\Driver',
'IDibiReflector' => 'Dibi\Reflector',
'IDibiResultDriver' => 'Dibi\ResultDriver',
'Dibi\Drivers\MsSql2005Driver' => 'Dibi\Drivers\SqlsrvDriver',
'Dibi\Drivers\MsSql2005Reflector' => 'Dibi\Drivers\SqlsrvReflector',
];
if (isset($map[$class])) {
require __DIR__ . '/Dibi/' . $map[$class];
} elseif (isset($old2new[$class])) {
class_alias($old2new[$class], $class);
}
});
// preload for compatiblity
array_map('class_exists', [
'DibiConnection',
'DibiDateTime',
'DibiDriverException',
'DibiEvent',
'DibiException',
'DibiFluent',
'DibiLiteral',
'DibiNotImplementedException',
'DibiNotSupportedException',
'DibiPcreException',
'DibiProcedureException',
'DibiResult',
'DibiRow',
'IDataSource',
'IDibiDriver',
]);

View File

@@ -1,44 +0,0 @@
[sqlite] ; default
driver = sqlite3
database = :memory:
system = sqlite
[sqlite pdo]
driver = pdo
dsn = "sqlite::memory:"
system = sqlite
[mysql]
driver = mysql
host = 127.0.0.1
username = root
password = "Password12!"
charset = utf8
system = mysql
[mysql improved]
driver = mysqli
host = 127.0.0.1
username = root
password = "Password12!"
charset = utf8
system = mysql
[mysql pdo]
driver = pdo
dsn = "mysql:host=127.0.0.1"
username = root
password = "Password12!"
system = mysql
[odbc]
driver = odbc
dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=data/odbc.mdb"
system = odbc
[odbc pdo]
driver = pdo
dsn = "odbc:Driver={Microsoft Access Driver (*.mdb)};Dbq=data/odbc.mdb"
username =
password =
system = odbc

View File

@@ -3,6 +3,11 @@ driver = sqlite3
database = :memory: database = :memory:
system = sqlite system = sqlite
[sqlite 2]
driver = sqlite
database = :memory:
system = sqlite
[sqlite pdo] [sqlite pdo]
driver = pdo driver = pdo
dsn = "sqlite::memory:" dsn = "sqlite::memory:"
@@ -57,8 +62,15 @@ username =
password = password =
system = odbc system = odbc
[mssql]
driver = mssql
host = 127.0.0.1
username = dibi
password =
system = sqlsrv
[sqlsrv] [sqlsrv]
driver = sqlsrv driver = mssql2005
host = (local) host = (local)
username = dibi username = dibi
password = password =

View File

@@ -1,46 +0,0 @@
[sqlite] ; default
driver = sqlite3
database = :memory:
system = sqlite
[sqlite pdo]
driver = pdo
dsn = "sqlite::memory:"
system = sqlite
[mysql]
driver = mysql
host = 127.0.0.1
username = root
password =
charset = utf8
system = mysql
[mysql improved]
driver = mysqli
host = 127.0.0.1
username = root
password =
charset = utf8
system = mysql
[mysql pdo]
driver = pdo
dsn = "mysql:host=127.0.0.1"
username = root
password =
system = mysql
[postgre]
driver = postgre
host = 127.0.0.1
username = postgres
password =
system = postgre
[postgre pdo]
driver = pdo
dsn = "pgsql:host=127.0.0.1;dbname=dibi_test"
username = postgres
password =
system = postgre

View File

@@ -1,320 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
use Dibi\Row;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch a single value
$res = $conn->query('SELECT [title] FROM [products]');
Assert::same('Chair', $res->fetchSingle());
// fetch complete result set
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal([
new Row(['product_id' => num(1), 'title' => 'Chair']),
new Row(['product_id' => num(2), 'title' => 'Table']),
new Row(['product_id' => num(3), 'title' => 'Computer']),
], $res->fetchAll());
// fetch complete result set like pairs key => value
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::same(
[1 => 'Chair', 'Table', 'Computer'],
$res->fetchPairs('product_id', 'title')
);
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::same(
[1 => 'Chair', 'Table', 'Computer'],
$res->fetchPairs()
);
// fetch row by row
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal([
new Row(['product_id' => num(1), 'title' => 'Chair']),
new Row(['product_id' => num(2), 'title' => 'Table']),
new Row(['product_id' => num(3), 'title' => 'Computer']),
], iterator_to_array($res));
// fetch complete result set like association array
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal([
'Chair' => new Row(['product_id' => num(1), 'title' => 'Chair']),
'Table' => new Row(['product_id' => num(2), 'title' => 'Table']),
'Computer' => new Row(['product_id' => num(3), 'title' => 'Computer']),
], $res->fetchAssoc('title'));
// more complex association array
function query($conn) {
return $conn->query($conn->getConfig('system') === 'odbc' ? '
SELECT products.title, customers.name, orders.amount
FROM ([products]
INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
INNER JOIN [customers] ON [orders.customer_id] = [customers.customer_id]
ORDER BY orders.order_id
' : '
SELECT products.title AS title, customers.name AS name, orders.amount AS amount
FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
ORDER BY orders.order_id
');
}
Assert::equal([
'Arnold Rimmer' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'Dave Lister' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'Kristine Kochanski' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
], query($conn)->fetchAssoc('name,title'));
Assert::equal([
'Arnold Rimmer' => [
[
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
],
[
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
],
'Dave Lister' => [
[
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
],
'Kristine Kochanski' => [
[
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
],
], query($conn)->fetchAssoc('name,#,title'));
Assert::equal([
'Arnold Rimmer' => [
'title' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
],
'Dave Lister' => [
'title' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'name' => 'Dave Lister',
'amount' => num(3.0),
],
'Kristine Kochanski' => [
'title' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
],
], query($conn)->fetchAssoc('name,=,title'));
Assert::equal([
'Arnold Rimmer' => new Row([
'title' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
]),
'Dave Lister' => new Row([
'title' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'name' => 'Dave Lister',
'amount' => num(3.0),
]),
'Kristine Kochanski' => new Row([
'title' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
]),
], query($conn)->fetchAssoc('name,@,title'));
Assert::equal([
new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
new Row([
'title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
new Row([
'title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
new Row([
'title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
], query($conn)->fetchAssoc('@,='));
Assert::equal([
'Arnold Rimmer' => [
'title' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
],
'Dave Lister' => [
'title' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'name' => 'Dave Lister',
'amount' => num(3.0),
],
'Kristine Kochanski' => [
'title' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
],
], query($conn)->fetchAssoc('name,=,title,@'));
// old syntax
Assert::equal([
'Arnold Rimmer' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'Dave Lister' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'Kristine Kochanski' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
], query($conn)->fetchAssoc('name|title'));
Assert::equal([
'Arnold Rimmer' => [
[
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
],
[
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
],
'Dave Lister' => [
[
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
],
'Kristine Kochanski' => [
[
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
],
], query($conn)->fetchAssoc('name[]title'));
Assert::equal([
'Arnold Rimmer' => new Row([
'title' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
]),
'Dave Lister' => new Row([
'title' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'name' => 'Dave Lister',
'amount' => num(3.0),
]),
'Kristine Kochanski' => new Row([
'title' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
]),
], query($conn)->fetchAssoc('name->title'));
Assert::equal([
'Arnold Rimmer' => new Row([
'title' => ['Chair' => 'Arnold Rimmer', 'Computer' => 'Arnold Rimmer'],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
]),
'Dave Lister' => new Row([
'title' => ['Table' => 'Dave Lister'],
'name' => 'Dave Lister',
'amount' => num(3.0),
]),
'Kristine Kochanski' => new Row([
'title' => ['Computer' => 'Kristine Kochanski'],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
]),
], query($conn)->fetchAssoc('name->title=name'));
Assert::equal([
new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
], query($conn)->fetchAssoc('[]'));
Assert::equal([
'Arnold Rimmer' => new Row([
'title' => [
'Chair' => new Row(['title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0)]),
'Computer' => new Row(['title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0)]),
],
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
]),
'Dave Lister' => new Row([
'title' => [
'Table' => new Row(['title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0)]),
],
'name' => 'Dave Lister',
'amount' => num(3.0),
]),
'Kristine Kochanski' => new Row([
'title' => [
'Computer' => new Row(['title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0)]),
],
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
]),
], query($conn)->fetchAssoc('name->title->'));

View File

@@ -1,26 +0,0 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
// create new substitution :blog: ==> wp_
$conn->getSubstitutes()->blog = 'wp_';
Assert::same(
reformat('UPDATE wp_items SET [text]=\'Hello World\''),
$conn->translate("UPDATE :blog:items SET [text]='Hello World'")
);
Assert::same(
reformat('UPDATE \'wp_\' SET [text]=\'Hello World\''),
$conn->translate("UPDATE :blog: SET [text]='Hello World'")
);
Assert::same(
reformat('UPDATE \':blg:\' SET [text]=\'Hello World\''),
$conn->translate("UPDATE :blg: SET [text]='Hello World'")
);

View File

@@ -1,12 +1,11 @@
<?php <?php
use Tester\Assert; use Tester\Assert;
use Dibi\Row;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
@@ -55,9 +54,9 @@ FROM (SELECT * FROM products) t
); );
$ds->select(['product_id']); $ds->select(array('product_id'));
$ds->orderBy(['product_id' => dibi::ASC]); $ds->orderBy(array('product_id' => dibi::ASC));
$ds->where(['product_id = 1']); $ds->where(array('product_id = 1'));
Assert::match( Assert::match(
reformat(" reformat("
SELECT [product_id] SELECT [product_id]
@@ -80,11 +79,11 @@ FROM (SELECT * FROM products) t
Assert::same(1, $ds->toDataSource()->count()); Assert::same(1, $ds->toDataSource()->count());
Assert::equal([ Assert::equal(array(
new Row([ new DibiRow(array(
'product_id' => 1, 'product_id' => 1,
]), )),
], iterator_to_array($ds)); ), iterator_to_array($ds));
Assert::match( Assert::match(
reformat(" reformat("
@@ -118,35 +117,35 @@ FROM (SELECT [title] FROM [products]) t'),
(string) $ds (string) $ds
); );
Assert::equal(new Row([ Assert::equal(new DibiRow(array(
'product_id' => 1, 'product_id' => 1,
'title' => 'Chair', 'title' => 'Chair',
]), $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetch()); )), $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetch());
Assert::same(1, $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchSingle()); Assert::same(1, $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchSingle());
Assert::same( Assert::same(
[1 => 'Chair', 'Table', 'Computer'], array(1 => 'Chair', 'Table', 'Computer'),
$conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchPairs() $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchPairs()
); );
Assert::equal([ Assert::equal(array(
1 => new Row([ 1 => new DibiRow(array(
'product_id' => 1, 'product_id' => 1,
'title' => 'Chair', 'title' => 'Chair',
]), )),
new Row([ new DibiRow(array(
'product_id' => 2, 'product_id' => 2,
'title' => 'Table', 'title' => 'Table',
]), )),
new Row([ new DibiRow(array(
'product_id' => 3, 'product_id' => 3,
'title' => 'Computer', 'title' => 'Computer',
]), )),
], $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchAssoc('product_id')); ), $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchAssoc('product_id'));
$ds = new Dibi\DataSource('products', $conn); $ds = new DibiDataSource('products', $conn);
Assert::match( Assert::match(
reformat(' reformat('

View File

@@ -8,13 +8,13 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$conn->query('INSERT INTO products', [ $conn->query('INSERT INTO products', array(
'title' => 'Test product', 'title' => 'Test product',
]); ));
Assert::same(1, $conn->getAffectedRows()); Assert::same(1, $conn->getAffectedRows());

View File

@@ -5,13 +5,12 @@
*/ */
use Tester\Assert; use Tester\Assert;
use Dibi\Connection;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
test(function () use ($config) { test(function () use ($config) {
$conn = new Connection($config); $conn = new DibiConnection($config);
Assert::true($conn->isConnected()); Assert::true($conn->isConnected());
$conn->disconnect(); $conn->disconnect();
@@ -20,7 +19,7 @@ test(function () use ($config) {
test(function () use ($config) { // lazy test(function () use ($config) { // lazy
$conn = new Connection($config + ['lazy' => TRUE]); $conn = new DibiConnection($config + array('lazy' => TRUE));
Assert::false($conn->isConnected()); Assert::false($conn->isConnected());
$conn->query('SELECT 1'); $conn->query('SELECT 1');
@@ -29,10 +28,10 @@ test(function () use ($config) { // lazy
test(function () use ($config) { // query string test(function () use ($config) { // query string
$conn = new Connection(http_build_query($config, NULL, '&')); $conn = new DibiConnection(http_build_query($config, NULL, '&'));
Assert::true($conn->isConnected()); Assert::true($conn->isConnected());
Assert::null($conn->getConfig('lazy')); Assert::null($conn->getConfig('lazy'));
Assert::same($config['driver'], $conn->getConfig('driver')); Assert::same($config['driver'], $conn->getConfig('driver'));
Assert::type('Dibi\Driver', $conn->getDriver()); Assert::type('IDibiDriver', $conn->getDriver());
}); });

View File

@@ -0,0 +1,319 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch a single value
$res = $conn->query('SELECT [title] FROM [products]');
Assert::same('Chair', $res->fetchSingle());
// fetch complete result set
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal(array(
new DibiRow(array('product_id' => num(1), 'title' => 'Chair')),
new DibiRow(array('product_id' => num(2), 'title' => 'Table')),
new DibiRow(array('product_id' => num(3), 'title' => 'Computer')),
), $res->fetchAll());
// fetch complete result set like pairs key => value
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::same(
array(1 => 'Chair', 'Table', 'Computer'),
$res->fetchPairs('product_id', 'title')
);
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::same(
array(1 => 'Chair', 'Table', 'Computer'),
$res->fetchPairs()
);
// fetch row by row
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal(array(
new DibiRow(array('product_id' => num(1), 'title' => 'Chair')),
new DibiRow(array('product_id' => num(2), 'title' => 'Table')),
new DibiRow(array('product_id' => num(3), 'title' => 'Computer')),
), iterator_to_array($res));
// fetch complete result set like association array
$res = $conn->query('SELECT * FROM [products] ORDER BY product_id');
Assert::equal(array(
'Chair' => new DibiRow(array('product_id' => num(1), 'title' => 'Chair')),
'Table' => new DibiRow(array('product_id' => num(2), 'title' => 'Table')),
'Computer' => new DibiRow(array('product_id' => num(3), 'title' => 'Computer')),
), $res->fetchAssoc('title'));
// more complex association array
function query($conn) {
return $conn->query($conn->getConfig('system') === 'odbc' ? '
SELECT products.title, customers.name, orders.amount
FROM ([products]
INNER JOIN [orders] ON [products.product_id] = [orders.product_id])
INNER JOIN [customers] ON [orders.customer_id] = [customers.customer_id]
ORDER BY orders.order_id
' : '
SELECT products.title AS title, customers.name AS name, orders.amount AS amount
FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
ORDER BY orders.order_id
');
}
Assert::equal(array(
'Arnold Rimmer' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'Dave Lister' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'Kristine Kochanski' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
), query($conn)->fetchAssoc('name,title'));
Assert::equal(array(
'Arnold Rimmer' => array(
array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
),
array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
),
'Dave Lister' => array(
array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
),
'Kristine Kochanski' => array(
array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
),
), query($conn)->fetchAssoc('name,#,title'));
Assert::equal(array(
'Arnold Rimmer' => array(
'title' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
),
'Dave Lister' => array(
'title' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'name' => 'Dave Lister',
'amount' => num(3.0),
),
'Kristine Kochanski' => array(
'title' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
),
), query($conn)->fetchAssoc('name,=,title'));
Assert::equal(array(
'Arnold Rimmer' => new DibiRow(array(
'title' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
)),
'Dave Lister' => new DibiRow(array(
'title' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'name' => 'Dave Lister',
'amount' => num(3.0),
)),
'Kristine Kochanski' => new DibiRow(array(
'title' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
)),
), query($conn)->fetchAssoc('name,@,title'));
Assert::equal(array(
new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
new DibiRow(array(
'title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
new DibiRow(array(
'title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
new DibiRow(array(
'title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
), query($conn)->fetchAssoc('@,='));
Assert::equal(array(
'Arnold Rimmer' => array(
'title' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
),
'Dave Lister' => array(
'title' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'name' => 'Dave Lister',
'amount' => num(3.0),
),
'Kristine Kochanski' => array(
'title' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
),
), query($conn)->fetchAssoc('name,=,title,@'));
// old syntax
Assert::equal(array(
'Arnold Rimmer' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'Dave Lister' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'Kristine Kochanski' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
), query($conn)->fetchAssoc('name|title'));
Assert::equal(array(
'Arnold Rimmer' => array(
array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
),
array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
),
'Dave Lister' => array(
array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
),
'Kristine Kochanski' => array(
array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
),
), query($conn)->fetchAssoc('name[]title'));
Assert::equal(array(
'Arnold Rimmer' => new DibiRow(array(
'title' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
)),
'Dave Lister' => new DibiRow(array(
'title' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'name' => 'Dave Lister',
'amount' => num(3.0),
)),
'Kristine Kochanski' => new DibiRow(array(
'title' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
)),
), query($conn)->fetchAssoc('name->title'));
Assert::equal(array(
'Arnold Rimmer' => new DibiRow(array(
'title' => array('Chair' => 'Arnold Rimmer', 'Computer' => 'Arnold Rimmer'),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
)),
'Dave Lister' => new DibiRow(array(
'title' => array('Table' => 'Dave Lister'),
'name' => 'Dave Lister',
'amount' => num(3.0),
)),
'Kristine Kochanski' => new DibiRow(array(
'title' => array('Computer' => 'Kristine Kochanski'),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
)),
), query($conn)->fetchAssoc('name->title=name'));
Assert::equal(array(
new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
), query($conn)->fetchAssoc('[]'));
Assert::equal(array(
'Arnold Rimmer' => new DibiRow(array(
'title' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'name' => 'Arnold Rimmer',
'amount' => num(7.0),
)),
'Dave Lister' => new DibiRow(array(
'title' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'name' => 'Dave Lister',
'amount' => num(3.0),
)),
'Kristine Kochanski' => new DibiRow(array(
'title' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
'name' => 'Kristine Kochanski',
'amount' => num(5.0),
)),
), query($conn)->fetchAssoc('name->title->'));

View File

@@ -0,0 +1,56 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new DibiConnection($config);
// create new substitution :blog: ==> wp_
$conn->getSubstitutes()->blog = 'wp_';
Assert::same(
reformat('UPDATE wp_items SET [val]=1'),
$conn->translate('UPDATE :blog:items SET [val]=1')
);
Assert::same(
reformat('UPDATE [wp_items] SET [val]=1'),
$conn->translate('UPDATE [:blog:items] SET [val]=1')
);
Assert::same(
reformat("UPDATE 'wp_' SET [val]=1"),
$conn->translate('UPDATE :blog: SET [val]=1')
);
Assert::same(
reformat("UPDATE ':blg:' SET [val]=1"),
$conn->translate('UPDATE :blg: SET [val]=1')
);
Assert::same(
reformat("UPDATE table SET [text]=':blog:a'"),
$conn->translate("UPDATE table SET [text]=':blog:a'")
);
// create new substitution :: (empty) ==> my_
$conn->getSubstitutes()->{''} = 'my_';
Assert::same(
reformat('UPDATE my_table SET [val]=1'),
$conn->translate('UPDATE ::table SET [val]=1')
);
// create substitutions using fallback callback
$conn->getSubstitutes()->setCallback(function ($expr) {
return '_' . $expr . '_';
});
Assert::same(
reformat('UPDATE _account_user SET [val]=1'),
$conn->translate('UPDATE :account:user SET [val]=1')
);

View File

@@ -9,30 +9,30 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
/*Assert::exception(function () use ($conn) { /*Assert::exception(function () use ($conn) {
$conn->rollback(); $conn->rollback();
}, 'Dibi\Exception'); }, 'DibiException');
Assert::exception(function () use ($conn) { Assert::exception(function () use ($conn) {
$conn->commit(); $conn->commit();
}, 'Dibi\Exception'); }, 'DibiException');
$conn->begin(); $conn->begin();
Assert::exception(function () use ($conn) { Assert::exception(function () use ($conn) {
$conn->begin(); $conn->begin();
}, 'Dibi\Exception'); }, 'DibiException');
*/ */
$conn->begin(); $conn->begin();
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle()); Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
$conn->query('INSERT INTO [products]', [ $conn->query('INSERT INTO [products]', array(
'title' => 'Test product', 'title' => 'Test product',
]); ));
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle()); Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
$conn->rollback(); $conn->rollback();
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle()); Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
@@ -41,8 +41,8 @@ Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSing
$conn->begin(); $conn->begin();
$conn->query('INSERT INTO [products]', [ $conn->query('INSERT INTO [products]', array(
'title' => 'Test product', 'title' => 'Test product',
]); ));
$conn->commit(); $conn->commit();
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle()); Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());

View File

@@ -1,14 +1,13 @@
<?php <?php
use Tester\Assert; use Tester\Assert;
use Dibi\Fluent;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$fluent = new Fluent($conn); $fluent = new DibiFluent($conn);
$fluent->select('*')->from('table')->where('x=1'); $fluent->select('*')->from('table')->where('x=1');
$dolly = clone $fluent; $dolly = clone $fluent;
$dolly->where('y=1'); $dolly->where('y=1');
@@ -18,7 +17,7 @@ Assert::same(reformat('SELECT * FROM [table] WHERE x=1'), (string) $fluent);
Assert::same(reformat('SELECT * FROM [table] WHERE x=1 AND y=1 FOO'), (string) $dolly); Assert::same(reformat('SELECT * FROM [table] WHERE x=1 AND y=1 FOO'), (string) $dolly);
$fluent = new Fluent($conn); $fluent = new DibiFluent($conn);
$fluent->select('id')->from('table')->where('id = %i', 1); $fluent->select('id')->from('table')->where('id = %i', 1);
$dolly = clone $fluent; $dolly = clone $fluent;
$dolly->where('cd = %i', 5); $dolly->where('cd = %i', 5);
@@ -27,7 +26,7 @@ Assert::same(reformat('SELECT [id] FROM [table] WHERE id = 1'), (string) $fluent
Assert::same(reformat('SELECT [id] FROM [table] WHERE id = 1 AND cd = 5'), (string) $dolly); Assert::same(reformat('SELECT [id] FROM [table] WHERE id = 1 AND cd = 5'), (string) $dolly);
$fluent = new Fluent($conn); $fluent = new DibiFluent($conn);
$fluent->select('*')->from('table'); $fluent->select('*')->from('table');
$dolly = clone $fluent; $dolly = clone $fluent;
$dolly->removeClause('select')->select('count(*)'); $dolly->removeClause('select')->select('count(*)');

View File

@@ -5,7 +5,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$fluent = $conn->delete('table')->as('bAlias') $fluent = $conn->delete('table')->as('bAlias')

View File

@@ -8,7 +8,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
@@ -20,28 +20,23 @@ $fluent = $conn->select('*')
->orderBy('customer_id'); ->orderBy('customer_id');
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent (string) $fluent
); );
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql
);
$fluent->fetchAll(2, 3);
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 3 OFFSET 2'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent (string) $fluent
); );
@@ -49,33 +44,39 @@ Assert::same(
$fluent->limit(0); $fluent->limit(0);
$fluent->fetch(); $fluent->fetch();
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); $fluent->fetchSingle();
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
(string) $fluent (string) $fluent
); );
$fluent->removeClause('limit'); $fluent->removeClause('limit');
$fluent->fetch(); try {
$fluent->fetch();
} catch (DibiException $e) {
}
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 3'),
dibi::$sql dibi::$sql
); );
$fluent->fetchSingle(); try {
$fluent->fetchSingle();
} catch (DibiException $e) {
}
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 3'),
dibi::$sql dibi::$sql
); );
Assert::same( Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 18446744073709551615 OFFSET 3'), reformat('SELECT * FROM [customers] ORDER BY [customer_id] OFFSET 3'),
(string) $fluent (string) $fluent
); );

View File

@@ -0,0 +1,49 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch a single value
$res = $conn->select('title')->from('products')->orderBy('product_id');
Assert::equal('Chair', $res->fetchSingle());
// fetch complete result set
$res = $conn->select('*')->from('products')->orderBy('product_id');
Assert::equal(array(
new DibiRow(array('product_id' => num(1), 'title' => 'Chair')),
new DibiRow(array('product_id' => num(2), 'title' => 'Table')),
new DibiRow(array('product_id' => num(3), 'title' => 'Computer')),
), $res->fetchAll());
// more complex association array
if ($config['system'] !== 'odbc') {
$res = $conn->select(array('products.title' => 'title', 'customers.name' => 'name'))->select('orders.amount')->as('amount')
->from('products')
->innerJoin('orders')->using('(product_id)')
->innerJoin('customers')->using('([customer_id])')
->orderBy('order_id');
Assert::equal(array(
'Arnold Rimmer' => array(
'Chair' => new DibiRow(array('title' => 'Chair', 'name' => 'Arnold Rimmer', 'amount' => num(7.0))),
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Arnold Rimmer', 'amount' => num(2.0))),
),
'Dave Lister' => array(
'Table' => new DibiRow(array('title' => 'Table', 'name' => 'Dave Lister', 'amount' => num(3.0))),
),
'Kristine Kochanski' => array(
'Computer' => new DibiRow(array('title' => 'Computer', 'name' => 'Kristine Kochanski', 'amount' => num(5.0))),
),
), $res->fetchAssoc('name,title'));
}

View File

@@ -5,14 +5,14 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$arr = [ $arr = array(
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => NULL,
]; );
$fluent = $conn->insert('table', $arr) $fluent = $conn->insert('table', $arr)
->setFlag('IGNORE')->setFlag('DELAYED'); ->setFlag('IGNORE')->setFlag('DELAYED');

View File

@@ -9,7 +9,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$max = 10; $max = 10;
@@ -18,7 +18,7 @@ $min = 5;
$fluent = $conn->select('*') $fluent = $conn->select('*')
->select('a') ->select('a')
->select('b')->as('bAlias') ->select('b')->as('bAlias')
->select(['c', 'd', 'e']) ->select(array('c', 'd', 'e'))
->select('%n', 'd'); ->select('%n', 'd');
Assert::same( Assert::same(
@@ -62,17 +62,17 @@ Assert::same(
$fluent->where('col > %i', $max) $fluent->where('col > %i', $max)
->or('col < %i', $min) ->or('col < %i', $min)
->where('active = 1') ->where('active = 1')
->where('col')->in([1, 2, 3]) ->where('col')->in(array(1, 2, 3))
->orderBy('val')->asc() ->orderBy('val')->asc()
->orderBy('[val2] DESC') ->orderBy('[val2] DESC')
->orderBy(['val3' => -1]); ->orderBy(array('val3' => -1));
Assert::same( Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3) ORDER BY [val] ASC , [val2] DESC , [val3] DESC'), reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3) ORDER BY [val] ASC , [val2] DESC , [val3] DESC'),
(string) $fluent (string) $fluent
); );
$fluent->orderBy(Dibi\Fluent::REMOVE); $fluent->orderBy(DibiFluent::REMOVE);
Assert::same( Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'), reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col WHERE col > 10 OR col < 5 AND active = 1 AND [col] IN (1, 2, 3)'),
@@ -92,20 +92,17 @@ $fluent = $conn->select('*')
->offset(0); ->offset(0);
Assert::same( Assert::same(
reformat([ reformat('SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 LIMIT 1 OFFSET 0'),
'odbc' => 'SELECT TOP 1 * FROM ( SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123) t',
' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 LIMIT 1',
]),
(string) $fluent (string) $fluent
); );
$fluent = $conn->select('*') $fluent = $conn->select('*')
->select(['x' => 'xAlias']) ->select(array('x' => 'xAlias'))
->from('products') ->from('products')
->innerJoin('orders')->using('(product_id)') ->innerJoin('orders')->using('(product_id)')
->innerJoin('customers')->using('([customer_id])') ->innerJoin('customers')->using('([customer_id])')
->innerJoin('items')->using('(%n)', ['customer_id', 'order_id']); ->innerJoin('items')->using('(%n)', array('customer_id', 'order_id'));
Assert::same( Assert::same(
reformat('SELECT * , [x] AS [xAlias] FROM [products] INNER JOIN [orders] USING (product_id) INNER JOIN [customers] USING ([customer_id]) INNER JOIN [items] USING ([customer_id], [order_id])'), reformat('SELECT * , [x] AS [xAlias] FROM [products] INNER JOIN [orders] USING (product_id) INNER JOIN [customers] USING ([customer_id]) INNER JOIN [items] USING ([customer_id], [order_id])'),
@@ -126,9 +123,9 @@ Assert::same(
$fluent = $conn->select('*') $fluent = $conn->select('*')
->from(['me' => 't']) ->from(array('me' => 't'))
->where('col > %i', $max) ->where('col > %i', $max)
->where(['x' => 'a', 'b', 'c']); ->where(array('x' => 'a', 'b', 'c'));
Assert::same( Assert::same(
reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'), reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'),
@@ -136,18 +133,6 @@ Assert::same(
); );
if ($config['system'] === 'mysql') {
$fluent = $conn->select('*')
->limit(' 1; DROP TABLE users')
->offset(' 1; DROP TABLE users');
Assert::same(
reformat(' SELECT * LIMIT 1 OFFSET 1'),
(string) $fluent
);
}
$fluent = $conn->select('*')->from('abc') $fluent = $conn->select('*')->from('abc')
->where('x IN (%SQL)', $conn->select('id')->from('xyz')); ->where('x IN (%SQL)', $conn->select('id')->from('xyz'));

View File

@@ -5,14 +5,14 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$arr = [ $arr = array(
'title' => 'Super Product', 'title' => 'Super Product',
'price' => 12, 'price' => 12,
'brand' => NULL, 'brand' => NULL,
]; );
$fluent = $conn->update('table', $arr) $fluent = $conn->update('table', $arr)
->setFlag('IGNORE')->setFlag('DELAYED'); ->setFlag('IGNORE')->setFlag('DELAYED');
@@ -22,7 +22,7 @@ Assert::same(
(string) $fluent (string) $fluent
); );
$fluent->set(['another' => 123]); $fluent->set(array('another' => 123));
Assert::same( Assert::same(
reformat('UPDATE IGNORE DELAYED [table] SET [title]=\'Super Product\', [price]=12, [brand]=NULL , [another]=123'), reformat('UPDATE IGNORE DELAYED [table] SET [title]=\'Super Product\', [price]=12, [brand]=NULL , [another]=123'),

View File

@@ -0,0 +1,70 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
class TestClass extends DibiObject
{
public function getBar()
{
return 123;
}
public function isFoo()
{
return 456;
}
}
// calling
Assert::exception(function () {
$obj = new TestClass;
$obj->undeclared();
}, 'LogicException', 'Call to undefined method TestClass::undeclared().');
Assert::exception(function () {
TestClass::undeclared();
}, 'LogicException', 'Call to undefined static method TestClass::undeclared().');
// writing
Assert::exception(function () {
$obj = new TestClass;
$obj->undeclared = 'value';
}, 'LogicException', 'Cannot assign to an undeclared property TestClass::$undeclared.');
// property getter
$obj = new TestClass;
Assert::true(isset($obj->bar));
Assert::same(123, $obj->bar);
Assert::false(isset($obj->foo));
Assert::same(456, $obj->foo);
// reading
Assert::exception(function () {
$obj = new TestClass;
$val = $obj->undeclared;
}, 'LogicException', 'Cannot read an undeclared property TestClass::$undeclared.');
// unset/isset
Assert::exception(function () {
$obj = new TestClass;
unset($obj->undeclared);
}, 'LogicException', 'Cannot unset the property TestClass::$undeclared.');
Assert::false(isset($obj->undeclared));
// extension method
TestClass::extensionMethod('join', $func = function (TestClass $that, $separator) {
return $that->foo . $separator . $that->bar;
});
$obj = new TestClass;
Assert::same('456*123', $obj->join('*'));

View File

@@ -8,7 +8,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$info = $conn->query(' $info = $conn->query('
@@ -20,14 +20,14 @@ $info = $conn->query('
Assert::same( Assert::same(
['product_id', 'order_id', 'name', 'xXx'], array('product_id', 'order_id', 'name', 'xXx'),
$info->getColumnNames() $info->getColumnNames()
); );
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') { if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
Assert::same( Assert::same(
['products.product_id', 'orders.order_id', 'customers.name', 'xXx'], array('products.product_id', 'orders.order_id', 'customers.name', 'xXx'),
$info->getColumnNames(TRUE) $info->getColumnNames(TRUE)
); );
} }
@@ -35,22 +35,22 @@ if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
$columns = $info->getColumns(); $columns = $info->getColumns();
Assert::same('product_id', $columns[0]->getName()); Assert::same('product_id', $columns[0]->name);
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') { if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
Assert::same('products', $columns[0]->getTableName()); Assert::same('products', $columns[0]->tableName);
} }
Assert::null($columns[0]->getVendorInfo('xxx')); Assert::null($columns[0]->getVendorInfo('xxx'));
if ($config['system'] !== 'sqlite') { if ($config['system'] !== 'sqlite') {
Assert::same('i', $columns[0]->getType()); Assert::same('i', $columns[0]->type);
} }
Assert::null($columns[0]->isNullable()); Assert::null($columns[0]->nullable);
Assert::same('xXx', $columns[3]->getName()); Assert::same('xXx', $columns[3]->name);
Assert::null($columns[3]->getTableName()); Assert::null($columns[3]->tableName);
if ($config['system'] !== 'sqlite') { if ($config['system'] !== 'sqlite') {
Assert::same('i', $columns[0]->getType()); Assert::same('i', $columns[0]->type);
} }
Assert::null($columns[3]->isNullable()); Assert::null($columns[3]->nullable);
Assert::same('xXx', $info->getColumn('xxx')->getName()); Assert::same('xXx', $info->getColumn('xxx')->getName());
Assert::same('xXx', $info->getColumn('xXx')->getName()); Assert::same('xXx', $info->getColumn('xXx')->getName());

View File

@@ -0,0 +1,130 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
class MockResult extends DibiResult
{
function __construct()
{}
function test($row)
{
$normalize = new ReflectionMethod('DibiResult', 'normalize');
$normalize->setAccessible(TRUE);
$normalize->invokeArgs($this, array(& $row));
return $row;
}
}
test(function () {
$result = new MockResult;
$result->setType('col', dibi::BOOL);
Assert::same(array('col' => NULL), $result->test(array('col' => NULL)));
Assert::same(array('col' => TRUE), $result->test(array('col' => TRUE)));
Assert::same(array('col' => FALSE), $result->test(array('col' => FALSE)));
Assert::same(array('col' => FALSE), $result->test(array('col' => '')));
Assert::same(array('col' => FALSE), $result->test(array('col' => '0')));
Assert::same(array('col' => TRUE), $result->test(array('col' => '1')));
Assert::same(array('col' => TRUE), $result->test(array('col' => 't')));
Assert::same(array('col' => FALSE), $result->test(array('col' => 'f')));
Assert::same(array('col' => TRUE), $result->test(array('col' => 'T')));
Assert::same(array('col' => FALSE), $result->test(array('col' => 'F')));
Assert::same(array('col' => FALSE), $result->test(array('col' => 0)));
Assert::same(array('col' => FALSE), $result->test(array('col' => 0.0)));
Assert::same(array('col' => TRUE), $result->test(array('col' => 1)));
Assert::same(array('col' => TRUE), $result->test(array('col' => 1.0)));
});
test(function () {
$result = new MockResult;
$result->setType('col', dibi::TEXT); // means TEXT or UNKNOWN
Assert::same(array('col' => NULL), $result->test(array('col' => NULL)));
Assert::same(array('col' => TRUE), $result->test(array('col' => TRUE)));
Assert::same(array('col' => FALSE), $result->test(array('col' => FALSE)));
Assert::same(array('col' => ''), $result->test(array('col' => '')));
Assert::same(array('col' => '0'), $result->test(array('col' => '0')));
Assert::same(array('col' => '1'), $result->test(array('col' => '1')));
Assert::same(array('col' => 0), $result->test(array('col' => 0)));
Assert::same(array('col' => 1), $result->test(array('col' => 1)));
});
test(function () {
$result = new MockResult;
$result->setType('col', dibi::FLOAT);
Assert::same(array('col' => NULL), $result->test(array('col' => NULL)));
Assert::same(array('col' => 1.0), $result->test(array('col' => TRUE)));
Assert::same(array('col' => 0.0), $result->test(array('col' => FALSE)));
Assert::same(array('col' => 0.0), $result->test(array('col' => '')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '.0')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '.1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0.0')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '0.1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0.000')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '0.100')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1.0')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '1.1')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1.000')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '1.100')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '001.000')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '001.100')));
Assert::same(array('col' => 10.0), $result->test(array('col' => '10')));
Assert::same(array('col' => 11.0), $result->test(array('col' => '11')));
Assert::same(array('col' => 10.0), $result->test(array('col' => '0010')));
Assert::same(array('col' => 11.0), $result->test(array('col' => '0011')));
Assert::same(array('col' => '0.00000000000000000001'), $result->test(array('col' => '0.00000000000000000001')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '12345678901234567890')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '012345678901234567890')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '12345678901234567890.000')));
Assert::same(array('col' => '12345678901234567890.1'), $result->test(array('col' => '012345678901234567890.100')));
Assert::same(array('col' => 0.0), $result->test(array('col' => 0)));
Assert::same(array('col' => 0.0), $result->test(array('col' => 0.0)));
Assert::same(array('col' => 1.0), $result->test(array('col' => 1)));
Assert::same(array('col' => 1.0), $result->test(array('col' => 1.0)));
setlocale(LC_ALL, 'de_DE@euro', 'de_DE', 'deu_deu');
Assert::same(array('col' => 0.0), $result->test(array('col' => '')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '.0')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '.1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0.0')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '0.1')));
Assert::same(array('col' => 0.0), $result->test(array('col' => '0.000')));
Assert::same(array('col' => 0.1), $result->test(array('col' => '0.100')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1.0')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '1.1')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '1.000')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '1.100')));
Assert::same(array('col' => 1.0), $result->test(array('col' => '001.000')));
Assert::same(array('col' => 1.1), $result->test(array('col' => '001.100')));
Assert::same(array('col' => 10.0), $result->test(array('col' => '10')));
Assert::same(array('col' => 11.0), $result->test(array('col' => '11')));
Assert::same(array('col' => 10.0), $result->test(array('col' => '0010')));
Assert::same(array('col' => 11.0), $result->test(array('col' => '0011')));
Assert::same(array('col' => '0.00000000000000000001'), $result->test(array('col' => '0.00000000000000000001')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '12345678901234567890')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '012345678901234567890')));
Assert::same(array('col' => '12345678901234567890'), $result->test(array('col' => '12345678901234567890.000')));
Assert::same(array('col' => '12345678901234567890.1'), $result->test(array('col' => '012345678901234567890.100')));
Assert::same(array('col' => 0.0), $result->test(array('col' => 0)));
Assert::same(array('col' => 0.0), $result->test(array('col' => 0.0)));
Assert::same(array('col' => 1.0), $result->test(array('col' => 1)));
Assert::same(array('col' => 1.0), $result->test(array('col' => 1.0)));
setlocale(LC_NUMERIC, 'C');
});

View File

@@ -4,15 +4,15 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
$res = $conn->query('SELECT * FROM [customers]'); $res = $conn->query('SELECT * FROM [customers]');
// auto-converts this column to integer // auto-converts this column to integer
$res->setType('customer_id', Dibi\Type::DATETIME, 'H:i j.n.Y'); $res->setType('customer_id', Dibi::DATETIME, 'H:i j.n.Y');
Assert::equal(new Dibi\Row([ Assert::equal(new DibiRow(array(
'customer_id' => new Dibi\DateTime('1970-01-01 01:00:01'), 'customer_id' => new DibiDateTime('1970-01-01 01:00:01'),
'name' => 'Dave Lister', 'name' => 'Dave Lister',
]), $res->fetch()); )), $res->fetch());

View File

@@ -8,7 +8,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql"); $conn->loadFile(__DIR__ . "/data/$config[system].sql");
@@ -24,29 +24,19 @@ Assert::true(isset($row['title']));
// missing // missing
Assert::error(function () use ($row) { Assert::error(function () use ($row) {
$x = $row->missing; $x = $row->missing;
}, E_USER_NOTICE, "Attempt to read missing column 'missing'."); }, E_NOTICE, 'Undefined property: DibiRow::$missing');
Assert::error(function () use ($row) { Assert::error(function () use ($row) {
$x = $row['missing']; $x = $row['missing'];
}, E_USER_NOTICE, "Attempt to read missing column 'missing'."); }, E_NOTICE, 'Undefined property: DibiRow::$missing');
Assert::false(isset($row->missing)); Assert::false(isset($row->missing));
Assert::false(isset($row['missing'])); Assert::false(isset($row['missing']));
// suggestions
Assert::error(function () use ($row) {
$x = $row->tilte;
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
Assert::error(function () use ($row) {
$x = $row['tilte'];
}, E_USER_NOTICE, "Attempt to read missing column 'tilte', did you mean 'title'?");
// to array // to array
Assert::same(['product_id' => num(1), 'title' => 'Chair'], iterator_to_array($row)); Assert::same(array('product_id' => num(1), 'title' => 'Chair'), iterator_to_array($row));
Assert::same(['product_id' => num(1), 'title' => 'Chair'], $row->toArray()); Assert::same(array('product_id' => num(1), 'title' => 'Chair'), $row->toArray());
// counting // counting
Assert::same(2, count($row)); Assert::same(2, count($row));

View File

@@ -8,8 +8,8 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
$translator = new Dibi\Translator($conn); $translator = new DibiTranslator($conn);
$datetime = new DateTime('1978-01-23 00:00:00'); $datetime = new DateTime('1978-01-23 00:00:00');

View File

@@ -8,7 +8,7 @@ use Tester\Assert;
require __DIR__ . '/bootstrap.php'; require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config); $conn = new DibiConnection($config);
// if & end // if & end

Some files were not shown because too many files have changed in this diff Show More