1
0
mirror of https://github.com/dg/dibi.git synced 2025-08-30 17:29:53 +02:00

Compare commits

..

21 Commits

Author SHA1 Message Date
David Grudl
17df4d17fb Released version 2.2.5
This release marks the end of life of 2.2 series.
2015-10-26 19:33:34 +01:00
David Grudl
51adcd98e0 DibiFluent::fetch() uses limit only when there is no LIMIT & OFFSET (fixes 20f2093 on MSSQL) 2015-10-26 16:46:20 +01:00
David Grudl
aaa5abd4b6 DibiFluent: prevents doubled processing 2015-10-26 16:45:58 +01:00
David Grudl
e4acc4a2c8 DibiTranslator: removed die() 2015-10-26 16:45:21 +01:00
David Grudl
0a2d12c47a Released version 2.2.4
This release marks the end of life of 2.2 series.
2015-10-22 02:50:29 +02:00
David Grudl
19748f7c5b Result: improved normalization of int 2015-10-13 15:16:56 +02:00
David Grudl
ee5f1dd293 Result: fixed normalization of float when ends with "0" [Closes #189] 2015-10-13 15:16:54 +02:00
castamir
f4ae1e692f DibiFluent::fetch(): fixed limit clause duplication [Closes #188][Closes #186][Closes #185] 2015-10-09 12:02:02 +02:00
David Grudl
443661f0e9 DibiFluent: removed keyword AS from SQL [Closes #172] 2015-10-09 12:01:40 +02:00
David Grudl
ac0ab9041b DibiObject: fixed compatibility with PHP 7 2015-10-09 12:01:03 +02:00
David Grudl
d7885921e6 removed rarely used @property 2015-10-09 12:01:03 +02:00
David Grudl
ce459f440a improved coding style
# Conflicts:
#	dibi/bridges/Tracy/Panel.php
#	dibi/drivers/DibiPdoDriver.php
#	dibi/drivers/DibiPostgreDriver.php
#	tests/dibi/DataSource.phpt
#	tests/dibi/DibiConnection.connect.phpt
#	tests/dibi/DibiConnection.transactions.phpt
#	tests/dibi/DibiFluent.cloning.phpt
#	tests/dibi/DibiFluent.insert.phpt
#	tests/dibi/DibiFluent.select.phpt
#	tests/dibi/DibiFluent.update.phpt
#	tests/dibi/DibiTranslator.conditions.phpt
#	tests/dibi/DibiTranslator.phpt
#	tests/dibi/PdoMssql.limits.phpt
#	tests/dibi/Postgre.like.phpt
2015-10-09 12:01:02 +02:00
David Grudl
df37a500fa used https 2015-10-05 16:16:31 +02:00
Miloslav Hůla
8a5eddfabc Postgre: fixed %like escaping [Closes #159] 2015-01-25 17:26:08 +01:00
David Grudl
a57f3dfc83 Released version 2.2.3 2015-01-13 06:07:08 +01:00
David Grudl
2702de6ccb composer.json: removed branch alias 2015-01-13 06:06:10 +01:00
David Grudl
73e1f366d4 removed tests 2015-01-13 06:05:14 +01:00
MartyIX
656cbfc40c DibiFluent: add leftJoin and on to phpdoc. 2015-01-13 06:02:58 +01:00
Petr BAGR Smrkovský
e778096641 DibiResult: float detection locale fix [Closes #154] 2015-01-13 06:01:54 +01:00
David Grudl
68521d69e3 * .travis: added code checker 2015-01-13 06:01:54 +01:00
David Grudl
a68886f51d examples: improved Tracy examples 2015-01-13 06:01:54 +01:00
127 changed files with 4100 additions and 7528 deletions

2
.gitattributes vendored
View File

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

View File

@@ -1,36 +0,0 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
allow_failures:
- php: hhvm
script:
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini
- php temp/code-checker/src/code-checker.php --short-arrays
after_failure:
# Print *.actual content
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
before_script:
# Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
# Create databases.ini
- cp ./tests/databases.travis.ini ./tests/databases.ini
# Create Postgre database
- psql -c 'CREATE DATABASE dibi_test' -U postgres
sudo: false
cache:
directories:
- $HOME/.composer/cache

View File

@@ -1,59 +0,0 @@
build: off
cache:
- c:\php5 -> appveyor.yml
- c:\php7 -> appveyor.yml
- '%LOCALAPPDATA%\Composer\files -> appveyor.yml'
clone_folder: c:\projects\dibi
services:
- mssql2012sp1
- mssql2014
- mysql
init:
- SET PATH=c:\php5;%PATH%
- SET ANSICON=121x90 (121x90)
install:
# Install PHP 5
- IF EXIST c:\php5 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 mkdir c:\php5
- IF %PHP%==1 cd c:\php5
- 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 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_56_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 copy SQLSRV\php_pdo_sqlsrv_56_ts.dll ext\php_pdo_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install PHP 7
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 mkdir c:\php7
- IF %PHP%==1 cd c:\php7
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip
- IF %PHP%==1 7z x php-7.0.3-Win32-VC14-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_7_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install Nette Tester
- cd c:\projects\dibi
- 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 c:\php5\php -c tests\php5-win.ini
- vendor\bin\tester tests -s -p c:\php7\php -c tests\php7-win.ini
on_failure:
# Print *.actual content
- type tests\dibi\output\*.actual

View File

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

View File

@@ -1,27 +0,0 @@
How to contribute & use the issue tracker
=========================================
The issue tracker is the preferred channel for bug reports, features requests
and submitting pull requests, but please respect the following restrictions:
* Please **do not** use the issue tracker for personal support requests (use
[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
respect the opinions of others.
* Use the GitHub **issue search** — check if the issue has already been
reported.
A good **bug report** shouldn't leave others needing to chase you up for more
information. Please try to be as detailed as possible in your report.
**Feature requests** are welcome. But take a moment to find out whether your idea
fits with the scope and aims of the project. It's up to *you* to make a strong
case to convince the project's developers of the merits of this feature.
We welcome **pull requests**. If you'd like to contribute, please take a moment
to [read the guidelines](https://nette.org/en/contributing) in order to make
the contribution process easy and effective for everyone involved.
Thanks!

View File

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

View File

@@ -0,0 +1,17 @@
# Requires Nette Framework 2.0 for PHP 5.3
#
# In bootstrap.php append these lines after line $configurator = new Nette\Config\Configurator;
#
# $configurator->onCompile[] = function($configurator, $compiler) {
# $compiler->addExtension('dibi', new DibiNette20Extension);
# };
#
# This will create service named 'dibi.connection'.
common:
dibi:
host: localhost
username: root
password: ***
database: foo
lazy: TRUE

View File

@@ -5,15 +5,15 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Nette;
/**
* Dibi extension for Nette Framework 2.1. Creates 'connection' service.
*
* @author David Grudl
* @package dibi\nette
* @phpversion 5.3
*/
class DibiExtension21 extends Nette\DI\CompilerExtension
class DibiNette21Extension extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
@@ -36,16 +36,15 @@ class DibiExtension21 extends Nette\DI\CompilerExtension
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
->setClass('DibiConnection', array($config));
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Nette\Panel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self'])
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']);
->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', array('@self'))
->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,22 @@
* 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;
if (interface_exists('Nette\Diagnostics\IBarPanel')) {
class_alias('Nette\Diagnostics\IBarPanel', 'IBarPanel');
}
/**
* Dibi panel for Nette\Diagnostics.
*
* @author David Grudl
* @package dibi\nette
*/
class Panel implements Nette\Diagnostics\IBarPanel
class DibiNettePanel extends DibiObject implements IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
static public $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -31,21 +29,36 @@ class Panel implements Nette\Diagnostics\IBarPanel
public $filter;
/** @var array */
private $events = [];
private $events = array();
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
public function register(DibiConnection $connection)
{
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
if (is_callable('Nette\Diagnostics\Debugger::enable') && !class_exists('NDebugger')) {
class_alias('Nette\Diagnostics\Debugger', 'NDebugger'); // PHP 5.2 code compatibility
}
if (is_callable('NDebugger::enable') && is_callable('NDebugger::getBlueScreen')) { // Nette Framework 2.1
NDebugger::getBar()->addPanel($this);
NDebugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('NDebugger::enable')) { // Nette Framework 2.0 (for PHP 5.3 or PHP 5.2 prefixed)
NDebugger::$bar && NDebugger::$bar->addPanel($this);
NDebugger::$blueScreen && NDebugger::$blueScreen->addPanel(array(__CLASS__, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('Debugger::enable') && !is_callable('Debugger::getBlueScreen')) { // Nette Framework 2.0 for PHP 5.2 non-prefixed
Debugger::$bar && Debugger::$bar->addPanel($this);
Debugger::$blueScreen && Debugger::$blueScreen->addPanel(array(__CLASS__, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
}
}
@@ -53,7 +66,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
* After event notification.
* @return void
*/
public function logEvent(Event $event)
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -68,11 +81,11 @@ class Panel implements Nette\Diagnostics\IBarPanel
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
if ($e instanceof DibiException && $e->getSql()) {
return array(
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
'panel' => dibi::dump($e->getSql(), TRUE),
);
}
}
@@ -105,15 +118,15 @@ class Panel implements Nette\Diagnostics\IBarPanel
foreach ($this->events as $event) {
$totalTime += $event->time;
$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 {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $e) {
}
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
@@ -123,7 +136,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 .= '</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) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
@@ -132,7 +145,7 @@ class Panel implements Nette\Diagnostics\IBarPanel
if (!class_exists($helpers)) {
$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>";

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,12 +7,15 @@
namespace Dibi\Bridges\Nette;
use Dibi;
use dibi;
use Nette;
/**
* Dibi extension for Nette Framework 2.2. Creates 'connection' & 'panel' services.
*
* @author David Grudl
* @package dibi\nette
*/
class DibiExtension22 extends Nette\DI\CompilerExtension
{
@@ -37,22 +40,12 @@ class DibiExtension22 extends Nette\DI\CompilerExtension
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
->setClass('DibiConnection', array($config));
if (class_exists('Tracy\Debugger')) {
$connection->addSetup(
[new Nette\DI\Statement('Tracy\Debugger::getBlueScreen'), 'addPanel'],
[['Dibi\Bridges\Tracy\Panel', 'renderException']]
);
}
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Tracy\Panel', [
isset($config['explain']) ? $config['explain'] : TRUE,
isset($config['filter']) && $config['filter'] === FALSE ? Dibi\Event::ALL : Dibi\Event::QUERY,
]);
$connection->addSetup([$panel, 'register'], [$connection]);
->setClass('Dibi\Bridges\Tracy\Panel');
$connection->addSetup(array($panel, 'register'), array($connection));
}
}

View File

@@ -7,21 +7,19 @@
namespace Dibi\Bridges\Tracy;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use dibi;
use Tracy;
/**
* Dibi panel for Tracy.
* @package dibi\nette
* @author David Grudl
*/
class Panel implements Tracy\IBarPanel
class Panel extends \DibiObject implements Tracy\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
static public $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -30,21 +28,21 @@ class Panel implements Tracy\IBarPanel
public $filter;
/** @var array */
private $events = [];
private $events = array();
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->filter = $filter ? (int) $filter : \DibiEvent::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
public function register(\DibiConnection $connection)
{
Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
Tracy\Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = array($this, 'logEvent');
}
@@ -52,7 +50,7 @@ class Panel implements Tracy\IBarPanel
* After event notification.
* @return void
*/
public function logEvent(Event $event)
public function logEvent(\DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -67,11 +65,11 @@ class Panel implements Tracy\IBarPanel
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
if ($e instanceof \DibiException && $e->getSql()) {
return array(
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
'panel' => dibi::dump($e->getSql(), TRUE),
);
}
}
@@ -83,14 +81,13 @@ class Panel implements Tracy\IBarPanel
public function getTab()
{
$totalTime = 0;
$count = count($this->events);
foreach ($this->events as $event) {
$totalTime += $event->time;
}
return '<span title="dibi"><svg viewBox="0 0 2048 2048" style="vertical-align: bottom; width:1.23em; height:1.55em"><path fill="' . ($count ? '#b079d6' : '#aaa') . '" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/></svg><span class="tracy-label">'
. $count . ' queries'
return '<span title="dibi"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC" />'
. count($this->events) . ' queries'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
. '</span></span>';
. '</span>';
}
@@ -105,15 +102,15 @@ class Panel implements Tracy\IBarPanel
foreach ($this->events as $event) {
$totalTime += $event->time;
$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 {
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (\DibiException $e) {
}
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
}
$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 .= '</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) {
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
}

View File

@@ -1,3 +1,39 @@
<?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';
if (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPanel')) {
require_once dirname(__FILE__) . '/bridges/Nette-2.1/DibiNettePanel.php';
}

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Firebird/InterBase database.
@@ -20,12 +16,13 @@ use Dibi;
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author Tomáš Kraina, Roman Sklenář
* @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;
/** @var resource Connection resource */
@@ -45,12 +42,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +55,37 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
Dibi\Helpers::alias($config, 'database', 'db');
DibiConnection::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$config += [
$config += array(
'username' => ini_get('ibase.default_password'),
'password' => ini_get('ibase.default_user'),
'database' => ini_get('ibase.default_db'),
'charset' => ini_get('ibase.default_charset'),
'buffers' => 0,
];
);
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, ibase_errcode());
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
}
}
}
@@ -103,22 +104,27 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if ($res === FALSE) {
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
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 {
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)) {
return $this->createResultDriver($res);
@@ -151,14 +157,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($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;
}
@@ -167,16 +173,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($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)) {
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;
@@ -187,16 +193,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($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)) {
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;
@@ -225,7 +231,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -236,7 +242,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -251,48 +257,34 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Encodes data for use in a SQL statement.
* @param string
* @return string
* @param mixed value
* @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)
{
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'");
public function escapeIdentifier($value)
{
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);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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 +296,23 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function escapeLike($value, $pos)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -359,7 +348,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
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 +359,16 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function fetch($assoc)
{
DibiDriverException::tryError();
$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) {
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 {
throw new Dibi\DriverException($msg, ibase_errcode());
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
}
}
@@ -390,11 +380,11 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* 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 Dibi\Exception
* @throws DibiException
*/
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.');
}
@@ -411,7 +401,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the result set resource.
* @return resource
* @return mysqli_result
*/
public function getResultResource()
{
@@ -427,21 +417,21 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
];
);
}
return $columns;
}
/********************* Dibi\Reflector ********************/
/********************* IDibiReflector ********************/
/**
@@ -456,12 +446,12 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'TRUE',
];
);
}
return $tables;
}
@@ -505,10 +495,10 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = [
$columns[$key] = array(
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
@@ -516,7 +506,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE,
];
);
}
return $columns;
}
@@ -543,7 +533,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
@@ -573,14 +563,14 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = [];
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = [
$keys[$key] = array(
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
];
);
}
return $keys;
}
@@ -600,7 +590,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = [];
$indices = array();
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
@@ -624,7 +614,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = [];
$constraints = array();
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
@@ -667,15 +657,15 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = [];
$triggers = array();
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = [
$triggers[$row['TRIGGER_NAME']] = array(
'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']),
'event' => trim($row['TRIGGER_EVENT']),
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
];
);
}
return $triggers;
}
@@ -695,7 +685,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = [];
$triggers = array();
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
@@ -742,7 +732,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = [];
$procedures = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
@@ -766,7 +756,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = [];
$procedures = array();
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
@@ -785,7 +775,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = [];
$generators = array();
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
@@ -804,7 +794,7 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = [];
$functions = array();
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
@@ -812,3 +802,41 @@ class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
}
/**
* Database procedure exception.
*
* @author Roman Sklenář
* @copyright Copyright (c) 2010
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}

View File

@@ -5,15 +5,12 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use Dibi\Connection;
use Dibi\Helpers;
require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
/**
* The dibi driver for Microsoft SQL Server and SQL Azure databases.
* The dibi driver for MS SQL Driver 2005 database.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
@@ -23,12 +20,13 @@ use Dibi\Helpers;
* - 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)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -41,17 +39,14 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $version;
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
@@ -59,37 +54,31 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset');
DibiConnection::alias($config, 'options|UID', 'username');
DibiConnection::alias($config, 'options|PWD', 'password');
DibiConnection::alias($config, 'options|Database', 'database');
DibiConnection::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$options = $config['options'];
// Default values
if (!isset($options['CharacterSet'])) {
$options['CharacterSet'] = 'UTF-8';
if (!isset($config['options']['CharacterSet'])) {
$config['options']['CharacterSet'] = 'UTF-8';
}
$options['PWD'] = (string) $options['PWD'];
$options['UID'] = (string) $options['UID'];
$options['Database'] = (string) $options['Database'];
$this->connection = sqlsrv_connect($config['host'], $options);
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
}
if (!is_resource($this->connection)) {
$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'];
}
@@ -106,8 +95,8 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -116,7 +105,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
if ($res === FALSE) {
$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)) {
$this->affectedRows = sqlsrv_rows_affected($res);
@@ -141,7 +130,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getInsertId($sequence)
{
$res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0];
@@ -154,7 +143,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -166,7 +155,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -178,7 +167,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -198,18 +187,18 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new SqlsrvReflector($this);
return new DibiMssql2005Reflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -225,48 +214,34 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* 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 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)
{
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'");
public function escapeIdentifier($value)
{
// @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);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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'");
}
@@ -278,27 +253,24 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -308,23 +280,13 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') AS T ';
}
} elseif (version_compare($this->version, 11, '<')) { // 11 == SQL Server 2012
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = sprintf('SELECT TOP (%d) * FROM (%s) t', $limit, $sql);
}
} elseif ($limit !== NULL) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
} elseif ($offset) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -348,7 +310,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
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.');
}
@@ -370,7 +332,7 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -391,13 +353,13 @@ class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getResultColumns()
{
$columns = [];
$columns = array();
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = [
$columns[] = array(
'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'],
];
);
}
return $columns;
}

View File

@@ -5,24 +5,21 @@
* 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.
*
* @author Daniel Kouba
* @package dibi\drivers
* @internal
*/
class SqlsrvReflector implements Dibi\Reflector
class DibiMsSql2005Reflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -34,13 +31,13 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getTables()
{
$res = $this->driver->query("SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE [TABLE_SCHEMA] = 'dbo'");
$tables = [];
$res = $this->driver->query('SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES');
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
);
}
return $tables;
}
@@ -57,10 +54,10 @@ class SqlsrvReflector implements Dibi\Reflector
SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT
FROM sys.columns c
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)) {
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
}
@@ -78,11 +75,11 @@ class SqlsrvReflector implements Dibi\Reflector
And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
And CCU.COLUMN_NAME = C.COLUMN_NAME
) 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)) {
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => strtoupper($row['DATA_TYPE']),
@@ -92,7 +89,7 @@ class SqlsrvReflector implements Dibi\Reflector
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
'vendor' => $row,
];
);
}
return $columns;
}
@@ -105,19 +102,19 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getIndexes($table)
{
$keyUsagesRes = $this->driver->query(sprintf("EXEC [sys].[sp_helpindex] @objname = N%s", $this->driver->escapeText($table)));
$keyUsages = [];
$keyUsagesRes = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$keyUsages = array();
while ($row = $keyUsagesRes->fetch(TRUE)) {
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']);
$keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME'];
}
$res = $this->driver->query("SELECT [i].* FROM [sys].[indexes] [i] INNER JOIN [sys].[tables] [t] ON [i].[object_id] = [t].[object_id] WHERE [t].[name] = {$this->driver->escapeText($table)}");
$indexes = [];
$res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = $row['is_unique'] === 1;
$indexes[$row['name']]['primary'] = $row['is_primary_key'] === 1;
$indexes[$row['name']]['columns'] = isset($keyUsages[$row['name']]) ? $keyUsages[$row['name']] : [];
$indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME'];
$indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE';
$indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : array();
}
return array_values($indexes);
}
@@ -130,7 +127,7 @@ class SqlsrvReflector implements Dibi\Reflector
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -5,10 +5,7 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
/**
* The dibi driver for MS SQL database.
@@ -20,12 +17,13 @@ use Dibi;
* - database => the database name to select
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -37,12 +35,12 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
@@ -50,7 +48,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
@@ -63,11 +61,11 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException("Can't connect to DB.");
throw new DibiDriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @
throw new Dibi\DriverException("Can't select DB '$config[database]'.");
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]'.");
}
}
@@ -85,15 +83,15 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) {
throw new Dibi\DriverException(mssql_get_last_message(), 0, $sql);
throw new DibiDriverException(mssql_get_last_message(), 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -130,7 +128,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -142,7 +140,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -154,7 +152,7 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -174,18 +172,18 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MsSqlReflector($this);
return new DibiMsSqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -201,48 +199,34 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* 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 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(array('[', ']'), array('[[', ']]'), $value) . ']';
public function escapeBinary($value)
{
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'");
public function escapeIdentifier($value)
{
// @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);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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'");
}
@@ -254,27 +238,24 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -284,15 +265,14 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -359,15 +339,15 @@ class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = [
$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;
}

View File

@@ -2,27 +2,27 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*
* Copyright (c) 2005, 2010 David Grudl (https://davidgrudl.com)
*
* @package dibi\drivers
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MS SQL databases.
* The dibi reflector for MsSQL databases.
*
* @author Steven Bredenberg
* @package dibi\drivers
* @internal
*/
class MsSqlReflector implements Dibi\Reflector
class DibiMsSqlReflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -38,12 +38,12 @@ class MsSqlReflector implements Dibi\Reflector
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
');
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
);
}
return $tables;
}
@@ -62,13 +62,13 @@ class MsSqlReflector implements Dibi\Reflector
$result = $this->driver->query("
SELECT MAX(rowcnt)
FROM sys.sysindexes
WHERE id=OBJECT_ID({$this->driver->escapeIdentifier($table)})
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->escapeIdentifier($table)}")->fetch(FALSE);
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escape($table, dibi::IDENTIFIER)}")->fetch(FALSE);
$count = intval($row[0]);
} else {
$count = FALSE;
@@ -91,22 +91,22 @@ class MsSqlReflector implements Dibi\Reflector
$res = $this->driver->query("
SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = {$this->driver->escapeText($table)}
WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
ORDER BY TABLE_NAME, ORDINAL_POSITION
");
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = FALSE;
$type = strtoupper($row['DATA_TYPE']);
$size_cols = [
$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]) {
@@ -114,7 +114,7 @@ class MsSqlReflector implements Dibi\Reflector
}
}
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $type,
@@ -124,7 +124,7 @@ class MsSqlReflector implements Dibi\Reflector
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => FALSE,
'vendor' => $row,
];
);
}
return $columns;
@@ -148,22 +148,22 @@ class MsSqlReflector implements Dibi\Reflector
(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->escapeText($table)}
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 = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$index_name = $row['index_name'];
if (!isset($indexes[$index_name])) {
$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'] = [];
$indexes[$index_name]['columns'] = array();
}
$indexes[$index_name]['columns'][] = $row['column_name'];
}
@@ -191,18 +191,18 @@ class MsSqlReflector implements Dibi\Reflector
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->escapeText($table)}
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escape($table, dibi::TEXT)}
");
$keys = [];
$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'] = [$row['column_name']]; // local columns
$keys[$key_name]['local'] = array($row['column_name']); // local columns
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
$keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns
$keys[$key_name]['foreign'] = array($row['reference_column_name']); // referenced columns
$keys[$key_name]['onDelete'] = FALSE;
$keys[$key_name]['onUpdate'] = FALSE;
} else {

View File

@@ -5,9 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/**
@@ -26,12 +25,13 @@ use Dibi;
* - 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
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @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_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
@@ -50,12 +50,12 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +63,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
@@ -72,13 +72,13 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
} else {
// default values
Dibi\Helpers::alias($config, 'flags', 'options');
$config += [
DibiConnection::alias($config, 'flags', 'options');
$config += array(
'charset' => 'utf8',
'timezone' => date('P'),
'username' => ini_get('mysql.default_user'),
'password' => ini_get('mysql.default_password'),
];
);
if (!isset($config['host'])) {
$host = ini_get('mysql.default_host');
if ($host) {
@@ -106,18 +106,23 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
}
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 (!@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]'");
}
}
if (isset($config['database'])) {
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 +151,8 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -157,8 +162,8 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
}
if ($code = mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql);
if (mysql_errno($this->connection)) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -172,10 +177,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function getInfo()
{
$res = [];
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
throw new DibiPcreException;
}
foreach ($matches as $m) {
@@ -209,7 +214,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -221,7 +226,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -233,7 +238,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -253,18 +258,18 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MySqlReflector($this);
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -280,54 +285,42 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* 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 escapeText($value)
public function escape($value, $type)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
switch ($type) {
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 +339,17 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -369,13 +359,10 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
if ($limit >= 0 || $offset > 0) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -400,7 +387,7 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount()
{
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);
}
@@ -421,12 +408,12 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
* 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 Dibi\Exception
* @throws DibiException
*/
public function seek($row)
{
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);
@@ -451,17 +438,16 @@ class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => strtoupper($row['type']),
'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row,
];
);
}
return $columns;
}

View File

@@ -5,24 +5,21 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MySQL databases.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class MySqlReflector implements Dibi\Reflector
class DibiMySqlReflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -34,13 +31,18 @@ class MySqlReflector implements Dibi\Reflector
*/
public function getTables()
{
/*$this->query("
SELECT TABLE_NAME as name, TABLE_TYPE = 'VIEW' as view
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query('SHOW FULL TABLES');
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
);
}
return $tables;
}
@@ -53,11 +55,17 @@ class MySqlReflector implements Dibi\Reflector
*/
public function getColumns($table)
{
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}");
$columns = [];
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
");*/
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$columns[] = [
$columns[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
@@ -67,7 +75,7 @@ class MySqlReflector implements Dibi\Reflector
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row,
];
);
}
return $columns;
}
@@ -80,8 +88,15 @@ class MySqlReflector implements Dibi\Reflector
*/
public function getIndexes($table)
{
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escapeIdentifier($table)}");
$indexes = [];
/*$table = $this->escape($table, dibi::TEXT);
$this->query("
SELECT *
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE TABLE_NAME = $table AND TABLE_SCHEMA = DATABASE()
AND REFERENCED_COLUMN_NAME IS NULL
");*/
$res = $this->driver->query("SHOW INDEX FROM {$this->driver->escape($table, dibi::IDENTIFIER)}");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['Key_name']]['name'] = $row['Key_name'];
$indexes[$row['Key_name']]['unique'] = !$row['Non_unique'];
@@ -96,13 +111,13 @@ class MySqlReflector implements Dibi\Reflector
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
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') {
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("
@@ -114,11 +129,11 @@ class MySqlReflector implements Dibi\Reflector
kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
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
");
$foreignKeys = [];
$foreignKeys = array();
while ($row = $res->fetch(TRUE)) {
$keyName = $row['CONSTRAINT_NAME'];

View File

@@ -5,9 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
/**
@@ -27,12 +26,13 @@ use Dibi;
* - 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
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @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_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
@@ -51,12 +51,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +64,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
@@ -74,14 +74,14 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
} else {
// default values
$config += [
$config += array(
'charset' => 'utf8',
'timezone' => date('P'),
'username' => ini_get('mysqli.default_user'),
'password' => ini_get('mysqli.default_pw'),
'socket' => (string) ini_get('mysqli.default_socket'),
'socket' => ini_get('mysqli.default_socket'),
'port' => NULL,
];
);
if (!isset($config['host'])) {
$host = ini_get('mysqli.default_host');
if ($host) {
@@ -110,7 +110,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 @
if ($errno = mysqli_connect_errno()) {
throw new Dibi\DriverException(mysqli_connect_error(), $errno);
throw new DibiDriverException(mysqli_connect_error(), $errno);
}
}
@@ -145,15 +145,15 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql);
if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
@@ -161,36 +161,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 (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.
* @return array
*/
public function getInfo()
{
$res = [];
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
throw new DibiPcreException;
}
foreach ($matches as $m) {
@@ -224,7 +204,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -236,7 +216,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -248,7 +228,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -268,20 +248,20 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new MySqlReflector($this);
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver(\mysqli_result $resource)
public function createResultDriver(mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -295,47 +275,35 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* 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 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)
{
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
case dibi::IDENTIFIER:
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
public function escapeIdentifier($value)
{
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 escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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,20 +322,17 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -377,13 +342,10 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
if ($limit >= 0 || $offset > 0) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -408,7 +370,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getRowCount()
{
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);
}
@@ -429,12 +391,12 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
* 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 Dibi\Exception
* @throws DibiException
*/
public function seek($row)
{
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);
}
@@ -458,10 +420,9 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
static $types;
if ($types === NULL) {
if (empty($types)) {
$consts = get_defined_constants(TRUE);
$types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
foreach ($consts['mysqli'] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12);
}
@@ -470,17 +431,16 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
}
$count = mysqli_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => $row['name'],
'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'],
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : NULL,
'nativetype' => $types[$row['type']],
'vendor' => $row,
];
);
}
return $columns;
}
@@ -493,7 +453,7 @@ class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
return $this->resultSet === NULL || $this->resultSet->type === NULL ? NULL : $this->resultSet;
}
}

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver interacting with databases via ODBC connections.
@@ -19,12 +15,13 @@ use Dibi;
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @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 */
private $connection;
@@ -42,12 +39,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +52,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
@@ -63,11 +60,11 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$this->connection = $config['resource'];
} else {
// default values
$config += [
$config += array(
'username' => ini_get('odbc.default_user'),
'password' => ini_get('odbc.default_pw'),
'dsn' => ini_get('odbc.default_db'),
];
);
if (empty($config['persistent'])) {
$this->connection = @odbc_connect($config['dsn'], $config['username'], $config['password']); // intentionally @
@@ -77,7 +74,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
}
}
@@ -95,8 +92,8 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -104,7 +101,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @odbc_exec($this->connection, $sql); // intentionally @
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)) {
$this->affectedRows = odbc_num_rows($res);
@@ -129,7 +126,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getInsertId($sequence)
{
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.');
throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
}
@@ -137,12 +134,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
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 +148,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
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);
}
@@ -166,12 +163,12 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
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);
}
@@ -199,7 +196,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -210,7 +207,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -226,47 +223,33 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* 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 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)
{
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 ? "#m/d/Y H:i:s#" : "#m/d/Y#");
public function escapeIdentifier($value)
{
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);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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 +261,24 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -308,14 +288,13 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function applyLimit(& $sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
}
}
@@ -359,7 +338,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
return FALSE;
}
$count = odbc_num_fields($set);
$cols = [];
$cols = array();
for ($i = 1; $i <= $count; $i++) {
$cols[] = odbc_result($set, $i);
}
@@ -398,14 +377,14 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$columns[] = [
$columns[] = array(
'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype' => odbc_field_type($this->resultSet, $i),
];
);
}
return $columns;
}
@@ -422,7 +401,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -432,13 +411,13 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables()
{
$res = odbc_tables($this->connection);
$tables = [];
$tables = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = [
$tables[] = array(
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
];
);
}
}
odbc_free_result($res);
@@ -454,17 +433,17 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getColumns($table)
{
$res = odbc_columns($this->connection);
$columns = [];
$columns = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$columns[] = [
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $row['TYPE_NAME'],
'size' => $row['COLUMN_SIZE'],
'nullable' => (bool) $row['NULLABLE'],
'default' => $row['COLUMN_DEF'],
];
);
}
}
odbc_free_result($res);
@@ -479,7 +458,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -490,7 +469,7 @@ class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
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)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Oracle database.
@@ -18,17 +14,17 @@ use Dibi;
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - schema => alters session schema
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource
* - 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
*
* @author David Grudl
* @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 */
private $connection;
@@ -46,12 +42,12 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
@@ -77,11 +73,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
if (!$this->connection) {
$err = oci_error();
throw new Dibi\DriverException($err['message'], $err['code']);
}
if (isset($config['schema'])) {
$this->query('ALTER SESSION SET CURRENT_SCHEMA=' . $config['schema']);
throw new DibiDriverException($err['message'], $err['code']);
}
}
@@ -99,44 +91,24 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$res = oci_parse($this->connection, $sql);
if ($res) {
@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);
if ($err) {
throw self::createException($err['message'], $err['code'], $sql);
throw new DibiDriverException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
} else {
$err = oci_error($this->connection);
throw new Dibi\DriverException($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);
throw new DibiDriverException($err['message'], $err['code'], $sql);
}
}
@@ -147,7 +119,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getAffectedRows()
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -177,13 +149,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if (!oci_commit($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;
}
@@ -193,13 +165,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if (!oci_rollback($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;
}
@@ -217,7 +189,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -228,7 +200,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -244,48 +216,34 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* 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 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)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
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);
public function escapeIdentifier($value)
{
// @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);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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 +263,17 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -328,16 +283,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($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 '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
}
@@ -362,7 +314,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
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 +336,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function seek($row)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -406,15 +358,14 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$type = oci_field_type($this->resultSet, $i);
$columns[] = [
$columns[] = array(
'name' => oci_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => oci_field_name($this->resultSet, $i),
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
];
'nativetype'=> oci_field_type($this->resultSet, $i),
);
}
return $columns;
}
@@ -431,7 +382,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -441,13 +392,13 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getTables()
{
$res = $this->query('SELECT * FROM cat');
$tables = [];
$tables = array();
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = [
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'VIEW',
];
);
}
}
return $tables;
@@ -461,7 +412,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getColumns($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -472,7 +423,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -483,7 +434,7 @@ class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -0,0 +1,465 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
* The dibi driver for PDO.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var PDO Connection resource */
private $connection;
/** @var PDOStatement Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
*/
public function connect(array & $config)
{
$foo = & $config['dsn'];
$foo = & $config['options'];
DibiConnection::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
} else {
try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (PDOException $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
}
}
if (!$this->connection) {
throw new DibiDriverException('Connecting error.');
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection = NULL;
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
// must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
}
} else {
$res = $this->connection->query($sql);
if ($res === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} else {
return $this->createResultDriver($res);
}
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertId();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Returns the connection resource.
* @return PDO
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new DibiMySqlReflector($this);
case 'sqlite':
case 'sqlite2':
return new DibiSqliteReflector($this);
default:
throw new DibiNotSupportedException;
}
}
/**
* Result set driver factory.
* @param PDOStatement
* @return IDibiResultDriver
*/
public function createResultDriver(PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
{
switch ($type) {
case dibi::TEXT:
return $this->connection->quote($value, PDO::PARAM_STR);
case dibi::BINARY:
return $this->connection->quote($value, PDO::PARAM_LOB);
case dibi::IDENTIFIER:
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
case 'sqlite2':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
return $value;
}
case dibi::BOOL:
return $this->connection->quote($value, PDO::PARAM_BOOL);
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)
{
throw new DibiNotImplementedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescape($value, $type)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) {
return;
}
switch ($this->driverName) {
case 'mysql':
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'pgsql':
if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
case 'sqlite2':
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
break;
case 'oci':
if ($offset > 0) {
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'odbc':
case 'dblib':
case 'mssql':
case 'sqlsrv':
if ($offset < 1) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break;
}
// intentionally break omitted
default:
throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return $this->resultSet->rowCount();
}
/**
* 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 $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
}
/**
* 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
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws DibiException
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new DibiNotSupportedException('Driver does not support meta data.');
}
// PHP < 5.2.3 compatibility
// @see: http://php.net/manual/en/pdostatement.getcolumnmeta.php#pdostatement.getcolumnmeta.changelog
$row = $row + array(
'table' => NULL,
'native_type' => 'VAR_STRING',
);
$columns[] = array(
'name' => $row['name'],
'table' => $row['table'],
'nativetype' => $row['native_type'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return PDOStatement
*/
public function getResultResource()
{
return $this->resultSet;
}
}

View File

@@ -5,10 +5,6 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for PostgreSQL database.
@@ -20,12 +16,13 @@ use Dibi;
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @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 */
private $connection;
@@ -40,12 +37,12 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +50,51 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public function connect(array & $config)
{
$error = NULL;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$config += [
$config += array(
'charset' => 'utf8',
];
);
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
Dibi\Helpers::alias($config, 'user', 'username');
Dibi\Helpers::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
DibiConnection::alias($config, 'user', 'username');
DibiConnection::alias($config, 'dbname', 'database');
foreach (array('host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service') as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
set_error_handler(function($severity, $message) use (& $error) {
$error = $message;
});
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$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)) {
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']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw self::createException(pg_last_error($this->connection));
if (isset($config['charset'])) {
DibiDriverException::tryError();
pg_set_client_encoding($this->connection, $config['charset']);
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
}
if (isset($config['schema'])) {
@@ -115,21 +113,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/**
* Pings database.
* @return bool
*/
public function ping()
{
return pg_ping($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -137,7 +125,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = @pg_query($this->connection, $sql); // intentionally @
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)) {
$this->affectedRows = pg_affected_rows($res);
@@ -148,34 +136,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.
* @return int|FALSE number of rows or FALSE on error
@@ -212,7 +172,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -224,7 +184,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -236,7 +196,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -250,7 +210,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
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 +226,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
@@ -277,7 +237,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
@@ -293,54 +253,42 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* 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 escapeText($value)
public function escape($value, $type)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
switch ($type) {
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 +302,24 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
$value = strtr($value, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @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);
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return pg_unescape_bytea($value);
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -384,13 +329,11 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
if ($limit >= 0) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
if ($offset > 0) {
$sql .= ' OFFSET ' . (int) $offset;
}
}
@@ -432,7 +375,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
/**
* 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
*/
public function seek($row)
@@ -459,13 +402,13 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
public function getResultColumns()
{
$count = pg_num_fields($this->resultSet);
$columns = [];
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = [
'name' => pg_field_name($this->resultSet, $i),
'table' => pg_field_table($this->resultSet, $i),
'nativetype' => pg_field_type($this->resultSet, $i),
];
$row = array(
'name' => pg_field_name($this->resultSet, $i),
'table' => pg_field_table($this->resultSet, $i),
'nativetype'=> pg_field_type($this->resultSet, $i),
);
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$columns[] = $row;
}
@@ -484,7 +427,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
}
/********************* Dibi\Reflector ****************d*g**/
/********************* IDibiReflector ****************d*g**/
/**
@@ -493,9 +436,9 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getTables()
{
$version = pg_parameter_status($this->getResource(), 'server_version');
$version = pg_parameter_status($this->resource, 'server_version');
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 = "
@@ -523,7 +466,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
$res = $this->query($query);
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : [];
return $tables ? $tables : array();
}
@@ -534,7 +477,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getColumns($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query("
SELECT indkey
FROM pg_class
@@ -576,10 +519,10 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
");
}
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = [
$columns[] = array(
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
@@ -588,7 +531,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row,
];
);
}
return $columns;
}
@@ -601,7 +544,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getIndexes($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query("
SELECT
a.attnum AS ordinal_position,
@@ -616,7 +559,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
ORDER BY ordinal_position
");
$columns = [];
$columns = array();
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
@@ -629,7 +572,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
WHERE pg_class.oid = $_table::regclass
");
$indexes = [];
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
@@ -649,7 +592,7 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
*/
public function getForeignKeys($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$res = $this->query("
SELECT
@@ -690,17 +633,17 @@ class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
c.conrelid = $_table::regclass
");
$fKeys = $references = [];
$fKeys = $references = array();
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = [
$fKeys[$row['name']] = array(
'name' => $row['name'],
'table' => $row['table'],
'local' => [],
'foreign' => [],
'local' => array(),
'foreign' => array(),
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
];
);
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));

View File

@@ -5,10 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use SQLite3;
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
/**
@@ -21,16 +19,17 @@ use SQLite3;
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
use Dibi\Strict;
/** @var SQLite3 Connection resource */
private $connection;
/** @var \SQLite3Result Resultset resource */
/** @var SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
@@ -44,12 +43,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
public function __construct()
{
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 +56,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
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->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
@@ -70,8 +69,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
} catch (Exception $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
}
}
@@ -102,8 +101,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
@@ -112,45 +111,15 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
}
$res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql);
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
} elseif ($res instanceof \SQLite3Result) {
} elseif ($res instanceof SQLite3Result) {
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.
* @return int|FALSE number of rows or FALSE on error
@@ -175,7 +144,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
@@ -187,7 +156,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
@@ -199,7 +168,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
@@ -219,20 +188,20 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the connection reflector.
* @return Dibi\Reflector
* @return IDibiReflector
*/
public function getReflector()
{
return new SqliteReflector($this);
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param \SQLite3Result
* @return Dibi\ResultDriver
* @param SQLite3Result
* @return IDibiResultDriver
*/
public function createResultDriver(\SQLite3Result $resource)
public function createResultDriver(SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -246,47 +215,35 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* 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 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)
{
return "X'" . bin2hex((string) $value) . "'";
}
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
public function escapeIdentifier($value)
{
return '[' . strtr($value, '[]', ' ') . ']';
}
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 escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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 +262,17 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Decodes data from result set.
* @param string
* @return string
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
public function unescapeBinary($value)
public function unescape($value, $type)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
}
@@ -328,12 +282,8 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
if ($limit >= 0 || $offset > 0) {
$sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -354,11 +304,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
/**
* Returns the number of rows in a result set.
* @return int
* @throws Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
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 +322,12 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = [];
$tmp = array();
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(['[', ']'], '', $k)] = $v;
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
}
return $tmp;
}
@@ -389,11 +339,11 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* 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 Dibi\NotSupportedException
* @throws DibiNotSupportedException
*/
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 +365,15 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
$columns = array();
static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
for ($i = 0; $i < $count; $i++) {
$columns[] = [
$columns[] = array(
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
];
);
}
return $columns;
}
@@ -450,7 +400,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments
* @return void
*/
public function registerFunction($name, callable $callback, $numArgs = -1)
public function registerFunction($name, $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
@@ -464,7 +414,7 @@ class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
* @param int num of arguments
* @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);
}

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,21 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for SQLite database.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class SqliteReflector implements Dibi\Reflector
class DibiSqliteReflector extends DibiObject implements IDibiReflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
/** @var IDibiDriver */
private $driver;
public function __construct(Dibi\Driver $driver)
public function __construct(IDibiDriver $driver)
{
$this->driver = $driver;
}
@@ -40,7 +37,7 @@ class SqliteReflector implements Dibi\Reflector
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = [];
$tables = array();
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
@@ -55,12 +52,19 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getColumns($table)
{
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})");
$columns = [];
$meta = $this->driver->query("
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
UNION ALL
SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
")->fetch(TRUE);
$res = $this->driver->query("PRAGMA table_info({$this->driver->escape($table, dibi::IDENTIFIER)})");
$columns = array();
while ($row = $res->fetch(TRUE)) {
$column = $row['name'];
$pattern = "/(\"$column\"|\[$column\]|$column)\\s+[^,]+\\s+PRIMARY\\s+KEY\\s+AUTOINCREMENT/Ui";
$type = explode('(', $row['type']);
$columns[] = [
$columns[] = array(
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
@@ -68,9 +72,9 @@ class SqliteReflector implements Dibi\Reflector
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
'vendor' => $row,
];
);
}
return $columns;
}
@@ -83,15 +87,15 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})");
$indexes = [];
$res = $this->driver->query("PRAGMA index_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})");
$res = $this->driver->query("PRAGMA index_info({$this->driver->escape($index, dibi::IDENTIFIER)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
@@ -112,12 +116,12 @@ class SqliteReflector implements Dibi\Reflector
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = [
$indexes[] = array(
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => [$column['name']],
];
'columns' => array($column['name']),
);
break;
}
}
@@ -134,8 +138,11 @@ class SqliteReflector implements Dibi\Reflector
*/
public function getForeignKeys($table)
{
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})");
$keys = [];
if (!($this->driver instanceof DibiSqlite3Driver)) {
// throw new DibiNotSupportedException; // @see http://www.sqlite.org/foreignkeys.html
}
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$keys = array();
while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns

View File

@@ -5,56 +5,56 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
use Dibi\Type;
/**
* This class is static container class for creating DB objects and
* store connections info.
*
* @author David Grudl
* @package 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
AFFECTED_ROWS = 'a',
IDENTIFIER = 'n';
/** version */
const
VERSION = '3.0.4',
REVISION = 'released on 2016-04-06';
/** sorting order */
const
ASC = 'ASC',
DESC = 'DESC';
const IDENTIFIER = 'n',
AFFECTED_ROWS = 'a';
/** @deprecated */
const
TEXT = Type::TEXT,
BINARY = Type::BINARY,
BOOL = Type::BOOL,
INTEGER = Type::INTEGER,
FLOAT = Type::FLOAT,
DATE = Type::DATE,
DATETIME = Type::DATETIME,
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;
const FIELD_TEXT = self::TEXT,
FIELD_BINARY = self::BINARY,
FIELD_BOOL = self::BOOL,
FIELD_INTEGER = self::INTEGER,
FIELD_FLOAT = self::FLOAT,
FIELD_DATE = self::DATE,
FIELD_DATETIME = self::DATETIME,
FIELD_TIME = self::TIME;
/** @var Dibi\Connection[] Connection registry storage for DibiConnection objects */
private static $registry = [];
/** version */
const VERSION = '2.2.5',
REVISION = 'released on 2015-10-26';
/** @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;
/** @var array @see addHandler */
private static $handlers = array();
/** @var string Last SQL command @see dibi::query() */
public static $sql;
@@ -68,7 +68,7 @@ class dibi
public static $numOfQueries = 0;
/** @var string Default dibi driver */
public static $defaultDriver = 'mysqli';
public static $defaultDriver = 'mysql';
/**
@@ -84,20 +84,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 string connection name
* @return Dibi\Connection
* @throws Dibi\Exception
* @return DibiConnection
* @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
*/
public static function disconnect()
@@ -119,21 +119,21 @@ class dibi
/**
* Retrieve active connection.
* @param string connection registy name
* @return Dibi\Connection
* @throws Dibi\Exception
* @return DibiConnection
* @throws DibiException
*/
public static function getConnection($name = NULL)
{
if ($name === 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;
}
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];
@@ -142,21 +142,23 @@ class dibi
/**
* Sets connection.
* @param Dibi\Connection
* @return Dibi\Connection
* @param DibiConnection
* @return DibiConnection
*/
public static function setConnection(Dibi\Connection $connection)
public static function setConnection(DibiConnection $connection)
{
return self::$connection = $connection;
}
/**
* @deprecated
* Change active connection.
* @param string connection registy name
* @return void
* @throws DibiException
*/
public static function activate($name)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
self::$connection = self::getConnection($name);
}
@@ -165,10 +167,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
* @return Dibi\Result|int result set object (if any)
* @throws Dibi\Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
public static function query($args)
{
@@ -178,9 +180,9 @@ class dibi
/**
* Executes the SQL query - Monostate for Dibi\Connection::nativeQuery().
* Executes the SQL query - Monostate for DibiConnection::nativeQuery().
* @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)
{
@@ -189,7 +191,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
* @return bool
*/
@@ -201,9 +203,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
* @return Dibi\DataSource
* @return DibiDataSource
*/
public static function dataSource($args)
{
@@ -213,10 +215,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
* @return Dibi\Row
* @throws Dibi\Exception
* @return DibiRow
* @throws DibiException
*/
public static function fetch($args)
{
@@ -226,10 +228,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
* @return Dibi\Row[]
* @throws Dibi\Exception
* @return DibiRow[]
* @throws DibiException
*/
public static function fetchAll($args)
{
@@ -239,10 +241,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
* @return string
* @throws Dibi\Exception
* @throws DibiException
*/
public static function fetchSingle($args)
{
@@ -252,10 +254,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
* @return string
* @throws Dibi\Exception
* @throws DibiException
*/
public static function fetchPairs($args)
{
@@ -266,9 +268,9 @@ class dibi
/**
* Gets the number of affected rows.
* Monostate for Dibi\Connection::getAffectedRows()
* Monostate for DibiConnection::getAffectedRows()
* @return int number of rows
* @throws Dibi\Exception
* @throws DibiException
*/
public static function getAffectedRows()
{
@@ -279,7 +281,7 @@ class dibi
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Dibi\Exception
* @throws DibiException
*/
public static function affectedRows()
{
@@ -289,10 +291,10 @@ class dibi
/**
* 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
* @return int
* @throws Dibi\Exception
* @throws DibiException
*/
public static function getInsertId($sequence = NULL)
{
@@ -304,7 +306,7 @@ class dibi
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
* @throws DibiException
*/
public static function insertId($sequence = NULL)
{
@@ -313,10 +315,10 @@ class dibi
/**
* Begins a transaction - Monostate for Dibi\Connection::begin().
* Begins a transaction - Monostate for DibiConnection::begin().
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public static function begin($savepoint = NULL)
{
@@ -325,10 +327,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
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public static function commit($savepoint = NULL)
{
@@ -337,10 +339,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
* @return void
* @throws Dibi\Exception
* @throws DibiException
*/
public static function rollback($savepoint = NULL)
{
@@ -349,8 +351,8 @@ class dibi
/**
* Gets a information about the current database - Monostate for Dibi\Connection::getDatabaseInfo().
* @return Dibi\Reflection\Database
* Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo().
* @return DibiDatabaseInfo
*/
public static function getDatabaseInfo()
{
@@ -365,7 +367,19 @@ class dibi
*/
public static function loadFile($file)
{
return Dibi\Helpers::loadFromFile(self::getConnection(), $file);
return self::getConnection()->loadFile($file);
}
/**
* Replacement for majority of dibi::methods() in future.
*/
public static function __callStatic($name, $args)
{
//if ($name = 'select', 'update', ...') {
// return self::command()->$name($args);
//}
return call_user_func_array(array(self::getConnection(), $name), $args);
}
@@ -373,7 +387,7 @@ class dibi
/**
* @return Dibi\Fluent
* @return DibiFluent
*/
public static function command()
{
@@ -383,19 +397,19 @@ class dibi
/**
* @param string column name
* @return Dibi\Fluent
* @return DibiFluent
*/
public static function select($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 array
* @return Dibi\Fluent
* @return DibiFluent
*/
public static function update($table, $args)
{
@@ -406,7 +420,7 @@ class dibi
/**
* @param string table
* @param array
* @return Dibi\Fluent
* @return DibiFluent
*/
public static function insert($table, $args)
{
@@ -416,7 +430,7 @@ class dibi
/**
* @param string table
* @return Dibi\Fluent
* @return DibiFluent
*/
public static function delete($table)
{
@@ -428,8 +442,8 @@ class dibi
/**
* Returns substitution hashmap - Monostate for Dibi\Connection::getSubstitutes().
* @return Dibi\HashMap
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
* @return DibiHashMap
*/
public static function getSubstitutes()
{
@@ -441,14 +455,89 @@ class dibi
/**
* Prints out a syntax highlighted version of the SQL command or Result.
* @param string|Result
* Prints out a syntax highlighted version of the SQL command or DibiResult.
* @param string|DibiResult
* @param bool return output instead of printing it?
* @return string
*/
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|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// 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,34 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use Traversable;
/**
* dibi connection.
*
* @author David Grudl
* @package dibi
*
* @property-read int $affectedRows
* @property-read int $insertId
*/
class Connection
class DibiConnection extends DibiObject
{
use Strict;
/** @var array of function (Event $event); Occurs after query is executed */
/** @var array of function (DibiEvent $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var Driver */
/** @var IDibiDriver */
private $driver;
/** @var Translator */
/** @var DibiTranslator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var HashMap Substitutes for identifiers */
/** @var DibiHashMap Substitutes for identifiers */
private $substitutes;
@@ -51,7 +48,7 @@ class Connection
* @param mixed connection parameters
* @param string connection name
* @throws Exception
* @throws DibiException
*/
public function __construct($config, $name = NULL)
{
@@ -59,66 +56,64 @@ class Connection
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
$tmp = [];
$tmp = array();
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} 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');
Helpers::alias($config, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname');
Helpers::alias($config, 'result|formatDate', 'resultDate');
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
self::alias($config, 'username', 'user');
self::alias($config, 'password', 'pass');
self::alias($config, 'host', 'hostname');
self::alias($config, 'result|formatDate', 'resultDate');
self::alias($config, 'result|formatDateTime', 'resultDateTime');
if (!isset($config['driver'])) {
$config['driver'] = \dibi::$defaultDriver;
$config['driver'] = dibi::$defaultDriver;
}
if ($config['driver'] instanceof Driver) {
$this->driver = $config['driver'];
$config['driver'] = get_class($this->driver);
} elseif (is_subclass_of($config['driver'], 'Dibi\Driver')) {
$this->driver = new $config['driver'];
} else {
$class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($config['driver'])));
$class = "Dibi\\Drivers\\{$class}Driver";
if (!class_exists($class)) {
throw new Exception("Unable to create instance of dibi driver '$class'.");
$class = preg_replace(array('#\W#', '#sql#'), array('_', 'Sql'), ucfirst(strtolower($config['driver'])));
$class = "Dibi{$class}Driver";
if (!class_exists($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;
}
$config['name'] = $name;
$this->config = $config;
$this->driver = new $class;
$this->translator = new DibiTranslator($this);
// profiler
$profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = ['run' => (bool) $profilerCfg];
$profilerCfg = array('run' => (bool) $profilerCfg);
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
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()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
}
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('Dibi\Bridges\Nette\Panel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
if (!interface_exists('Tracy\IBarPanel') && (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPanel'))) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
}
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
@@ -148,13 +143,13 @@ class Connection
*/
final public function connect()
{
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -203,17 +198,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)
{
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
Helpers::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]);
}
}
/**
* Returns the driver and connects to a database in lazy mode.
* @return Driver
* @return IDibiDriver
*/
final public function getDriver()
{
@@ -225,8 +233,8 @@ class Connection
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
* @return Result|int result set object (if any)
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
final public function query($args)
{
@@ -239,7 +247,7 @@ class Connection
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws Exception
* @throws DibiException
*/
final public function translate($args)
{
@@ -257,12 +265,12 @@ class Connection
{
$args = func_get_args();
try {
Helpers::dump($this->translateArgs($args));
dibi::dump($this->translateArgs($args));
return TRUE;
} catch (Exception $e) {
} catch (DibiException $e) {
if ($e->getSql()) {
Helpers::dump($e->getSql());
dibi::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
@@ -272,15 +280,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
* @return DataSource
* @throws Exception
* @return DibiDataSource
* @throws DibiException
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DataSource($this->translateArgs($args), $this);
return new DibiDataSource($this->translateArgs($args), $this);
}
@@ -292,30 +300,26 @@ class Connection
private function translateArgs($args)
{
$this->connected || $this->connect();
if (!$this->translator) {
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
return $this->translator->translate($args);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Result|int result set object (if any)
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
final public function nativeQuery($sql)
{
$this->connected || $this->connect();
\dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL;
dibi::$sql = $sql;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
try {
$res = $this->driver->query($sql);
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -334,14 +338,14 @@ class Connection
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows
* @throws Exception
* @throws DibiException
*/
public function getAffectedRows()
{
$this->connected || $this->connect();
$rows = $this->driver->getAffectedRows();
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;
}
@@ -350,7 +354,7 @@ class Connection
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Exception
* @throws DibiException
*/
public function affectedRows()
{
@@ -362,14 +366,14 @@ class Connection
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string optional sequence name
* @return int
* @throws Exception
* @throws DibiException
*/
public function getInsertId($sequence = NULL)
{
$this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence);
if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.');
throw new DibiException('Cannot retrieve last generated ID.');
}
return (int) $id;
}
@@ -379,7 +383,7 @@ class Connection
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Exception
* @throws DibiException
*/
public function insertId($sequence = NULL)
{
@@ -395,12 +399,12 @@ class Connection
public function begin($savepoint = NULL)
{
$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 {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -415,12 +419,12 @@ class Connection
public function commit($savepoint = NULL)
{
$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 {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -435,12 +439,12 @@ class Connection
public function rollback($savepoint = NULL)
{
$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 {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -449,14 +453,14 @@ class Connection
/**
* Result set factory.
* @param ResultDriver
* @return Result
* @param IDibiResultDriver
* @return DibiResult
*/
public function createResultSet(ResultDriver $resultDriver)
public function createResultSet(IDibiResultDriver $resultDriver)
{
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
$res = new DibiResult($resultDriver);
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
}
@@ -464,17 +468,17 @@ class Connection
/**
* @return Fluent
* @return DibiFluent
*/
public function command()
{
return new Fluent($this);
return new DibiFluent($this);
}
/**
* @param string column name
* @return Fluent
* @return DibiFluent
*/
public function select($args)
{
@@ -486,12 +490,12 @@ class Connection
/**
* @param string table
* @param array
* @return Fluent
* @return DibiFluent
*/
public function update($table, $args)
{
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);
}
@@ -500,14 +504,14 @@ class Connection
/**
* @param string table
* @param array
* @return Fluent
* @return DibiFluent
*/
public function insert($table, $args)
{
if ($args instanceof Traversable) {
$args = iterator_to_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()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
@@ -516,7 +520,7 @@ class Connection
/**
* @param string table
* @return Fluent
* @return DibiFluent
*/
public function delete($table)
{
@@ -529,7 +533,7 @@ class Connection
/**
* Returns substitution hashmap.
* @return HashMap
* @return DibiHashMap
*/
public function getSubstitutes()
{
@@ -543,9 +547,16 @@ class Connection
*/
public function substitute($value)
{
return strpos($value, ':') === FALSE
? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
}
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
}
@@ -555,8 +566,8 @@ class Connection
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return Row
* @throws Exception
* @return DibiRow
* @throws DibiException
*/
public function fetch($args)
{
@@ -568,8 +579,8 @@ class Connection
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return Row[]
* @throws Exception
* @return DibiRow[]
* @throws DibiException
*/
public function fetchAll($args)
{
@@ -582,7 +593,7 @@ class Connection
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws Exception
* @throws DibiException
*/
public function fetchSingle($args)
{
@@ -595,7 +606,7 @@ class Connection
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws Exception
* @throws DibiException
*/
public function fetchPairs($args)
{
@@ -604,37 +615,59 @@ class Connection
}
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/
/**
* Import SQL dump from file.
* Import SQL dump from file - extreme fast!
* @param string filename
* @return int count of sql commands
*/
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.
* @return Reflection\Database
* @return DibiDatabaseInfo
*/
public function getDatabaseInfo()
{
$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 +676,7 @@ class Connection
*/
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 +685,7 @@ class Connection
*/
public function __sleep()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
protected function onEvent($arg)
{
foreach ($this->onEvent ?: [] as $handler) {
call_user_func($handler, $arg);
}
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
}

View File

@@ -5,24 +5,22 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Default implementation of IDataSource for dibi.
*
* @author David Grudl
* @package dibi
*/
class DataSource implements IDataSource
class DibiDataSource extends DibiObject implements IDataSource
{
use Strict;
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var string */
private $sql;
/** @var Result */
/** @var DibiResult */
private $result;
/** @var int */
@@ -32,13 +30,13 @@ class DataSource implements IDataSource
private $totalCount;
/** @var array */
private $cols = [];
private $cols = array();
/** @var array */
private $sorting = [];
private $sorting = array();
/** @var array */
private $conds = [];
private $conds = array();
/** @var int */
private $offset;
@@ -49,12 +47,12 @@ class DataSource implements IDataSource
/**
* @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) {
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
} else {
$this->sql = '(' . $sql . ') t'; // SQL command
}
@@ -133,7 +131,7 @@ class DataSource implements IDataSource
/**
* Returns the dibi connection.
* @return Connection
* @return DibiConnection
*/
final public function getConnection()
{
@@ -145,8 +143,8 @@ class DataSource implements IDataSource
/**
* Returns (and queries) Result.
* @return Result
* Returns (and queries) DibiResult.
* @return DibiResult
*/
public function getResult()
{
@@ -158,7 +156,7 @@ class DataSource implements IDataSource
/**
* @return ResultIterator
* @return DibiResultIterator
*/
public function getIterator()
{
@@ -168,7 +166,7 @@ class DataSource implements IDataSource
/**
* 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()
{
@@ -233,8 +231,8 @@ class DataSource implements IDataSource
/**
* Returns this data source wrapped in Fluent object.
* @return Fluent
* Returns this data source wrapped in DibiFluent object.
* @return DibiFluent
*/
public function toFluent()
{
@@ -243,8 +241,8 @@ class DataSource implements IDataSource
/**
* Returns this data source wrapped in DataSource object.
* @return DataSource
* Returns this data source wrapped in DibiDataSource object.
* @return DibiDataSource
*/
public function toDataSource()
{
@@ -262,11 +260,11 @@ class DataSource implements IDataSource
return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : NULL, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : NULL, '
%ex', $this->conds ? array('WHERE %and', $this->conds) : NULL, '
%ex', $this->sorting ? array('ORDER BY %by', $this->sorting) : NULL, '
%ofs %lmt', $this->offset, $this->limit
);
} catch (\Exception $e) {
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}

View File

@@ -0,0 +1,708 @@
<?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.
*
* @author David Grudl
* @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.
*
* @author David Grudl
* @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.
*
* @author David Grudl
* @package dibi\reflection
*
* @property-read array $columns
* @property-read array $columnNames
*/
class DibiResultInfo extends DibiObject
{
/** @var IDibiResultDriver */
private $driver;
/** @var array */
private $columns;
/** @var array */
private $names;
public function __construct(IDibiResultDriver $driver)
{
$this->driver = $driver;
}
/**
* @return DibiColumnInfo[]
*/
public function getColumns()
{
$this->initColumns();
return array_values($this->columns);
}
/**
* @param bool
* @return string[]
*/
public function getColumnNames($fullNames = FALSE)
{
$this->initColumns();
$res = array();
foreach ($this->columns as $column) {
$res[] = $fullNames ? $column->getFullName() : $column->getName();
}
return $res;
}
/**
* @param string
* @return DibiColumnInfo
*/
public function getColumn($name)
{
$this->initColumns();
$l = strtolower($name);
if (isset($this->names[$l])) {
return $this->names[$l];
} else {
throw new DibiException("Result set has no column '$name'.");
}
}
/**
* @param string
* @return bool
*/
public function hasColumn($name)
{
$this->initColumns();
return isset($this->names[strtolower($name)]);
}
/**
* @return void
*/
protected function initColumns()
{
if ($this->columns === NULL) {
$this->columns = array();
$reflector = $this->driver instanceof IDibiReflector ? $this->driver : NULL;
foreach ($this->driver->getResultColumns() as $info) {
$this->columns[] = $this->names[$info['name']] = new DibiColumnInfo($reflector, $info);
}
}
}
}
/**
* Reflection metadata class for a table or result set column.
*
* @author David Grudl
* @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;
}
/**
* @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' => 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.
*
* @author David Grudl
* @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.
*
* @author David Grudl
* @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,89 @@
<?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.
*
* @author David Grudl
* @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,15 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Profiler & logger event.
*
* @author David Grudl
* @package dibi
*/
class Event
class DibiEvent
{
use Strict;
/** event type */
const CONNECT = 1,
SELECT = 4,
@@ -28,7 +27,7 @@ class Event
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var Connection */
/** @var DibiConnection */
public $connection;
/** @var int */
@@ -37,7 +36,7 @@ class Event
/** @var string */
public $sql;
/** @var Result|DriverException|NULL */
/** @var DibiResult|DibiDriverException|NULL */
public $result;
/** @var float */
@@ -50,7 +49,7 @@ class Event
public $source;
public function __construct(Connection $connection, $type, $sql = NULL)
public function __construct(DibiConnection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
@@ -58,25 +57,25 @@ class Event
$this->time = -microtime(TRUE);
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,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
];
);
$this->type = $types[strtoupper($matches[1])];
}
$rc = new \ReflectionClass('dibi');
$rc = new ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = [$row['file'], (int) $row['line']];
$this->source = array($row['file'], (int) $row['line']);
break;
}
}
\dibi::$elapsedTime = FALSE;
\dibi::$numOfQueries++;
\dibi::$sql = $sql;
dibi::$elapsedTime = FALSE;
dibi::$numOfQueries++;
dibi::$sql = $sql;
}
@@ -84,14 +83,14 @@ class Event
{
$this->result = $result;
try {
$this->count = $result instanceof Result ? count($result) : NULL;
} catch (Exception $e) {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = NULL;
}
$this->time += microtime(TRUE);
\dibi::$elapsedTime = $this->time;
\dibi::$totalTime += $this->time;
dibi::$elapsedTime = $this->time;
dibi::$totalTime += $this->time;
return $this;
}

View File

@@ -5,28 +5,28 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi common exception.
*
* @author David Grudl
* @package dibi
*/
class Exception extends \Exception
class DibiException extends Exception
{
/** @var string|NULL */
/** @var string */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param mixed
* @param string SQL command
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $sql = NULL)
{
parent::__construct($message);
$this->code = $code;
parent::__construct($message, (int) $code);
$this->sql = $sql;
}
@@ -53,104 +53,96 @@ class Exception extends \Exception
/**
* database server exception.
*
* @author David Grudl
* @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.
*
* @author David Grudl
* @package dibi
*/
class PcreException extends Exception
class DibiPcreException extends Exception
{
public function __construct($message = '%msg.')
{
static $messages = [
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
];
);
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
class NotImplementedException extends Exception
{
}
class NotSupportedException extends Exception
{
}
/**
* @package dibi
*/
class DibiNotImplementedException extends DibiException
{}
/**
* Database procedure exception.
* @package dibi
*/
class ProcedureException extends Exception
{
/** @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
{
}
class DibiNotSupportedException extends DibiException
{}

View File

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

View File

@@ -5,26 +5,20 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi FirePHP logger.
*
* @author David Grudl
* @package dibi
*/
class FirePhpLogger
class DibiFirePhpLogger extends DibiObject
{
use Dibi\Strict;
/** maximum number of rows */
public static $maxQueries = 30;
static public $maxQueries = 30;
/** maximum SQL length */
public static $maxLength = 1000;
/** size of json stream chunk */
public static $streamChunkSize = 4990;
static public $maxLength = 1000;
/** @var int */
public $filter;
@@ -36,7 +30,7 @@ class FirePhpLogger
public $numOfQueries = 0;
/** @var array */
private static $fireTable = [['Time', 'SQL Statement', 'Rows', 'Connection']];
private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
/**
@@ -50,7 +44,7 @@ class FirePhpLogger
public function __construct($filter = NULL)
{
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
@@ -58,34 +52,33 @@ class FirePhpLogger
* After event notification.
* @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) {
return;
}
if (!$this->numOfQueries) {
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
}
$this->totalTime += $event->time;
$this->numOfQueries++;
self::$fireTable[] = [
self::$fireTable[] = array(
sprintf('%0.3f', $event->time * 1000),
strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql,
$event->result instanceof \Exception ? 'ERROR' : (string) $event->count,
$event->result instanceof Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'),
];
);
$payload = json_encode([
[
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
$payload = json_encode(array(
array(
'Type' => 'TABLE',
'Label' => 'dibi profiler (' . $this->numOfQueries . ' SQL queries took ' . sprintf('%0.3f', $this->totalTime * 1000) . ' ms)',
],
),
self::$fireTable,
]);
foreach (str_split($payload, self::$streamChunkSize) as $num => $s) {
));
foreach (str_split($payload, 4990) as $num => $s) {
$num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index
}

View File

@@ -5,47 +5,40 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @method Fluent select(...$field)
* @method Fluent distinct()
* @method Fluent from($table)
* @method Fluent where(...$cond)
* @method Fluent groupBy(...$field)
* @method Fluent having(...$cond)
* @method Fluent orderBy(...$field)
* @method Fluent limit(int $limit)
* @method Fluent offset(int $offset)
* @method Fluent join(...$table)
* @method Fluent leftJoin(...$table)
* @method Fluent innerJoin(...$table)
* @method Fluent rightJoin(...$table)
* @method Fluent outerJoin(...$table)
* @method Fluent as(...$field)
* @method Fluent on(...$cond)
* @method Fluent using(...$cond)
* @author David Grudl
* @package dibi
*
* @method DibiFluent select($field)
* @method DibiFluent distinct()
* @method DibiFluent from($table)
* @method DibiFluent where($cond)
* @method DibiFluent groupBy($field)
* @method DibiFluent having($cond)
* @method DibiFluent orderBy($field)
* @method DibiFluent limit(int $limit)
* @method DibiFluent offset(int $offset)
* @method DibiFluent leftJoin($table)
* @method DibiFluent on($cond)
*/
class Fluent implements IDataSource
class DibiFluent extends DibiObject implements IDataSource
{
use Strict;
const REMOVE = FALSE;
/** @var array */
public static $masks = [
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'],
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'],
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'],
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'],
];
public static $masks = array(
'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'),
'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'),
'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT'),
'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'),
);
/** @var array default modifiers for arrays */
public static $modifiers = [
public static $modifiers = array(
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%in',
@@ -55,10 +48,10 @@ class Fluent implements IDataSource
'HAVING' => '%and',
'ORDER BY' => '%by',
'GROUP BY' => '%by',
];
);
/** @var array clauses separators */
public static $separators = [
public static $separators = array(
'SELECT' => ',',
'FROM' => ',',
'WHERE' => 'AND',
@@ -70,47 +63,47 @@ class Fluent implements IDataSource
'SET' => ',',
'VALUES' => ',',
'INTO' => FALSE,
];
);
/** @var array clauses */
public static $clauseSwitches = [
public static $clauseSwitches = array(
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
];
);
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var array */
private $setups = [];
private $setups = array();
/** @var string */
private $command;
/** @var array */
private $clauses = [];
private $clauses = array();
/** @var array */
private $flags = [];
private $flags = array();
/** @var array */
private $cursor;
/** @var HashMap normalized clauses */
/** @var DibiHashMap normalized clauses */
private static $normalizer;
/**
* @param Connection
* @param DibiConnection
*/
public function __construct(Connection $connection)
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
}
}
@@ -131,7 +124,7 @@ class Fluent implements IDataSource
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
}
$this->cursor = & $this->clauses[$clause];
$this->cursor = [];
$this->cursor = array();
$this->command = $clause;
}
@@ -145,7 +138,7 @@ class Fluent implements IDataSource
$this->cursor = & $this->clauses[$clause];
// TODO: really delete?
if ($args === [self::REMOVE]) {
if ($args === array(self::REMOVE)) {
$this->cursor = NULL;
return $this;
}
@@ -153,7 +146,7 @@ class Fluent implements IDataSource
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace
$this->cursor = [];
$this->cursor = array();
} elseif (!empty($this->cursor)) {
$this->cursor[] = $sep;
@@ -162,7 +155,7 @@ class Fluent implements IDataSource
} else {
// append to currect flow
if ($args === [self::REMOVE]) {
if ($args === array(self::REMOVE)) {
return $this;
}
@@ -170,7 +163,7 @@ class Fluent implements IDataSource
}
if ($this->cursor === NULL) {
$this->cursor = [];
$this->cursor = array();
}
// special types or argument
@@ -181,21 +174,22 @@ class Fluent implements IDataSource
return $this;
} 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 (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = [self::$modifiers[$clause], $arg];
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = ['%a', $arg];
$args = array('%a', $arg);
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = new Literal("($arg)");
$this->cursor[] = '%SQL';
$arg = "($arg)";
}
$this->cursor[] = $arg;
}
@@ -213,7 +207,7 @@ class Fluent implements IDataSource
{
$this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) {
$this->cursor = [];
$this->cursor = array();
}
return $this;
@@ -273,7 +267,7 @@ class Fluent implements IDataSource
/**
* Returns the dibi connection.
* @return Connection
* @return DibiConnection
*/
final public function getConnection()
{
@@ -282,7 +276,7 @@ class Fluent implements IDataSource
/**
* Adds Result setup.
* Adds DibiResult setup.
* @param string method
* @param mixed args
* @return self
@@ -300,16 +294,16 @@ class Fluent implements IDataSource
/**
* Generates and executes SQL query.
* @param mixed what to return?
* @return Result|int result set object (if any)
* @throws Exception
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
public function execute($return = NULL)
{
$res = $this->query($this->_export());
switch ($return) {
case \dibi::IDENTIFIER:
case dibi::IDENTIFIER:
return $this->connection->getInsertId();
case \dibi::AFFECTED_ROWS:
case dibi::AFFECTED_ROWS:
return $this->connection->getAffectedRows();
default:
return $res;
@@ -319,12 +313,12 @@ class Fluent implements IDataSource
/**
* 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()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT'] && !$this->clauses['OFFSET']) {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
@@ -337,8 +331,8 @@ class Fluent implements IDataSource
*/
public function fetchSingle()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT'] && !$this->clauses['OFFSET']) {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
@@ -353,7 +347,7 @@ class Fluent implements IDataSource
*/
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();
}
@@ -384,11 +378,11 @@ class Fluent implements IDataSource
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return ResultIterator
* @return DibiResultIterator
*/
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();
}
@@ -408,20 +402,20 @@ class Fluent implements IDataSource
*/
public function count()
{
return (int) $this->query([
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle();
))->fetchSingle();
}
/**
* @return Result
* @return DibiResult
*/
private function query($args)
{
$res = $this->connection->query($args);
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;
}
@@ -431,11 +425,11 @@ class Fluent implements IDataSource
/**
* @return DataSource
* @return DibiDataSource
*/
public function toDataSource()
{
return new DataSource($this->connection->translate($this->_export()), $this->connection);
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
}
@@ -447,32 +441,28 @@ class Fluent implements IDataSource
{
try {
return $this->connection->translate($this->_export());
} catch (\Exception $e) {
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/**
* Generates parameters for Translator.
* Generates parameters for DibiTranslator.
* @param string clause name
* @return array
*/
protected function _export($clause = NULL, $args = [])
protected function _export($clause = NULL, $args = array())
{
if ($clause === NULL) {
$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 {
$clause = self::$normalizer->$clause;
if (array_key_exists($clause, $this->clauses)) {
$data = [$clause => $this->clauses[$clause]];
$data = array($clause => $this->clauses[$clause]);
} else {
return [];
return array();
}
}

View File

@@ -5,26 +5,31 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Lazy cached storage.
*
* @author David Grudl
* @package dibi
* @internal
*/
abstract class HashMapBase
abstract class DibiHashMapBase
{
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;
}
@@ -40,9 +45,10 @@ abstract class HashMapBase
/**
* Lazy cached storage.
*
* @author David Grudl
* @internal
*/
final class HashMap extends HashMapBase
final class DibiHashMap extends DibiHashMapBase
{
public function __set($nm, $val)

View File

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

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

@@ -0,0 +1,308 @@
<?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>
*
* @author David Grudl
* @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)
*/
namespace Dibi;
/**
* dibi result set.
@@ -18,42 +16,43 @@ namespace Dibi;
* $value = $result->fetchSingle();
* $table = $result->fetchAll();
* $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('col1');
* $assoc = $result->fetchAssoc('col1[]col2->col3');
* $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('active,#,id');
*
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*
* @property-read int $rowCount
*/
class Result implements IDataSource
class DibiResult extends DibiObject implements IDataSource
{
use Strict;
/** @var array ResultDriver */
/** @var array IDibiResultDriver */
private $driver;
/** @var array Translate table */
private $types = [];
private $types = array();
/** @var Reflection\Result */
/** @var DibiResultInfo */
private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE;
/** @var string returned object class */
private $rowClass = 'Dibi\Row';
private $rowClass = 'DibiRow';
/** @var callable returned object factory*/
/** @var Callback returned object factory*/
private $rowFactory;
/** @var array format */
private $formats = [];
private $formats = array();
/**
* @param ResultDriver
* @param IDibiResultDriver
*/
public function __construct($driver)
{
@@ -67,7 +66,6 @@ class Result implements IDataSource
*/
final public function getResource()
{
trigger_error(__METHOD__ . '() is deprecated, use getResultDriver()->getResultResource().', E_USER_DEPRECATED);
return $this->getResultDriver()->getResultResource();
}
@@ -87,13 +85,13 @@ class Result implements IDataSource
/**
* Safe access to property $driver.
* @return ResultDriver
* @throws \RuntimeException
* @return IDibiResultDriver
* @throws RuntimeException
*/
final public function getResultDriver()
{
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;
@@ -107,7 +105,7 @@ class Result implements IDataSource
* 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 Exception
* @throws DibiException
*/
final public function seek($row)
{
@@ -137,11 +135,11 @@ class Result implements IDataSource
/**
* Required by the IteratorAggregate interface.
* @return ResultIterator
* @return DibiResultIterator
*/
final public function getIterator()
{
return new ResultIterator($this);
return new DibiResultIterator($this);
}
@@ -149,7 +147,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
* @return self
*/
@@ -171,10 +169,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
*/
public function setRowFactory(callable $callback)
public function setRowFactory($callback)
{
$this->rowFactory = $callback;
return $this;
@@ -184,7 +183,7 @@ class Result implements IDataSource
/**
* Fetches the row at current position, process optional type conversion.
* 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()
{
@@ -223,7 +222,7 @@ class Result implements IDataSource
* Fetches all records from table.
* @param int offset
* @param int limit
* @return Row[]
* @return DibiRow[]
*/
final public function fetchAll($offset = NULL, $limit = NULL)
{
@@ -231,10 +230,10 @@ class Result implements IDataSource
$this->seek((int) $offset);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = [];
$data = array();
do {
if ($limit === 0) {
break;
@@ -255,8 +254,8 @@ class Result implements IDataSource
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return array
* @throws \InvalidArgumentException
* @return DibiRow
* @throws InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
@@ -267,7 +266,7 @@ class Result implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = NULL;
@@ -277,7 +276,7 @@ class Result implements IDataSource
foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
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 +334,7 @@ class Result implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = NULL;
@@ -405,21 +404,21 @@ class Result implements IDataSource
* @param string associative key
* @param string value
* @return array
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
final public function fetchPairs($key = NULL, $value = NULL)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
return array(); // empty result set
}
$data = [];
$data = array();
if ($value === 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
@@ -436,7 +435,7 @@ class Result implements IDataSource
} else {
if (!property_exists($row, $value)) {
throw new \InvalidArgumentException("Unknown value column '$value'.");
throw new InvalidArgumentException("Unknown value column '$value'.");
}
if ($key === NULL) { // indexed-array
@@ -447,7 +446,7 @@ class Result implements IDataSource
}
if (!property_exists($row, $key)) {
throw new \InvalidArgumentException("Unknown key column '$key'.");
throw new InvalidArgumentException("Unknown key column '$key'.");
}
}
@@ -468,12 +467,12 @@ class Result implements IDataSource
*/
private function detectTypes()
{
$cache = Helpers::getTypeCache();
$cache = DibiColumnInfo::getTypeCache();
try {
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 +489,14 @@ class Result implements IDataSource
continue;
}
$value = $row[$key];
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
if ($value === FALSE || $type === dibi::TEXT) {
} elseif ($type === Type::INTEGER) {
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1)
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === Type::FLOAT) {
} elseif ($type === dibi::FLOAT) {
$value = ltrim($value, '0');
$p = strpos($value, '.');
if ($p !== FALSE) {
@@ -511,24 +509,17 @@ class Result implements IDataSource
? $float
: $value;
} elseif ($type === Type::BOOL) {
} elseif ($type === dibi::BOOL) {
$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', ...
$value = new DateTime($value);
$value = new DibiDateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
} else {
$row[$key] = NULL;
}
} elseif ($type === Type::TIME_INTERVAL) {
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
$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);
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
}
}
}
@@ -537,7 +528,7 @@ class Result implements IDataSource
/**
* Define column type.
* @param string column
* @param string type (use constant Type::*)
* @param string type (use constant Dibi::*)
* @return self
*/
final public function setType($col, $type)
@@ -559,7 +550,7 @@ class Result implements IDataSource
/**
* Sets data format.
* @param string type (use constant Type::*)
* @param string type (use constant Dibi::*)
* @param string format
* @return self
*/
@@ -585,19 +576,19 @@ class Result implements IDataSource
/**
* Returns a meta information about the current result set.
* @return Reflection\Result
* @return DibiResultInfo
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new Reflection\Result($this->getResultDriver());
$this->meta = new DibiResultInfo($this->getResultDriver());
}
return $this->meta;
}
/**
* @return Reflection\Column[]
* @deprecated
*/
final public function getColumns()
{
@@ -614,7 +605,66 @@ class Result implements IDataSource
*/
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)
*/
namespace Dibi;
/**
* 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>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $row) {
@@ -19,12 +17,13 @@ namespace Dibi;
* }
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*/
class ResultIterator implements \Iterator, \Countable
class DibiResultIterator implements Iterator, Countable
{
use Strict;
/** @var Result */
/** @var DibiResult */
private $result;
/** @var int */
@@ -35,9 +34,9 @@ class ResultIterator implements \Iterator, \Countable
/**
* @param Result
* @param DibiResult
*/
public function __construct(Result $result)
public function __construct(DibiResult $result)
{
$this->result = $result;
}

View File

@@ -5,13 +5,14 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Result set single row.
*
* @author David Grudl
* @package dibi
*/
class Row implements \ArrayAccess, \IteratorAggregate, \Countable
class DibiRow implements ArrayAccess, IteratorAggregate, Countable
{
public function __construct($arr)
@@ -32,28 +33,21 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
* Converts value to DateTime object.
* @param string key
* @param string format
* @return \DateTime
* @return DateTime
*/
public function asDateTime($key, $format = NULL)
{
$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', ...
return NULL;
}
$time = new DateTime($time);
$time = new DibiDateTime($time);
}
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**/
@@ -65,7 +59,7 @@ class Row implements \ArrayAccess, \IteratorAggregate, \Countable
final public function getIterator()
{
return new \ArrayIterator($this);
return new ArrayIterator($this);
}

View File

@@ -5,39 +5,38 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL translator.
*
* @author David Grudl
* @package dibi
*/
final class Translator
final class DibiTranslator extends DibiObject
{
use Strict;
/** @var Connection */
/** @var DibiConnection */
private $connection;
/** @var Driver */
/** @var IDibiDriver */
private $driver;
/** @var int */
private $cursor = 0;
private $cursor;
/** @var array */
private $args;
/** @var string[] */
private $errors;
/** @var bool */
private $hasError;
/** @var bool */
private $comment = FALSE;
private $comment;
/** @var int */
private $ifLevel = 0;
private $ifLevel;
/** @var int */
private $ifLevelStart = 0;
private $ifLevelStart;
/** @var int */
private $limit;
@@ -45,39 +44,51 @@ final class Translator
/** @var int */
private $offset;
/** @var HashMap */
/** @var DibiHashMap */
private $identifiers;
public function __construct(Connection $connection)
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
$this->driver = $connection->getDriver();
$this->identifiers = new HashMap([$this, 'delimite']);
$this->identifiers = new DibiHashMap(array($this, 'delimite'));
}
/**
* Generates SQL. Can be called only once.
* Generates SQL.
* @param array
* @return string
* @throws Exception
* @throws DibiException
*/
public function translate(array $args)
{
if (!$this->driver) {
$this->driver = $this->connection->getDriver();
}
$args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]);
}
$this->args = $args;
$this->limit = -1;
$this->offset = 0;
$this->hasError = FALSE;
$commandIns = NULL;
$lastArr = NULL;
// shortcuts
$cursor = & $this->cursor;
$cursor = 0;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
$comment = & $this->comment;
$comment = FALSE;
// iterate
$sql = [];
$sql = array();
while ($cursor < count($this->args)) {
$arg = $this->args[$cursor];
$cursor++;
@@ -106,11 +117,11 @@ final class Translator
)/xs',
*/ // 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',
[$this, 'cb'],
array($this, 'cb'),
substr($arg, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
throw new DibiPcreException;
}
}
continue;
@@ -121,24 +132,26 @@ final class Translator
continue;
}
if ($arg instanceof \Traversable) {
if ($arg instanceof Traversable) {
$arg = iterator_to_array($arg);
}
if (is_array($arg) && is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
if ($lastArr === $cursor - 1) {
$sql[] = ',';
if (is_array($arg)) {
if (is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
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
@@ -152,12 +165,12 @@ final class Translator
$sql = implode(' ', $sql);
if ($this->errors) {
throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql);
if ($this->hasError) {
throw new DibiException('SQL translate error', 0, $sql);
}
// apply limit
if ($this->limit !== NULL || $this->offset !== NULL) {
if ($this->limit > -1 || $this->offset > 0) {
$this->driver->applyLimit($sql, $this->limit, $this->offset);
}
@@ -177,13 +190,17 @@ final class Translator
return '...';
}
if (!$this->driver) {
$this->driver = $this->connection->getDriver();
}
// array processing (with or without modifier)
if ($value instanceof \Traversable) {
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
$vx = $kx = [];
$vx = $kx = array();
switch ($modifier) {
case 'and':
case 'or': // key=val AND key IS NULL AND ...
@@ -199,7 +216,7 @@ final class Translator
$v = $this->formatValue($v, FALSE);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
} elseif ($pair[1] === 'ex') {
} elseif ($pair[1] === 'ex') { // TODO: this will be removed
$vx[] = $k . $this->formatValue($v, 'ex');
} else {
@@ -265,13 +282,15 @@ final class Translator
if (is_array($v)) {
if (isset($proto)) {
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 {
$proto = array_keys($v);
}
} 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
@@ -300,7 +319,8 @@ final class Translator
case 'ex':
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
foreach ($value as $v) {
@@ -313,36 +333,23 @@ final class Translator
// with modifier procession
if ($modifier) {
if ($value !== NULL && !is_scalar($value)) { // array is already processed
if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) {
$modifier = 'SQL';
} 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**";
}
if ($value !== NULL && !is_scalar($value) && !$value instanceof DateTime && !$value instanceof DateTimeInterface) { // array is already processed
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
}
switch ($modifier) {
case 's': // string
return $value === NULL ? 'NULL' : $this->driver->escapeText($value);
case 'bin':// binary
return $value === NULL ? 'NULL' : $this->driver->escapeBinary($value);
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':
return $value == '' ? 'NULL' : $this->driver->escapeText($value); // notice two equal signs
case 'in': // deprecated
trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED);
// intentionally break omitted
return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
case 'iN': // signed int or NULL
case 'in': // deprecated
if ($value == '') {
$value = NULL;
}
@@ -350,33 +357,33 @@ final class Translator
case 'i': // signed int
case 'u': // unsigned int, ignored
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && preg_match('#[+-]?\d++(?:e\d+)?\z#A', $value)) {
return $value; // support for long numbers - keep them unchanged
} elseif (is_string($value) && substr($value, 1, 1) === 'x' && is_numeric($value)) {
trigger_error('Support for hex strings has been deprecated.', E_USER_DEPRECATED);
return (string) hexdec($value);
// support for long numbers - keep them unchanged
if (is_string($value) && preg_match('#[+-]?\d++(e\d+)?\z#A', $value)) {
return $value;
} else {
return (string) (int) $value;
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
}
case 'f': // float
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && is_numeric($value) && substr($value, 1, 1) !== 'x') {
return $value; // support for extreme numbers - keep them unchanged
// support for extreme numbers - keep them unchanged
if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) {
return $value; // something like -9E-005 is accepted by SQL, HEX values are not
} else {
return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
}
case 'd': // date
case 't': // datetime
case 'dt': // datetime
if ($value === NULL) {
return 'NULL';
} 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':
@@ -392,11 +399,11 @@ final class Translator
$value = substr($value, 0, $toSkip)
. preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
[$this, 'cb'],
array($this, 'cb'),
substr($value, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
throw new DibiPcreException;
}
}
return $value;
@@ -418,18 +425,19 @@ final class Translator
case 'a':
case 'l':
case 'v':
$type = gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
default:
return $this->errors[] = "**Unknown or unexpected modifier %$modifier**";
$this->hasError = TRUE;
return "**Unknown or invalid modifier %$modifier**";
}
}
// without modifier procession
if (is_string($value)) {
return $this->driver->escapeText($value);
return $this->driver->escape($value, dibi::TEXT);
} elseif (is_int($value)) {
return (string) $value;
@@ -438,20 +446,20 @@ final class Translator
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
} elseif (is_bool($value)) {
return $this->driver->escapeBool($value);
return $this->driver->escape($value, dibi::BOOL);
} elseif ($value === NULL) {
return 'NULL';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
return $this->driver->escapeDateTime($value);
} elseif ($value instanceof DateTime || $value instanceof DateTimeInterface) {
return $this->driver->escape($value, dibi::DATETIME);
} elseif ($value instanceof Literal) {
} elseif ($value instanceof DibiLiteral) {
return (string) $value;
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Unexpected $type**";
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
}
}
@@ -480,7 +488,8 @@ final class Translator
$cursor = & $this->cursor;
if ($cursor >= count($this->args)) {
return $this->errors[] = '**Extra placeholder**';
$this->hasError = TRUE;
return '**Extra placeholder**';
}
$cursor++;
@@ -492,7 +501,8 @@ final class Translator
$cursor = & $this->cursor;
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') {
@@ -532,23 +542,17 @@ final class Translator
return '';
} elseif ($mod === 'lmt') { // apply limit
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(limit $arg)";
} else {
$this->limit = (int) $arg;
if ($this->args[$cursor] !== NULL) {
$this->limit = (int) $this->args[$cursor];
}
$cursor++;
return '';
} elseif ($mod === 'ofs') { // apply offset
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(offset $arg)";
} else {
$this->offset = (int) $arg;
if ($this->args[$cursor] !== NULL) {
$this->offset = (int) $this->args[$cursor];
}
$cursor++;
return '';
} else { // default processing
@@ -568,13 +572,14 @@ final class Translator
return $this->identifiers->{$matches[2]};
} 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: "..."
return $this->driver->escapeText(str_replace('""', '"', $matches[6]));
return $this->driver->escape(str_replace('""', '"', $matches[6]), dibi::TEXT);
} elseif ($matches[7]) { // string quote
return $this->errors[] = '**Alone quote**';
$this->hasError = TRUE;
return '**Alone quote**';
}
if ($matches[8]) { // SQL identifier substitution
@@ -583,7 +588,7 @@ final class Translator
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');
}
@@ -599,7 +604,7 @@ final class Translator
$parts = explode('.', $value);
foreach ($parts as & $v) {
if ($v !== '*') {
$v = $this->driver->escapeIdentifier($v);
$v = $this->driver->escape($v, dibi::IDENTIFIER);
}
}
return implode('.', $parts);

View File

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

View File

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

BIN
examples/data/sample.sdb Normal file

Binary file not shown.

View File

@@ -4,22 +4,22 @@
<?php
require __DIR__ . '/../src/loader.php';
require __DIR__ . '/../dibi/dibi.php';
dibi::connect([
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
));
// retrieve database reflection
$database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->getName()}'</h2>\n";
echo "<h2>Database '{$database->name}'</h2>\n";
echo "<ul>\n";
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";
@@ -27,12 +27,12 @@ echo "</ul>\n";
// table reflection
$table = $database->getTable('products');
echo "<h2>Table '{$table->getName()}'</h2>\n";
echo "<h2>Table '{$table->name}'</h2>\n";
echo "Columns\n";
echo "<ul>\n";
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";
@@ -40,9 +40,9 @@ echo "</ul>\n";
echo 'Indexes';
echo "<ul>\n";
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) {
echo $column->getName(), ', ';
echo "$column->name, ";
}
echo ")</li>\n";
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,26 +4,26 @@
<?php
require __DIR__ . '/../src/loader.php';
require __DIR__ . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
dibi::connect([
dibi::connect(array(
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'formatDate' => "'Y-m-d'",
'formatDateTime' => "'Y-m-d H-i-s'",
]);
));
// generate and dump SQL
dibi::test('
INSERT INTO [mytable]', [
INSERT INTO [mytable]', array(
'id' => 123,
'date' => new DateTime('12.3.2007'),
'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')

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,7 @@
[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)
[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)
=========================================================
[![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 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)
[![License](https://img.shields.io/badge/license-New%20BSD-blue.svg)](https://github.com/dg/dibi/blob/master/license.md)
Database access functions in PHP are not standardised. This library
hides the differences between them, and above all, it gives you a very handy interface.
@@ -14,29 +10,29 @@ The best way to install Dibi is to use a [Composer](https://getcomposer.org/down
php composer.phar require dibi/dibi
Or you can download the latest package from https://dibiphp.com. In this
Or you can download the latest package from http://dibiphp.com. In this
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.
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
--------
Refer to the `examples` directory for examples. Dibi documentation is
available on the [homepage](https://dibiphp.com).
available on the [homepage](http://dibiphp.com).
Connect to database:
```php
// connect to database (static way)
dibi::connect([
dibi::connect(array(
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
]);
));
// or object way; in all other examples use $connection-> instead of dibi::
$connection = new DibiConnection($options);
@@ -47,19 +43,19 @@ SELECT, INSERT, UPDATE
```php
dibi::query('SELECT * FROM users WHERE id = ?', $id);
$arr = [
$arr = array(
'name' => 'John',
'is_admin' => TRUE,
];
);
dibi::query('INSERT INTO users', $arr);
// INSERT INTO users (`name`, `is_admin`) VALUES ('John', 1)
dibi::query('UPDATE users SET', $arr, 'WHERE `id`=?', $x);
// 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'),
]);
));
// UPDATE users SET 'title' = SHA1('tajneheslo')
```
@@ -82,10 +78,10 @@ foreach ($result as $n => $row) {
Modifiers for arrays:
```php
dibi::query('SELECT * FROM users WHERE %and', [
dibi::query('SELECT * FROM users WHERE %and', array(
array('number > ?', 10),
array('number < ?', 100),
]);
));
// SELECT * FROM users WHERE (number > 10) AND (number < 100)
```
@@ -117,9 +113,9 @@ dibi::query("SELECT * FROM table WHERE name LIKE %like~", $query);
DateTime:
```php
dibi::query('UPDATE users SET', [
dibi::query('UPDATE users SET', array(
'time' => new DateTime,
]);
));
// 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,558 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use PDO;
/**
* The dibi driver for PDO.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - version
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var PDO Connection resource */
private $connection;
/** @var \PDOStatement Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/** @var string */
private $serverVersion;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
$foo = & $config['dsn'];
$foo = & $config['options'];
Dibi\Helpers::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
unset($config['resource'], $config['pdo']);
} else {
try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
}
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->serverVersion = isset($config['version'])
? $config['version']
: @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); // @ - may be not supported
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection = NULL;
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
// must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1];
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows !== FALSE) {
return;
}
} else {
$res = $this->connection->query($sql);
if ($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);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertId();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Returns the connection resource.
* @return PDO
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new MySqlReflector($this);
case 'sqlite':
return new SqliteReflector($this);
default:
throw new Dibi\NotSupportedException;
}
}
/**
* Result set driver factory.
* @param \PDOStatement
* @return Dibi\ResultDriver
*/
public function createResultDriver(\PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @return string encoded value
*/
public function escapeText($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_STR);
}
}
public function escapeBinary($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
}
public function escapeIdentifier($value)
{
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
case 'dblib':
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
return $value;
}
}
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.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
switch ($this->driverName) {
case 'mysql':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'oci':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'pgsql':
$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 = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc':
case 'mssql':
case 'dblib':
case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default:
throw new Dibi\NotImplementedException;
}
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
switch ($this->driverName) {
case 'mysql':
if ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'pgsql':
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
if ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'oci':
if ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'mssql':
case 'sqlsrv':
case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
if ($limit !== NULL) {
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
} elseif ($offset) {
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
}
break;
}
// intentionally break omitted
case 'odbc':
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break;
}
// intentionally break omitted
default:
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return $this->resultSet->rowCount();
}
/**
* 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 $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
}
/**
* 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
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* 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
* @throws Dibi\Exception
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new Dibi\NotSupportedException('Driver does not support meta data.');
}
$row = $row + [
'table' => NULL,
'native_type' => 'VAR_STRING',
];
$columns[] = [
'name' => $row['name'],
'table' => $row['table'],
'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'],
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return \PDOStatement
*/
public function getResultResource()
{
return $this->resultSet;
}
}

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|STRING' => Type::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => 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,152 +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();
}
if (self::$extMethods === NULL) { // for backwards compatibility
$list = get_defined_functions();
foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce);
if (count($pair) === 2) {
trigger_error("Extension method defined as $fce() is deprecated, use $class::extensionMethod('$name', ...).", E_USER_DEPRECATED);
self::$extMethods[$pair[1]][(new ReflectionClass($pair[0]))->getName()] = $fce;
self::$extMethods[$pair[1]][''] = NULL;
}
}
}
$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,144 +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\MsSqlDriver' => 'Drivers/MsSqlDriver.php',
'Dibi\Drivers\MsSqlReflector' => 'Drivers/MsSqlReflector.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',
'DibiMsSqlDriver' => 'Dibi\Drivers\MsSqlDriver',
'DibiMsSqlReflector' => 'Dibi\Drivers\MsSqlReflector',
'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',
]);

4
tests/.gitignore vendored
View File

@@ -1,4 +0,0 @@
output
/tmp
/test.log
/databases.ini

View File

@@ -1,89 +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
;[sqlsrv 2008]
;driver = sqlsrv
;host = "(local)\SQL2008R2SP2"
;database = master
;username = sa
;password = "Password12!"
;system = sqlsrv
;[sqlsrv 2008-pdo]
;driver = pdo
;dsn = "sqlsrv:Server=(local)\SQL2008R2SP2;Database=master"
;username = sa
;password = "Password12!"
;system = sqlsrv
[sqlsrv 2012]
driver = sqlsrv
host = "(local)\SQL2012SP1"
database = master
username = sa
password = "Password12!"
system = sqlsrv
;[sqlsrv 2012-pdo]
;driver = pdo
;dsn = "sqlsrv:Server=(local)\SQL2012SP1;Database=master"
;username = sa
;password = "Password12!"
;system = sqlsrv
[sqlsrv 2014]
driver = sqlsrv
host = "(local)\SQL2014"
database = master
username = sa
password = "Password12!"
system = sqlsrv
;[sqlsrv 2014-pdo]
;driver = pdo
;dsn = "sqlsrv:Server=(local)\SQL2014;Database=master"
;username = sa
;password = "Password12!"
;system = sqlsrv

View File

@@ -1,106 +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
[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
[mssql]
driver = mssql
host = 127.0.0.1
username = dibi
password =
system = mssql
[mssql pdo]
driver = pdo
host = mssql:host=127.0.0.1;dbname=dibi_test
username = dibi
password =
system = mssql
[sqlsrv]
driver = sqlsrv
host = (local)
username = dibi
password =
system = sqlsrv
[sqlsrv pdo]
driver = pdo
dsn = "sqlsrv:Server=127.0.0.1"
username = dibi
password =
system = sqlsrv
[oracle]
driver = oracle
username = dibi
password =
system = oracle
[oracle pdo]
driver = pdo
dsn = "oci:dbname=dibi"
username = dibi
password =
system = oracle
[firebird]
driver = firebird
database = database.fdb
username = dibi
password =
system = firebird

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,35 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
$conn->query('INSERT INTO products', [
'title' => 'Test product',
]);
Assert::same(1, $conn->getAffectedRows());
$conn->query('UPDATE products SET title="xxx" WHERE product_id > 100');
Assert::same(0, $conn->getAffectedRows());
$conn->query('UPDATE products SET title="xxx"');
Assert::same(4, $conn->getAffectedRows());
$conn->query('DELETE FROM orders');
$conn->query('DELETE FROM products WHERE product_id > 100');
Assert::same(0, $conn->getAffectedRows());
$conn->query('DELETE FROM products WHERE product_id < 3');
Assert::same(2, $conn->getAffectedRows());

View File

@@ -1,38 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
use Dibi\Connection;
require __DIR__ . '/bootstrap.php';
test(function () use ($config) {
$conn = new Connection($config);
Assert::true($conn->isConnected());
$conn->disconnect();
Assert::false($conn->isConnected());
});
test(function () use ($config) { // lazy
$conn = new Connection($config + ['lazy' => TRUE]);
Assert::false($conn->isConnected());
$conn->query('SELECT 1');
Assert::true($conn->isConnected());
});
test(function () use ($config) { // query string
$conn = new Connection(http_build_query($config, NULL, '&'));
Assert::true($conn->isConnected());
Assert::null($conn->getConfig('lazy'));
Assert::same($config['driver'], $conn->getConfig('driver'));
Assert::type('Dibi\Driver', $conn->getDriver());
});

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] ORDER BY [product_id]');
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(in_array($conn->getConfig('system'), ['odbc', 'sqlsrv']) ? '
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,56 +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 [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

@@ -1,48 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
/*Assert::exception(function () use ($conn) {
$conn->rollback();
}, 'Dibi\Exception');
Assert::exception(function () use ($conn) {
$conn->commit();
}, 'Dibi\Exception');
$conn->begin();
Assert::exception(function () use ($conn) {
$conn->begin();
}, 'Dibi\Exception');
*/
$conn->begin();
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
$conn->query('INSERT INTO [products]', [
'title' => 'Test product',
]);
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
$conn->rollback();
Assert::same(3, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());
$conn->begin();
$conn->query('INSERT INTO [products]', [
'title' => 'Test product',
]);
$conn->commit();
Assert::same(4, (int) $conn->query('SELECT COUNT(*) FROM [products]')->fetchSingle());

View File

@@ -1,160 +0,0 @@
<?php
use Tester\Assert;
use Dibi\Row;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
$ds = $conn->dataSource('SELECT * FROM products');
Assert::match(
reformat('
SELECT *
FROM (SELECT * FROM products) t'),
(string) $ds
);
Assert::same(3, $ds->count());
Assert::same(3, $ds->getTotalCount());
Assert::same(
reformat('SELECT COUNT(*) FROM (SELECT * FROM products) t'),
dibi::$sql
);
$ds->select('title');
$ds->orderBy('title', dibi::DESC);
$ds->where('title like "%a%"');
Assert::match(
reformat("
SELECT [title]
FROM (SELECT * FROM products) t
WHERE (title like '%a%')
ORDER BY [title] DESC
"),
(string) $ds
);
$ds->select('product_id');
$ds->orderBy('product_id', dibi::ASC);
$ds->where('product_id = %i', 1);
Assert::match(
reformat("
SELECT [title], [product_id]
FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1)
ORDER BY [title] DESC, [product_id] ASC
"),
(string) $ds
);
$ds->select(['product_id']);
$ds->orderBy(['product_id' => dibi::ASC]);
$ds->where(['product_id = 1']);
Assert::match(
reformat("
SELECT [product_id]
FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC
"),
(string) $ds
);
Assert::same(1, $ds->count());
Assert::same(3, $ds->getTotalCount());
Assert::match(reformat("SELECT COUNT(*) FROM (
SELECT [product_id]
FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC
) t"), dibi::$sql);
Assert::same(1, $ds->toDataSource()->count());
Assert::equal([
new Row([
'product_id' => 1,
]),
], iterator_to_array($ds));
Assert::match(
reformat("
SELECT [product_id]
FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC
"),
dibi::$sql
);
$fluent = $ds->toFluent();
Assert::same(1, $fluent->count());
Assert::match(
reformat("SELECT * FROM (
SELECT [product_id]
FROM (SELECT * FROM products) t
WHERE (title like '%a%') AND (product_id = 1) AND (product_id = 1)
ORDER BY [product_id] ASC
) t"),
(string) $fluent
);
$ds = $conn->select('title')->from('products')->toDataSource();
Assert::match(
reformat('
SELECT *
FROM (SELECT [title] FROM [products]) t'),
(string) $ds
);
Assert::equal(new Row([
'product_id' => 1,
'title' => 'Chair',
]), $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 => 'Chair', 'Table', 'Computer'],
$conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchPairs()
);
Assert::equal([
1 => new Row([
'product_id' => 1,
'title' => 'Chair',
]),
new Row([
'product_id' => 2,
'title' => 'Table',
]),
new Row([
'product_id' => 3,
'title' => 'Computer',
]),
], $conn->dataSource('SELECT * FROM products ORDER BY product_id')->fetchAssoc('product_id'));
$ds = new Dibi\DataSource('products', $conn);
Assert::match(
reformat('
SELECT *
FROM [products]'),
(string) $ds
);
Assert::same(3, $ds->count());
Assert::same(3, $ds->getTotalCount());
Assert::same(reformat('SELECT COUNT(*) FROM [products]'), dibi::$sql);

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

@@ -1,36 +0,0 @@
<?php
use Tester\Assert;
use Dibi\Fluent;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$fluent = new Fluent($conn);
$fluent->select('*')->from('table')->where('x=1');
$dolly = clone $fluent;
$dolly->where('y=1');
$dolly->clause('FOO');
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);
$fluent = new Fluent($conn);
$fluent->select('id')->from('table')->where('id = %i', 1);
$dolly = clone $fluent;
$dolly->where('cd = %i', 5);
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);
$fluent = new Fluent($conn);
$fluent->select('*')->from('table');
$dolly = clone $fluent;
$dolly->removeClause('select')->select('count(*)');
Assert::same(reformat('SELECT * FROM [table]'), (string) $fluent);
Assert::same(reformat('SELECT count(*) FROM [table]'), (string) $dolly);

View File

@@ -1,45 +0,0 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$fluent = $conn->delete('table')->as('bAlias')
->setFlag('IGNORE');
Assert::same(
reformat('DELETE IGNORE FROM [table] AS [bAlias]'),
(string) $fluent
);
$fluent->removeClause('from')->from('anotherTable');
Assert::same(
reformat('DELETE IGNORE FROM [anotherTable]'),
(string) $fluent
);
$fluent->using('thirdTable');
Assert::same(
reformat('DELETE IGNORE FROM [anotherTable] USING [thirdTable]'),
(string) $fluent
);
$fluent->setFlag('IGNORE', FALSE);
Assert::same(
reformat('DELETE FROM [anotherTable] USING [thirdTable]'),
(string) $fluent
);
$fluent->limit(10);
Assert::same(
reformat('DELETE FROM [anotherTable] USING [thirdTable] LIMIT 10'),
(string) $fluent
);

View File

@@ -1,101 +0,0 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
class MockDriver extends Dibi\Drivers\SqlsrvDriver
{
function __construct()
{}
function connect(array & $config)
{}
function query($sql)
{
return $this;
}
function getResultColumns()
{
return [];
}
function fetch($assoc)
{
return FALSE;
}
}
$config['driver'] = new MockDriver;
$conn = new Dibi\Connection($config);
// fetch & limit
$fluent = $conn->select('*')
->from('customers')
->limit(1)
->orderBy('customer_id');
Assert::same(
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent
);
$fluent->fetch();
Assert::same(
'SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t',
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
$fluent->fetchAll(0, 3);
Assert::same(
reformat('SELECT TOP (3) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
Assert::same(
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent
);
$fluent->limit(0);
$fluent->fetch();
Assert::same(
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
Assert::same(
reformat('SELECT TOP (0) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
(string) $fluent
);
$fluent->removeClause('limit');
$fluent->removeClause('offset');
$fluent->fetch();
Assert::same(
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat('SELECT TOP (1) * FROM ( SELECT * FROM [customers] ORDER BY [customer_id]) t'),
dibi::$sql
);
Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id]'),
(string) $fluent
);

View File

@@ -1,97 +0,0 @@
<?php
/**
* @dataProvider? ../databases.ini mysql
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
// fetch & limit
$fluent = $conn->select('*')
->from('customers')
->limit(1)
->offset(3)
->orderBy('customer_id');
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent
);
$fluent->fetch();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
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
);
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
(string) $fluent
);
$fluent->limit(0);
$fluent->fetch();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
dibi::$sql
);
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 0 OFFSET 3'),
(string) $fluent
);
$fluent->removeClause('limit');
$fluent->fetch();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1 OFFSET 3'),
dibi::$sql
);
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 18446744073709551615 OFFSET 3'),
(string) $fluent
);
$fluent->removeClause('offset');
$fluent->fetch();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'),
dibi::$sql
);
$fluent->fetchSingle();
Assert::same(
reformat(' SELECT * FROM [customers] ORDER BY [customer_id] LIMIT 1'),
dibi::$sql
);
Assert::same(
reformat('SELECT * FROM [customers] ORDER BY [customer_id]'),
(string) $fluent
);

View File

@@ -1,50 +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->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([
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());
// more complex association array
if (!in_array($config['system'], ['odbc', 'sqlsrv'])) {
$res = $conn->select(['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([
'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)]),
],
], $res->fetchAssoc('name,title'));
}

View File

@@ -1,58 +0,0 @@
<?php
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$arr = [
'title' => 'Super Product',
'price' => 12,
'brand' => NULL,
];
$fluent = $conn->insert('table', $arr)
->setFlag('IGNORE')->setFlag('DELAYED');
Assert::same(
reformat('INSERT IGNORE DELAYED INTO [table] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL)'),
(string) $fluent
);
$fluent->setFlag('IGNORE', FALSE);
Assert::same(
reformat('INSERT DELAYED INTO [table] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL)'),
(string) $fluent
);
$fluent->setFlag('HIGH_priority');
Assert::same(
reformat('INSERT DELAYED HIGH_PRIORITY INTO [table] ([title], [price], [brand]) VALUES (\'Super Product\', 12, NULL)'),
(string) $fluent
);
$fluent->into('anotherTable');
Assert::same(
reformat('INSERT DELAYED HIGH_PRIORITY INTO [anotherTable] VALUES (\'Super Product\', 12, NULL)'),
(string) $fluent
);
$fluent->values('%l', $arr);
Assert::same(
reformat('INSERT DELAYED HIGH_PRIORITY INTO [anotherTable] VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'),
(string) $fluent
);
$fluent->values($arr);
Assert::same(
reformat('INSERT DELAYED HIGH_PRIORITY INTO [anotherTable] VALUES (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL) , (\'Super Product\', 12, NULL)'),
(string) $fluent
);

View File

@@ -1,158 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$conn = new Dibi\Connection($config);
$max = 10;
$min = 5;
$fluent = $conn->select('*')
->select('a')
->select('b')->as('bAlias')
->select(['c', 'd', 'e'])
->select('%n', 'd');
Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d]'),
(string) $fluent
);
$fluent->from('table')->as('tableAlias')
->innerJoin('table1')->on('table.col = table1.col')
->innerJoin('table2')->on('table.col = table2.col');
Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [tableAlias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col'),
(string) $fluent
);
$fluent->from('anotherTable');
Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [table] AS [tableAlias] INNER JOIN [table1] ON table.col = table1.col INNER JOIN [table2] ON table.col = table2.col , [anotherTable]'),
(string) $fluent
);
$fluent->removeClause('from')->from('anotherTable');
Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable]'),
(string) $fluent
);
$fluent->as('anotherAlias')
->clause('from')
->innerJoin('table3')
->on('table.col = table3.col');
Assert::same(
reformat('SELECT * , [a] , [b] AS [bAlias] , [c], [d], [e] , [d] FROM [anotherTable] AS [anotherAlias] INNER JOIN [table3] ON table.col = table3.col'),
(string) $fluent
);
$fluent->where('col > %i', $max)
->or('col < %i', $min)
->where('active = 1')
->where('col')->in([1, 2, 3])
->orderBy('val')->asc()
->orderBy('[val2] DESC')
->orderBy(['val3' => -1]);
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'),
(string) $fluent
);
$fluent->orderBy(Dibi\Fluent::REMOVE);
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)'),
(string) $fluent
);
$fluent = $conn->select('*')
->select(
$conn->select('count(*)')
->from('precteni')->as('P')
->where('P.id_clanku', '=', 'C.id_clanku')
)
->from('clanky')->as('C')
->where('id_clanku=%i', 123)
->limit(1)
->offset(0);
Assert::same(
reformat([
'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',
'sqlsrv' => ' SELECT * , (SELECT count(*) FROM [precteni] AS [P] WHERE P.id_clanku = C.id_clanku) FROM [clanky] AS [C] WHERE id_clanku=123 OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY',
' 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
);
$fluent = $conn->select('*')
->select(['x' => 'xAlias'])
->from('products')
->innerJoin('orders')->using('(product_id)')
->innerJoin('customers')->using('([customer_id])')
->innerJoin('items')->using('(%n)', ['customer_id', 'order_id']);
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])'),
(string) $fluent
);
$fluent = $conn->command()->select()
->from('products')
->select('*')
->innerJoin('orders')->using('(product_id)');
Assert::same(
reformat('SELECT * FROM [products] INNER JOIN [orders] USING (product_id)'),
(string) $fluent
);
$fluent = $conn->select('*')
->from(['me' => 't'])
->where('col > %i', $max)
->where(['x' => 'a', 'b', 'c']);
Assert::same(
reformat('SELECT * FROM [me] AS [t] WHERE col > 10 AND ([x] = \'a\') AND (b) AND (c)'),
(string) $fluent
);
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')
->where('x IN (%SQL)', $conn->select('id')->from('xyz'));
Assert::same(
reformat('SELECT * FROM [abc] WHERE x IN ((SELECT [id] FROM [xyz]))'),
(string) $fluent
);

View File

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

View File

@@ -1,36 +0,0 @@
<?php
use Dibi\Helpers;
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
Assert::same(NULL, Helpers::getSuggestion([], ''));
Assert::same(NULL, Helpers::getSuggestion([], 'a'));
Assert::same(NULL, Helpers::getSuggestion(['a'], 'a'));
Assert::same('a', Helpers::getSuggestion(['a', 'b'], ''));
Assert::same('b', Helpers::getSuggestion(['a', 'b'], 'a')); // ignore 100% match
Assert::same('a1', Helpers::getSuggestion(['a1', 'a2'], 'a')); // take first
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'a'));
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'ab'));
Assert::same(NULL, Helpers::getSuggestion(['aaa', 'bbb'], 'abc'));
Assert::same('bar', Helpers::getSuggestion(['foo', 'bar', 'baz'], 'baz'));
Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'acbd'));
Assert::same('abcd', Helpers::getSuggestion(['abcd'], 'axbd'));
Assert::same(NULL, Helpers::getSuggestion(['abcd'], 'axyd'));
/*
length allowed ins/del replacements
-------------------------------------
0 1 0
1 1 1
2 1 1
3 1 1
4 2 1
5 2 2
6 2 2
7 2 2
8 3 2
*/

View File

@@ -1,30 +0,0 @@
<?php
/**
* @dataProvider? ../databases.ini postgre
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
$tests = function ($conn) {
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A_B')->fetchSingle());
Assert::true($conn->query("SELECT 'AA_BB' LIKE %~like~", 'A_B')->fetchSingle());
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A%B')->fetchSingle());
Assert::true($conn->query("SELECT 'AA%BB' LIKE %~like~", 'A%B')->fetchSingle());
Assert::same('AA\\BB', $conn->query("SELECT 'AA\\BB'")->fetchSingle());
Assert::false($conn->query("SELECT 'AAxBB' LIKE %~like~", 'A\\B')->fetchSingle());
Assert::true($conn->query("SELECT 'AA\\BB' LIKE %~like~", 'A\\B')->fetchSingle());
};
$conn = new Dibi\Connection($config);
$conn->query('SET escape_string_warning = off'); // do not log warnings
$conn->query('SET standard_conforming_strings = on');
$tests($conn);
$conn->query('SET standard_conforming_strings = off');
$tests($conn);

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