1
0
mirror of https://github.com/dg/dibi.git synced 2025-08-30 01:09:50 +02:00

Compare commits

...

145 Commits
v2.3.4 ... v3.0

Author SHA1 Message Date
David Grudl
da9c42d540 loader: added missing classes 2018-08-22 15:41:19 +02:00
David Grudl
e8559898f1 Released version 3.0.9 2018-03-09 12:56:49 +01:00
David Grudl
ece85e8b48 appveyor: PHP is downloaded using cURL
"03/mar/2018: We've upgraded the server bandwidth. This is however still not sufficient to handle all empty user agent connections. Please update the user agent in your scripts accordingly or contact us so we can discuss it."
2018-03-08 13:53:10 +01:00
David Grudl
acd5ac8616 loader: fixed missing class 'dibi.php' 2018-02-15 11:54:37 +01:00
David Grudl
44876973f5 Translator: fixed %dt with DateTimeInterface object [Closes #263] 2017-09-21 14:05:20 +02:00
David Grudl
0e5d951dfb Released version 3.0.8 2017-06-10 03:05:18 +02:00
David Grudl
126422ad7e strict fixes 2017-06-10 03:05:18 +02:00
David Grudl
277f52c928 appveyor: is unable to start MSSQL 2012 & 2014 together 2017-06-10 02:58:47 +02:00
David Grudl
80ac569621 fixed phpDoc 2017-06-10 02:58:47 +02:00
David Grudl
d9628f933d coding style: removed space after reference & 2017-06-10 02:58:47 +02:00
David Grudl
f7009f3e0c Tracy\Panel: better typography 2017-06-10 02:58:47 +02:00
Jiří Trávníček
36d30c1fcf Dibi\Bridges\Tracy\Panel: host added to tracy panel [closes #250] (#251) 2017-06-10 02:58:44 +02:00
David Grudl
ae6c8756b6 Tracy\Panel: one panel is used per connection 2017-06-10 02:58:06 +02:00
David Grudl
6b2c996b16 Tracy\Panel: dump() may fails
related: https://forum.nette.org/cs/26790-error-dibi-bridges-tracy-panel-oracle
917971992f (commitcomment-18444224)
2017-06-09 12:12:07 +02:00
David Grudl
718c617764 travis: removed HHVM 2017-06-09 12:12:07 +02:00
David Grudl
6194152e67 examples: tracy is loaded before output [Closes #248] 2017-06-09 12:12:07 +02:00
David Grudl
d39603e23d updated .gitattributes 2017-06-09 12:12:07 +02:00
David Grudl
22e6ea4e40 Released version 3.0.7 2017-01-04 15:18:17 +01:00
David Grudl
f0d2c3f414 travis: removed PHP 7.1 from allowed failures 2017-01-04 15:18:17 +01:00
David Grudl
873ed3115d Connection::translateArgs() is protected [Closes #237] 2017-01-04 15:18:17 +01:00
Martin Dzíbela
73289d0569 PostgreSQL: add support for more than one schema in search_path (#239) 2017-01-04 15:18:17 +01:00
David Grudl
7b899ddda5 updated contributing.md, added GitHub templates 2017-01-04 15:18:17 +01:00
Ondřej Mirtes
8860268791 typos (#243) 2016-12-03 19:29:29 +01:00
Honza Machala
551b576271 Typos (#242) 2016-11-14 13:47:45 +01:00
David Grudl
74d0a78ec2 Result, Row: added support for datetime like 'Jun 17 2015 12:00:00:000AM' [Closes #180] 2016-09-02 17:36:37 +02:00
Aleš Culek
be7c3f095d Implemented OracleDriver::getColumns() (#233) 2016-08-09 22:37:45 +02:00
Radovan Kepák
3a6dc07da8 FirebirdDriver: Fixed DriverException throw (#231)
Fixed fetch Dibi\DriverException throw and more PHP warnings silenced
2016-08-09 22:36:44 +02:00
David Grudl
9b070bb737 Released version 3.0.6 2016-07-31 16:50:07 +02:00
David Grudl
917971992f Bridges\Tracy\Panel: silenced PHP warning
https://forum.nette.org/cs/26790-error-dibi-bridges-tracy-panel-oracle
2016-07-27 14:01:46 +02:00
Radovan Kepák
bc564555f8 FirebirdDriver: silenced PHP warning
If fetch have error, PHP show Warning, this is error by my opinion as Dibi Throw Exception with information, so the Warning is useless, and even more, if we catch exception, we still have warning.
2016-07-27 13:55:15 +02:00
David Grudl
3e20a6b8fc Dibi\DateTime::__wakeup() doesn't call parent after 8e8e6dfd [Closes #228] 2016-07-27 13:28:23 +02:00
David Grudl
c019e7cac2 tests: fixed compatibility with PHP 7.1 2016-07-21 14:38:56 +02:00
David Grudl
2294c195f4 Released version 3.0.5 2016-07-20 16:15:13 +02:00
David Grudl
25246529f7 Translator, Fluent: preserve dot in name after AS [Closes #224] 2016-07-20 16:13:33 +02:00
David Grudl
d405ec369b Translator: added %N 2016-07-20 16:13:32 +02:00
David Grudl
b7974fe192 travis: added PHP 7.1 2016-07-20 15:54:58 +02:00
David Grudl
80f1898e1b tests: removed deprecated 'storage_engine' 2016-07-20 15:50:33 +02:00
Roman Pavlík
8e8e6dfdca Dibi\DateTime: provides BC for serialized older versions [Closes #226] (#227) 2016-07-20 15:50:33 +02:00
Milan Pála
6510fcce25 Disconnect on not connected driver not fail (#222)
Sometimes database go away and Connection::isConnected() is returning TRUE. Prevent this should be posibble to disconnect on closed connection without error.
2016-07-20 15:50:32 +02:00
aldria
b7d84b90ef Correct limit and offset for Firebird Driver (#221)
SELECT [FIRST (<int-expr>)] [SKIP (<int-expr>)] <columns> FROM ...
2016-05-27 03:48:06 +02:00
David Grudl
2571e54f3c composer.json: replaces all dg/dibi versions 2016-05-04 20:13:50 +02:00
Aleš Culek
15e6d9f738 Implemented OracleDriver::getAffectedRows() 2016-05-03 08:55:22 +02:00
David Grudl
ddfd4a0f1a tests/travis: reports code coverage to Coveralls 2016-04-21 11:29:10 +02:00
David Grudl
4e838fc2b5 Released version 3.0.4 2016-04-06 19:09:24 +02:00
David Grudl
37487816db removed Strict from exceptions [Closes #216] 2016-04-06 18:52:42 +02:00
David Grudl
0c099bb2bc tests: a different .ini for PHP 5 and PHP 7 2016-03-20 19:23:57 +01:00
David Grudl
1786c861b9 Merge pull request #213 from castamir/php7sqlsrv
SqlsrvDriver: php7 compatibility
2016-03-20 16:34:36 +01:00
Mira Paulik
bc0578928f SqlsrvDriver: php7 compatibility 2016-03-20 13:50:10 +01:00
David Grudl
12a07ff6ad Merge pull request #212 from soukiii/master
Fluent: missing annotation
2016-03-18 17:16:23 +01:00
Petr Soukup
b50f59c64c Fluent: missing annotation 2016-03-18 15:40:16 +01:00
David Grudl
fdebf349f5 appveyor: test under PHP 7 2016-03-18 15:07:45 +01:00
David Grudl
954c2f25d0 Merge pull request #209 from castamir/sqlsrv_insert
SqlsrvDriver::getInsertId() last inserted id is from last statement instead of last inserted row
2016-02-29 23:37:17 +01:00
Mira Paulik
43dccb1ba2 SqlsrvDriver::getInsertId() last inserted id is from last statement instead of last inserted row regardless of the table that produced the value 2016-02-29 16:10:59 +01:00
David Grudl
352a683ec1 Released version 3.0.3 2016-02-21 02:07:19 +01:00
David Grudl
f4638796fb Helpers::detectType() detects VAR_STRING as Type::TEXT 2016-02-20 21:08:01 +01:00
David Grudl
4659f4550e DibiExtension22: added options 'explain' & 'filter' [Closes #203] 2016-02-10 00:08:27 +01:00
David Grudl
c3548465fb Merge pull request #204 from castamir/sqlsrv
SqlsrvDriver: fixed: sql server does not respond on non-string password
2016-02-09 23:19:53 +01:00
David Grudl
dda0bdbd67 Merge pull request #208 from ondrej-tuhacek/master
FirebirdDriver::delimite - Quotation marks for escaping identifiers
2016-02-04 16:08:25 +01:00
Ondřej Tuháček
7142be254b FirebirdDriver::delimite - Quotation marks for escaping identifiers 2016-02-04 13:29:06 +01:00
David Grudl
8948cc293c Released version 3.0.2 2016-01-29 15:46:23 +01:00
David Grudl
193252dc5f PdoDriver::applyLimit() is the same as SqlsrvDriver 2016-01-28 20:13:49 +01:00
Mira Paulik
6b2a46bcb8 appveyor: testing with SQL Server 2012 & 2014 2016-01-28 19:55:55 +01:00
Mira Paulik
2c01d993d0 SqlsrvReflector::getTables(): gets list of all tables from dbo schema only 2016-01-28 19:54:46 +01:00
Mira Paulik
e415157206 SqlsrvDriver::applyLimit(): fixed limit and offset behaviour for odbc 11+
SqlsrvReflector: changed constrains metadata loading from INFORMATION_SCHEMA to sys schema to get complete list of all constraints, not PK only
2016-01-28 19:48:49 +01:00
David Grudl
8a7dbcba86 Revert "removed MsSqlDriver (is not available with PHP 5.3 or later; replaced with SqlsrvDriver)"
This reverts commit ac1ab26e7a.
2016-01-24 22:52:18 +01:00
Mira Paulik
3b00115ee5 SqlsrvDriver: fixed: sql server does not respond on non-string credentials 2016-01-22 17:14:12 +01:00
David Grudl
4f2a0dac97 Released version 3.0.1 2015-12-16 15:15:56 +01:00
David Grudl
a13eb6f3d1 Merge pull request #200 from michal-kocarek/master
Allow disconnecting when passing PDO instance.
2015-12-16 15:02:43 +01:00
David Grudl
e3f01c879f uses https 2015-12-14 14:26:09 +01:00
Michal Kočárek
efae5808f0 PdoDriver: unset remaining references to PDO to allow disconnection 2015-12-14 12:40:41 +01:00
David Grudl
6a8a136c0a Merge pull request #199 from janlanger/patch-1
Helpers::detectType now resolves tinyint as integer
2015-12-06 23:30:38 +01:00
Jan Langer
bbb654fc27 Helpers::detectType resolves tinyint as integer 2015-12-06 22:03:06 +01:00
David Grudl
b6933815c7 Translator: added modifier %dt [Closes #198] 2015-12-05 11:33:28 +01:00
David Grudl
a8691eb8f5 Connection::substitute() fixed [Closes #197] 2015-11-26 12:24:57 +01:00
David Grudl
0cce3b9916 Strict: added support for the old way of adding extension methods [Closes #195] 2015-11-11 13:34:23 +01:00
David Grudl
98d1b2a519 MySqliDriver::createException() removed undefined ConnectionException [Closes #194] 2015-11-10 17:51:50 +01:00
David Grudl
f327212b57 Released version 3.0.0 2015-11-07 18:55:58 +01:00
David Grudl
ff22e8de09 Helpers::getSuggestion() better balance. Replacement is more expensive than insertion/deletion. 2015-11-07 02:31:14 +01:00
Petr Soukup
044fe4fc2a Fluent: added missing annotations [Closes #191] 2015-11-06 23:31:57 +01:00
David Grudl
e9c677c750 typo, docs 2015-11-06 23:31:56 +01:00
David Grudl
607ec8ae77 tests: not supported drivers are not skipped (except for 'mysql' on PHP 7) 2015-11-04 23:36:34 +01:00
David Grudl
deb56bb166 added appveyor.yml (thanks @mhujer) 2015-11-04 23:36:33 +01:00
David Grudl
5d44e55527 travis: uses own databases.travis.ini 2015-11-04 17:40:28 +01:00
David Grudl
78d24a0e74 tests: fixes 2015-11-04 17:40:19 +01:00
David Grudl
5aab1ff023 tests: improved ini quering, removed duplicated tests 2015-11-04 17:29:51 +01:00
David Grudl
6730fb4633 tests: added missing items to databases.sample.ini & etc 2015-11-04 17:29:51 +01:00
David Grudl
ac1ab26e7a removed MsSqlDriver (is not available with PHP 5.3 or later; replaced with SqlsrvDriver) 2015-11-04 03:59:21 +01:00
David Grudl
26a66c92d9 used type callable 2015-11-03 19:01:16 +01:00
David Grudl
2bb99ef3a0 DibiExtension22: bluescreen panel is registered in production mode too 2015-11-03 19:01:16 +01:00
David Grudl
b090ec802b differentiated Type::TIME and Type::DATETIME 2015-11-03 19:01:15 +01:00
David Grudl
89e92d24a2 Connection: added literal() 2015-11-02 18:23:59 +01:00
David Grudl
2627e23701 Fluent: fixed combination of modifier and inner fluent [Closes #192] 2015-11-02 18:00:29 +01:00
David Grudl
65d6f62a5b Translator::translate() can be called only once (BC break) 2015-11-02 18:00:28 +01:00
David Grudl
0c22f3b43f Translator: DateTime can be used only with %d or %t modifiers (BC break?) 2015-11-02 18:00:28 +01:00
David Grudl
b7d922d992 Translator: Literal can be used with %sql or %SQL modifiers 2015-11-02 18:00:27 +01:00
David Grudl
a9a45f0c4c Translator: better error messages 2015-11-02 18:00:27 +01:00
David Grudl
654e345921 added test, whitespace 2015-11-02 18:00:26 +01:00
David Grudl
f2a400084f Reflection\Result: fixed case insensitivity 2015-11-02 18:00:26 +01:00
David Grudl
a3325cac4d added deprecation notices 2015-11-02 18:00:25 +01:00
David Grudl
f19dd9208a Connection::alias() & loadFile() moved to Helpers 2015-10-26 19:20:59 +01:00
David Grudl
fd5cfaa9d3 drivers: applyLimit throws exception for negative values (BC break) 2015-10-26 19:20:58 +01:00
David Grudl
1333d07833 SqlsrvDriver: added support for LIMIT & OFFSET on SQL Server 2012 2015-10-26 19:20:58 +01:00
David Grudl
c26201c75d Fluent: exports limit & offset as %lmt and %ofs [Closes #82]
Also fixes 20f2093 on MSSQL
2015-10-26 19:20:57 +01:00
David Grudl
70c68402ea Fluent: prevents doubled processing 2015-10-26 19:20:57 +01:00
Pavel Zelezny
5fac432272 Connection: option 'driver' can contain driver instance or class name [Closes #153] 2015-10-26 19:20:56 +01:00
David Grudl
b3696f9beb FirebirdDriver: removed $sql from exception in fetch() 2015-10-23 17:24:01 +02:00
David Grudl
11027e2573 MsSql2005 driver renamed to Sqlsrv 2015-10-22 02:05:32 +02:00
David Grudl
d74908402d fixes 2015-10-22 02:05:31 +02:00
David Grudl
31cd9594a1 removed unused code 2015-10-22 02:05:30 +02:00
David Grudl
52f0793991 Result::dump() moved to Helpers 2015-10-13 21:16:49 +02:00
David Grudl
c79f4a1475 MySQL drivers: type TIME is returned as DateInterval (BC break) [Closes #168] 2015-10-13 21:16:49 +02:00
David Grudl
fbb63a9cc3 implemented DriverException descendants:
- ConstraintViolationException
- ForeignKeyConstraintViolationException
- NotNullConstraintViolationException
- UniqueConstraintViolationException
2015-10-13 21:16:48 +02:00
David Grudl
3f44e96353 tests: improved sql dumps, renamed pgsql -> postgre 2015-10-13 20:09:18 +02:00
David Grudl
6994f6d11a Dibi\Exception: code can be string 2015-10-13 20:09:12 +02:00
David Grudl
e849be486f DriverException: removed tryError & catchError (BC break) 2015-10-13 20:08:26 +02:00
David Grudl
8649366b1f PostgreDriver: removed usage of tryError & catchError 2015-10-13 20:04:45 +02:00
David Grudl
1a027c75ab FirebirdDriver: removed usage of tryError & catchError (not tested!) 2015-10-13 20:03:27 +02:00
David Grudl
ee4cd0d6ef Column::detectTypes() moved to Helpers 2015-10-13 15:48:24 +02:00
David Grudl
4630e1818f Result: normalizes invalid date to NULL 2015-10-13 15:04:22 +02:00
David Grudl
c0416bf176 Result: improved normalization of type INT 2015-10-13 15:04:14 +02:00
David Grudl
407a6f7550 Result: fixed normalization of float when ends with "0" [Closes #189] 2015-10-13 15:03:59 +02:00
David Grudl
91c2be2a84 Result: normalize always converts type TEXT to string 2015-10-13 15:03:48 +02:00
David Grudl
6f273ef601 Column::detectType() return NULL when type is unknown instead of TEXT 2015-10-13 15:03:18 +02:00
David Grudl
8bfb39f790 Merge pull request #190 from bckp/patch-1
Updated docs
2015-10-13 11:54:55 +02:00
Radovan Kepák
617e5ca6da Updated docs
Docs updated to new syntax, so people use new and not old deprecated one
2015-10-13 10:36:24 +02:00
castamir
20f2093aa5 Fluent::fetch(): fixed limit clause duplication [Closes #188][Closes #186][Closes #185] 2015-10-09 12:20:15 +02:00
David Grudl
0c4a28935d Fluent: removed keyword AS from SQL [Closes #172] 2015-10-09 12:20:14 +02:00
David Grudl
2e09a559f2 moved to namespace Dibi
added loader for old class names
2015-10-09 12:20:14 +02:00
David Grudl
6222e966c7 typos 2015-10-09 12:20:13 +02:00
David Grudl
8e2feec9fb removed @package 2015-10-09 12:20:12 +02:00
David Grudl
80e2fd6cc3 DibiRow: shows suggestions for missing columns 2015-10-09 12:20:12 +02:00
David Grudl
a119921832 DibiObject: shows suggestions for undeclared members 2015-10-09 12:20:11 +02:00
David Grudl
f42d1b1611 DibiObject replaced with trait DibiStrict 2015-10-09 12:20:11 +02:00
David Grudl
76396ab250 DibiObject: simplified to minimum (BC break)
Removed support for setters, onEvent(), getClass() & getReflection().
Retained support for getters and extension methods.
2015-10-09 12:20:10 +02:00
David Grudl
ae68965710 type constants dibi::* moved to new class Type 2015-10-09 12:20:09 +02:00
David Grudl
785a021b8d dibi::dump() moved to class Helpers 2015-10-09 12:20:09 +02:00
David Grudl
59223d937d removed usage of magic properties 2015-10-09 12:20:08 +02:00
David Grudl
7c1f735f9b used PHP 5.4 syntax 2015-10-09 12:20:08 +02:00
David Grudl
a32e24262f loader.php: uses SPL autoloader 2015-10-09 12:20:07 +02:00
David Grudl
3e010c0f4d removed PHP < 5.4 stuff 2015-10-09 12:20:06 +02:00
David Grudl
96daa02525 Minimal required PHP version changed to 5.4.4 2015-10-09 12:20:06 +02:00
David Grudl
cbebcba37d IDibiDriver: escape() & unescape() replaced with escape*() & unescapeBinary() (BC break!) 2015-10-09 12:20:05 +02:00
David Grudl
7f76a8b2f4 separation of some classes into own files 2015-10-09 12:20:05 +02:00
David Grudl
2522dcd7fe new directory structure, moved to /src 2015-10-09 12:20:04 +02:00
David Grudl
c96271c09b opened 2.4-dev 2015-10-08 22:23:03 +02:00
130 changed files with 6203 additions and 4733 deletions

2
.gitattributes vendored
View File

@@ -1,4 +1,6 @@
.gitattributes export-ignore
.gitignore export-ignore
.github export-ignore
.travis.yml export-ignore
appveyor.yml export-ignore
tests/ export-ignore

16
.github/issue_template.md vendored Normal file
View File

@@ -0,0 +1,16 @@
- bug report? yes/no
- feature request? yes/no
- version: ?.?.? <!-- exact release version, for bug reports -->
### Description
...
### Steps To Reproduce
... If possible a minimal demo of the problem ...
<!--
REMEMBER, AN ISSUE IS NOT THE PLACE TO ASK QUESTIONS. We will be happy to help you on Gitter https://gitter.im/nette/nette
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. Explain your intentions. It's up to you to make a strong case to convince the project's developers of the merits of this feature.
-->

11
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,11 @@
- bug fix? yes/no <!-- #issue numbers, if any -->
- new feature? yes/no
- BC break? yes/no
<!--
Describe your changes here to communicate to the maintainers why we should accept this pull request.
Please add new tests to show the fix or feature works.
Thanks for contributing!
-->

View File

@@ -1,20 +1,14 @@
language: php
php:
- 5.3.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
allow_failures:
- php: 7.0
- php: hhvm
- 7.1
script:
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini
- php code-checker/src/code-checker.php
- vendor/bin/tester tests -s -p php -c tests/php-unix.ini $COVERAGE
- php temp/code-checker/src/code-checker.php --short-arrays
after_failure:
# Print *.actual content
@@ -23,14 +17,23 @@ after_failure:
before_script:
# Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker code-checker ~2.5 --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
- if [ $TRAVIS_PHP_VERSION == "7.0" ]; then COVERAGE="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi
# Create databases.ini
- cp ./tests/databases.sample.ini ./tests/databases.ini
- cp ./tests/databases.travis.ini ./tests/databases.ini
# Create Postgre database
- psql -c 'CREATE DATABASE dibi_test' -U postgres
after_script:
# Report Code Coverage
- >
if [ "$COVERAGE" != "" ]; then
wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar
&& php coveralls.phar --verbose --config tests/.coveralls.yml
|| true; fi
sudo: false
cache:

59
appveyor.yml Normal file
View File

@@ -0,0 +1,59 @@
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 curl https://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip --output php.zip
- IF %PHP%==1 7z x php.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 curl https://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip --output php.zip
- IF %PHP%==1 7z x php.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", "oracle", "access", "pdo", "odbc"],
"homepage": "http://dibiphp.com",
"keywords": ["database", "dbal", "mysql", "postgresql", "sqlite", "mssql", "sqlsrv", "oracle", "access", "pdo", "odbc"],
"homepage": "https://dibiphp.com",
"license": ["BSD-3-Clause", "GPL-2.0", "GPL-3.0"],
"authors": [
{
@@ -11,21 +11,22 @@
}
],
"require": {
"php": ">=5.2.0"
"php": ">=5.4.4"
},
"require-dev": {
"tracy/tracy": "~2.2",
"nette/tester": "~1.3"
"nette/tester": "~1.7"
},
"replace": {
"dg/dibi": "self.version"
"dg/dibi": "*"
},
"autoload": {
"classmap": ["dibi/"]
"classmap": ["src/"],
"files": ["src/loader.php"]
},
"extra": {
"branch-alias": {
"dev-master": "2.3-dev"
"dev-master": "3.0-dev"
}
}
}

View File

@@ -1,27 +1,31 @@
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:
Dibi welcomes your contributions. There are several ways to help out:
* Please **do not** use the issue tracker for personal support requests (use
[dibi forum](http://forum.dibiphp.com) or [Stack Overflow](http://stackoverflow.com)).
* Create an issue on GitHub, if you have found a bug
* Write test cases for open bug issues
* Write fixes for open bug/feature issues, preferably with test cases included
* Contribute to the documentation
* Please **do not** derail or troll issues. Keep the discussion on topic and
respect the opinions of others.
Issues
------
* Use the GitHub **issue search** &mdash; check if the issue has already been
reported.
Please **do not use the issue tracker to ask questions**. We will be happy to help you
on [Dibi forum](https://forum.dibiphp.com).
A good **bug report** shouldn't leave others needing to chase you up for more
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.
Contributing
------------
Thanks!
The best way to propose a feature is to discuss your ideas on [Dibi forum](https://forum.dibiphp.com) before implementing them.
Please do not fix whitespace, format code, or make a purely cosmetic patch.
Thanks! :heart:

View File

@@ -1,12 +0,0 @@
# 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

@@ -1,35 +1,3 @@
<?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';
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php';

View File

@@ -1,419 +0,0 @@
<?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

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

View File

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

View File

@@ -1,145 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
/**
* dibi common exception.
*
* @package dibi
*/
class DibiException extends Exception
{
/** @var string */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $sql = NULL)
{
parent::__construct($message, (int) $code);
$this->sql = $sql;
}
/**
* @return string The SQL passed to the constructor
*/
final public function getSql()
{
return $this->sql;
}
/**
* @return string string represenation of exception with SQL command
*/
public function __toString()
{
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
}
/**
* database server exception.
*
* @package dibi
*/
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.
*
* @package dibi
*/
class DibiPcreException extends Exception
{
public function __construct($message = '%msg.')
{
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
);
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
/**
* @package dibi
*/
class DibiNotImplementedException extends DibiException
{}
/**
* @package dibi
*/
class DibiNotSupportedException extends DibiException
{}

View File

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

View File

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

Binary file not shown.

View File

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

View File

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

View File

@@ -1,7 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -10,11 +6,17 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
dibi::connect(array(
<h1>Fetching Examples | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
));
]);
/*

View File

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

View File

@@ -4,15 +4,15 @@
<?php
require __DIR__ . '/../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
dibi::connect([
'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 DibiDateTime($timestamp)
AND [date] > ', new Dibi\DateTime($timestamp)
);
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// dibi detects INSERT or REPLACE command
dibi::test('
REPLACE INTO products', array(
REPLACE INTO products', [
'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', array(
UPDATE colors SET', [
'color' => 'blue',
'order' => 12,
), '
], '
WHERE id=?', 123);
// -> UPDATE colors SET [color]='blue', [order]=12 WHERE id=123
// modifier applied to array
$array = array(1, 2, 3);
$array = [1, 2, 3];
dibi::test('
SELECT *
FROM people
@@ -70,10 +70,10 @@ dibi::test('
// modifier %by for ORDER BY
$order = array(
$order = [
'field1' => 'asc',
'field2' => 'desc',
);
];
dibi::test('
SELECT *
FROM people

View File

@@ -1,9 +1,7 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<?php
use Dibi\Type;
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
@@ -12,24 +10,30 @@ Tracy\Debugger::enable();
date_default_timezone_set('Europe/Prague');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
dibi::connect(array(
<h1>Result Set Data Types | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
));
]);
// using manual hints
$res = dibi::query('SELECT * FROM [customers]');
$res->setType('customer_id', Dibi::INTEGER)
->setType('added', Dibi::DATETIME)
->setFormat(dibi::DATETIME, 'Y-m-d H:i:s');
$res->setType('customer_id', Type::INTEGER)
->setType('added', Type::DATETIME)
->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
Tracy\Dumper::dump($res->fetch());
// outputs:
// DibiRow(3) {
// Dibi\Row(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)
@@ -40,7 +44,7 @@ $res = dibi::query('SELECT * FROM [customers]');
Tracy\Dumper::dump($res->fetch());
// outputs:
// DibiRow(3) {
// Dibi\Row(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)

View File

@@ -1,9 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Tracy & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via <a href="https://tracy.nette.org">Tracy</a>.</p>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -15,13 +9,13 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable();
$connection = dibi::connect(array(
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => array(
'profiler' => [
'run' => TRUE,
),
));
],
]);
// add panel to debug bar
@@ -31,3 +25,9 @@ $panel->register($connection);
// throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);
?><!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Tracy & SQL Exceptions | dibi</h1>
<p>Dibi can display and log exceptions via <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -1,11 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Tracy | dibi</h1>
<p>Dibi can log queries and dump variables to the <a href="https://tracy.nette.org">Tracy</a>.</p>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -17,13 +9,13 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable();
$connection = dibi::connect(array(
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => array(
'profiler' => [
'run' => TRUE,
),
));
],
]);
// add panel to debug bar
@@ -36,3 +28,13 @@ dibi::query('SELECT 123');
// result set will be dumped
Tracy\Debugger::barDump(dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]');
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<style> html { background: url(data/arrow.png) no-repeat bottom right; height: 100%; } </style>
<h1>Tracy | dibi</h1>
<p>Dibi can log queries and dump variables to the <a href="https://tracy.nette.org">Tracy</a>.</p>

View File

@@ -4,26 +4,26 @@
<?php
require __DIR__ . '/../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
dibi::connect(array(
dibi::connect([
'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]', array(
INSERT INTO [mytable]', [
'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

@@ -1,7 +1,3 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
@@ -10,15 +6,21 @@ if (@!include __DIR__ . '/../vendor/autoload.php') {
Tracy\Debugger::enable();
?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
dibi::connect(array(
<h1>Using Extension Methods | dibi</h1>
<?php
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
));
]);
// using the "prototype" to add custom method to class DibiResult
DibiResult::extensionMethod('fetchShuffle', function (DibiResult $obj) {
// using the "prototype" to add custom method to class Dibi\Result
Dibi\Result::extensionMethod('fetchShuffle', function (Dibi\Result $obj) {
$all = $obj->fetchAll();
shuffle($all);
return $all;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,13 +4,13 @@
<?php
require __DIR__ . '/../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
dibi::connect(array(
dibi::connect([
'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]', array(
dibi::query('INSERT INTO [products]', [
'title' => 'Test product',
));
]);
echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();

View File

@@ -1,8 +1,9 @@
[Dibi](http://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
[Dibi](https://dibiphp.com) - smart database layer for PHP [![Buy me a coffee](https://files.nette.org/images/coffee1s.png)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9XXL5ZJHAYQUN)
=========================================================
[![Downloads this Month](https://img.shields.io/packagist/dm/dibi/dibi.svg)](https://packagist.org/packages/dibi/dibi)
[![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)
@@ -13,29 +14,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 http://dibiphp.com. In this
Or you can download the latest package from https://dibiphp.com. In this
package is also `Dibi.minified`, shrinked single-file version of whole Dibi,
useful when you don't want to modify the library, but just use it.
Dibi requires PHP 5.2.0 or later. It has been tested with PHP 5.5 too.
Dibi requires PHP 5.4.4 or later. It has been tested with PHP 7 too.
Examples
--------
Refer to the `examples` directory for examples. Dibi documentation is
available on the [homepage](http://dibiphp.com).
available on the [homepage](https://dibiphp.com).
Connect to database:
```php
// connect to database (static way)
dibi::connect(array(
dibi::connect([
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => '***',
));
]);
// or object way; in all other examples use $connection-> instead of dibi::
$connection = new DibiConnection($options);
@@ -46,19 +47,19 @@ SELECT, INSERT, UPDATE
```php
dibi::query('SELECT * FROM users WHERE id = ?', $id);
$arr = array(
$arr = [
'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', array(
dibi::query('UPDATE users SET', [
'title' => array('SHA1(?)', 'tajneheslo'),
));
]);
// UPDATE users SET 'title' = SHA1('tajneheslo')
```
@@ -81,10 +82,10 @@ foreach ($result as $n => $row) {
Modifiers for arrays:
```php
dibi::query('SELECT * FROM users WHERE %and', array(
dibi::query('SELECT * FROM users WHERE %and', [
array('number > ?', 10),
array('number < ?', 100),
));
]);
// SELECT * FROM users WHERE (number > 10) AND (number < 100)
```
@@ -116,9 +117,9 @@ dibi::query("SELECT * FROM table WHERE name LIKE %like~", $query);
DateTime:
```php
dibi::query('UPDATE users SET', array(
dibi::query('UPDATE users SET', [
'time' => new DateTime,
));
]);
// UPDATE users SET ('2008-01-01 01:08:10')
```

View File

@@ -5,14 +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.
*
* @package dibi\nette
* @phpversion 5.3
*/
class DibiNette21Extension extends Nette\DI\CompilerExtension
class DibiExtension21 extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
@@ -35,16 +36,16 @@ class DibiNette21Extension extends Nette\DI\CompilerExtension
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('DibiConnection', array($config))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('DibiNettePanel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', array('@self'))
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', array('DibiNettePanel::renderException'));
->setClass('Dibi\Bridges\Nette\Panel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self'])
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']);
$connection->addSetup('$service->onEvent[] = ?', array(array($panel, 'logEvent')));
$connection->addSetup('$service->onEvent[] = ?', [[$panel, 'logEvent']]);
}
}

View File

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

View File

@@ -5,18 +5,24 @@
* 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;
/**
* Dibi panel for Nette\Diagnostics.
*
* @package dibi\nette
*/
class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
class Panel implements Nette\Diagnostics\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
static public $maxLength = 1000;
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -25,21 +31,21 @@ class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
public $filter;
/** @var array */
private $events = array();
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(DibiConnection $connection)
public function register(Dibi\Connection $connection)
{
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = array($this, 'logEvent');
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
@@ -47,7 +53,7 @@ class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -62,11 +68,11 @@ class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
*/
public static function renderException($e)
{
if ($e instanceof DibiException && $e->getSql()) {
return array(
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => dibi::dump($e->getSql(), TRUE),
);
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
@@ -99,15 +105,15 @@ class DibiNettePanel extends DibiObject 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 === DibiEvent::SELECT) {
if ($this->explain && $event->type === Event::SELECT) {
try {
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$backup = [$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 = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $e) {
$explain = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $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);
@@ -117,7 +123,7 @@ class DibiNettePanel extends DibiObject 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">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
$s .= '</td><td class="nette-DibiProfiler-sql">' . Helpers::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>";
}
@@ -126,7 +132,7 @@ class DibiNettePanel extends DibiObject implements Nette\Diagnostics\IBarPanel
if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
}
$s .= call_user_func(array($helpers, 'editorLink'), $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
$s .= call_user_func([$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

@@ -7,18 +7,21 @@
namespace Dibi\Bridges\Tracy;
use dibi;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Tracy;
/**
* Dibi panel for Tracy.
* @package dibi\nette
*/
class Panel extends \DibiObject implements Tracy\IBarPanel
class Panel implements Tracy\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
static public $maxLength = 1000;
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -27,21 +30,21 @@ class Panel extends \DibiObject implements Tracy\IBarPanel
public $filter;
/** @var array */
private $events = array();
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : \DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(\DibiConnection $connection)
public function register(Dibi\Connection $connection)
{
Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel(array(__CLASS__, 'renderException'));
$connection->onEvent[] = array($this, 'logEvent');
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
@@ -49,7 +52,7 @@ class Panel extends \DibiObject implements Tracy\IBarPanel
* After event notification.
* @return void
*/
public function logEvent(\DibiEvent $event)
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -60,22 +63,22 @@ class Panel extends \DibiObject implements Tracy\IBarPanel
/**
* Returns blue-screen custom tab.
* @return mixed
* @return array|NULL
*/
public static function renderException($e)
{
if ($e instanceof \DibiException && $e->getSql()) {
return array(
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => dibi::dump($e->getSql(), TRUE),
);
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (Tracy\IBarPanel)
* @return mixed
* @return string
*/
public function getTab()
{
@@ -85,42 +88,46 @@ class Panel extends \DibiObject implements Tracy\IBarPanel
$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'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
. $count . ' queries'
. ($totalTime ? ' / ' . number_format($totalTime * 1000, 1, '.', '') . 'ms' : '')
. '</span></span>';
}
/**
* Returns HTML code for custom panel. (Tracy\IBarPanel)
* @return mixed
* @return string|NULL
*/
public function getPanel()
{
if (!$this->events) {
return NULL;
}
$totalTime = $s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$totalTime += $event->time;
$connection = $event->connection;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === \DibiEvent::SELECT) {
if ($this->explain && $event->type === Event::SELECT) {
try {
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (\DibiException $e) {
$backup = [$connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN FOR' : 'EXPLAIN');
$explain = @Helpers::dump($connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
}
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
list($connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
$s .= '<tr><td>' . number_format($event->time * 1000, 3, '.', '');
if ($explain) {
static $counter;
$counter++;
$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">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::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>";
}
@@ -128,17 +135,19 @@ class Panel extends \DibiObject implements Tracy\IBarPanel
$s .= Tracy\Helpers::editorLink($event->source[0], $event->source[1]);//->class('tracy-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
$s .= "</td><td>{$event->count}</td></tr>";
}
return empty($this->events) ? '' :
'<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
return '<style> #tracy-debug td.tracy-DibiProfiler-sql { background: white !important }
#tracy-debug .tracy-DibiProfiler-source { color: #999 !important }
#tracy-debug tracy-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . count($this->events) . ($totalTime === NULL ? '' : sprintf(', time: %0.3f ms', $totalTime * 1000)) . '</h1>
<h1>Queries: ' . count($this->events)
. ($totalTime === NULL ? '' : ', time: ' . number_format($totalTime * 1000, 1, '.', '') . 'ms') . ', '
. htmlSpecialChars($connection->getConfig('driver') . ($connection->getConfig('name') ? '/' . $connection->getConfig('name') : '')
. ($connection->getConfig('host') ? '@' . $connection->getConfig('host') : '')) . '</h1>
<div class="tracy-inner tracy-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th></tr>' . $s . '
</table>
</div>';
}

View File

@@ -5,33 +5,37 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use Traversable;
/**
* dibi connection.
*
* @package dibi
*
* @property-read int $affectedRows
* @property-read int $insertId
*/
class DibiConnection extends DibiObject
class Connection
{
/** @var array of function (DibiEvent $event); Occurs after query is executed */
use Strict;
/** @var array of function (Event $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var IDibiDriver */
/** @var Driver */
private $driver;
/** @var DibiTranslator */
/** @var Translator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var DibiHashMap Substitutes for identifiers */
/** @var HashMap Substitutes for identifiers */
private $substitutes;
@@ -47,7 +51,7 @@ class DibiConnection extends DibiObject
* @param mixed connection parameters
* @param string connection name
* @throws DibiException
* @throws Exception
*/
public function __construct($config, $name = NULL)
{
@@ -55,64 +59,66 @@ class DibiConnection extends DibiObject
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
$tmp = array();
$tmp = [];
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.');
}
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');
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');
if (!isset($config['driver'])) {
$config['driver'] = dibi::$defaultDriver;
$config['driver'] = \dibi::$defaultDriver;
}
$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'.");
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'.");
}
$this->driver = new $class;
}
$config['name'] = $name;
$this->config = $config;
$this->driver = new $class;
$this->translator = new DibiTranslator($this);
// profiler
$profilerCfg = & $config['profiler'];
$profilerCfg = &$config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = array('run' => (bool) $profilerCfg);
$profilerCfg = ['run' => (bool) $profilerCfg];
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
$this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $filter), 'logEvent'];
}
if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
if (Loggers\FirePhpLogger::isAvailable()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
}
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('DibiNettePanel')) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
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);
$panel->register($this);
}
}
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
@@ -142,13 +148,13 @@ class DibiConnection extends DibiObject
*/
final public function connect()
{
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -197,30 +203,17 @@ class DibiConnection extends DibiObject
}
/**
* 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)
/** @deprecated */
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]);
}
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
Helpers::alias($config, $key, $alias);
}
/**
* Returns the driver and connects to a database in lazy mode.
* @return IDibiDriver
* @return Driver
*/
final public function getDriver()
{
@@ -232,8 +225,8 @@ class DibiConnection extends DibiObject
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set or number of affected rows
* @throws Exception
*/
final public function query($args)
{
@@ -246,7 +239,7 @@ class DibiConnection extends DibiObject
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @throws Exception
*/
final public function translate($args)
{
@@ -264,12 +257,12 @@ class DibiConnection extends DibiObject
{
$args = func_get_args();
try {
dibi::dump($this->translateArgs($args));
Helpers::dump($this->translateArgs($args));
return TRUE;
} catch (DibiException $e) {
} catch (Exception $e) {
if ($e->getSql()) {
dibi::dump($e->getSql());
Helpers::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
@@ -279,15 +272,15 @@ class DibiConnection extends DibiObject
/**
* Generates (translates) and returns SQL query as DibiDataSource.
* Generates (translates) and returns SQL query as DataSource.
* @param array|mixed one or more arguments
* @return DibiDataSource
* @throws DibiException
* @return DataSource
* @throws Exception
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DibiDataSource($this->translateArgs($args), $this);
return new DataSource($this->translateArgs($args), $this);
}
@@ -296,29 +289,33 @@ class DibiConnection extends DibiObject
* @param array
* @return string
*/
private function translateArgs($args)
protected function translateArgs($args)
{
$this->connected || $this->connect();
return $this->translator->translate($args);
if (!$this->translator) {
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set or number of affected rows
* @throws Exception
*/
final public function nativeQuery($sql)
{
$this->connected || $this->connect();
dibi::$sql = $sql;
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
\dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL;
try {
$res = $this->driver->query($sql);
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -337,14 +334,14 @@ class DibiConnection extends DibiObject
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows
* @throws DibiException
* @throws Exception
*/
public function getAffectedRows()
{
$this->connected || $this->connect();
$rows = $this->driver->getAffectedRows();
if (!is_int($rows) || $rows < 0) {
throw new DibiException('Cannot retrieve number of affected rows.');
throw new Exception('Cannot retrieve number of affected rows.');
}
return $rows;
}
@@ -353,7 +350,7 @@ class DibiConnection extends DibiObject
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws DibiException
* @throws Exception
*/
public function affectedRows()
{
@@ -365,14 +362,14 @@ class DibiConnection extends DibiObject
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string optional sequence name
* @return int
* @throws DibiException
* @throws Exception
*/
public function getInsertId($sequence = NULL)
{
$this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence);
if ($id < 1) {
throw new DibiException('Cannot retrieve last generated ID.');
throw new Exception('Cannot retrieve last generated ID.');
}
return (int) $id;
}
@@ -382,7 +379,7 @@ class DibiConnection extends DibiObject
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws DibiException
* @throws Exception
*/
public function insertId($sequence = NULL)
{
@@ -398,12 +395,12 @@ class DibiConnection extends DibiObject
public function begin($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL;
try {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -418,12 +415,12 @@ class DibiConnection extends DibiObject
public function commit($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL;
try {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -438,12 +435,12 @@ class DibiConnection extends DibiObject
public function rollback($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL;
try {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -452,14 +449,14 @@ class DibiConnection extends DibiObject
/**
* Result set factory.
* @param IDibiResultDriver
* @return DibiResult
* @param ResultDriver
* @return Result
*/
public function createResultSet(IDibiResultDriver $resultDriver)
public function createResultSet(ResultDriver $resultDriver)
{
$res = new DibiResult($resultDriver);
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
}
@@ -467,17 +464,17 @@ class DibiConnection extends DibiObject
/**
* @return DibiFluent
* @return Fluent
*/
public function command()
{
return new DibiFluent($this);
return new Fluent($this);
}
/**
* @param string column name
* @return DibiFluent
* @param mixed column name
* @return Fluent
*/
public function select($args)
{
@@ -489,12 +486,12 @@ class DibiConnection extends DibiObject
/**
* @param string table
* @param array
* @return DibiFluent
* @return Fluent
*/
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);
}
@@ -503,14 +500,14 @@ class DibiConnection extends DibiObject
/**
* @param string table
* @param array
* @return DibiFluent
* @return Fluent
*/
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);
@@ -519,7 +516,7 @@ class DibiConnection extends DibiObject
/**
* @param string table
* @return DibiFluent
* @return Fluent
*/
public function delete($table)
{
@@ -532,7 +529,7 @@ class DibiConnection extends DibiObject
/**
* Returns substitution hashmap.
* @return DibiHashMap
* @return HashMap
*/
public function getSubstitutes()
{
@@ -546,16 +543,9 @@ class DibiConnection extends DibiObject
*/
public function substitute($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]};
return strpos($value, ':') === FALSE
? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
}
@@ -565,8 +555,8 @@ class DibiConnection extends DibiObject
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return DibiRow
* @throws DibiException
* @return Row|FALSE
* @throws Exception
*/
public function fetch($args)
{
@@ -578,8 +568,8 @@ class DibiConnection extends DibiObject
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return DibiRow[]
* @throws DibiException
* @return Row[]
* @throws Exception
*/
public function fetchAll($args)
{
@@ -591,8 +581,8 @@ class DibiConnection extends DibiObject
/**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @return mixed
* @throws Exception
*/
public function fetchSingle($args)
{
@@ -604,8 +594,8 @@ class DibiConnection extends DibiObject
/**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @return array
* @throws Exception
*/
public function fetchPairs($args)
{
@@ -614,59 +604,37 @@ class DibiConnection extends DibiObject
}
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/
/**
* Import SQL dump from file - extreme fast!
* Import SQL dump from file.
* @param string filename
* @return int count of sql commands
*/
public function loadFile($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;
return Helpers::loadFromFile($this, $file);
}
/**
* Gets a information about the current database.
* @return DibiDatabaseInfo
* @return Reflection\Database
*/
public function getDatabaseInfo()
{
$this->connected || $this->connect();
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
}
@@ -675,7 +643,7 @@ class DibiConnection extends DibiObject
*/
public function __wakeup()
{
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
@@ -684,7 +652,15 @@ class DibiConnection extends DibiObject
*/
public function __sleep()
{
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
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);
}
}
}

View File

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

75
src/Dibi/DateTime.php Normal file
View File

@@ -0,0 +1,75 @@
<?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;
/**
* @param string|int
*/
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');
}
public function __wakeup()
{
if (isset($this->fix, $this->fix[1])) {
$this->__construct($this->fix[0], new \DateTimeZone($this->fix[1]));
unset($this->fix);
} elseif (isset($this->fix)) {
$this->__construct($this->fix[0]);
unset($this->fix);
} else {
parent::__wakeup();
}
}
}

View File

@@ -5,6 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Firebird/InterBase database.
@@ -16,24 +20,24 @@
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $transaction;
/** @var bool */
@@ -41,12 +45,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
@@ -54,37 +58,33 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
DibiConnection::alias($config, 'database', 'db');
Dibi\Helpers::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$config += array(
$config += [
'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 @
$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());
$this->connection = @ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
}
}
}
@@ -96,38 +96,34 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function disconnect()
{
ibase_close($this->connection);
@ibase_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if (DibiDriverException::catchError($msg)) {
if ($res === FALSE) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
} else {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), dibi::$sql);
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql);
}
}
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
@@ -156,14 +152,14 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->resource);
$this->transaction = ibase_trans($this->getResource());
$this->inTransaction = TRUE;
}
@@ -172,16 +168,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
@@ -192,16 +188,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
@@ -220,7 +216,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Returns the connection resource.
* @return resource
* @return resource|NULL
*/
public function getResource()
{
@@ -230,7 +226,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -241,7 +237,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -256,34 +252,68 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::IDENTIFIER:
return $value;
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeBinary($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'");
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '"' . str_replace('"', '""', $value). '"';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -295,35 +325,41 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function escapeLike($value, $pos)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit >= 0 && $offset > 0) {
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
if ($limit > 0 || $offset > 0) {
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . (int) $limit : '') . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
}
}
@@ -347,7 +383,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
throw new Dibi\NotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
}
@@ -358,16 +394,15 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function fetch($assoc)
{
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
$result = $assoc ? @ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : @ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode()) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
} else {
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
}
}
@@ -379,11 +414,11 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
* 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
* @throws Dibi\Exception
*/
public function seek($row)
{
throw new DibiNotSupportedException('Firebird/Interbase do not support seek in result set.');
throw new Dibi\NotSupportedException('Firebird/Interbase do not support seek in result set.');
}
@@ -400,7 +435,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* Returns the result set resource.
* @return resource
* @return resource|NULL
*/
public function getResultResource()
{
@@ -416,21 +451,21 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
);
];
}
return $columns;
}
/********************* IDibiReflector ********************/
/********************* Dibi\Reflector ********************/
/**
@@ -445,12 +480,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = array();
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'TRUE',
);
];
}
return $tables;
}
@@ -494,10 +529,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = array(
$columns[$key] = [
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
@@ -505,7 +540,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE,
);
];
}
return $columns;
}
@@ -532,7 +567,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = array();
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
@@ -562,14 +597,14 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = array();
$keys = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = array(
$keys[$key] = [
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
);
];
}
return $keys;
}
@@ -589,7 +624,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = array();
$indices = [];
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
@@ -613,7 +648,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = array();
$constraints = [];
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
@@ -656,15 +691,15 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = array();
$triggers = [];
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = array(
$triggers[$row['TRIGGER_NAME']] = [
'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;
}
@@ -684,7 +719,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = array();
$triggers = [];
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
@@ -731,7 +766,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = array();
$procedures = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
@@ -755,7 +790,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = array();
$procedures = [];
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
@@ -774,7 +809,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = array();
$generators = [];
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
@@ -793,7 +828,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = array();
$functions = [];
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
@@ -801,39 +836,3 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
}
/**
* Database procedure exception.
*
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}

View File

@@ -5,7 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for MS SQL database.
@@ -17,16 +20,16 @@ require_once dirname(__FILE__) . '/DibiMsSqlReflector.php';
* - database => the database name to select
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
{
/** @var resource Connection resource */
use Dibi\Strict;
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -34,12 +37,12 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
@@ -47,9 +50,9 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
@@ -60,11 +63,11 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
if (!is_resource($this->connection)) {
throw new DibiDriverException("Can't connect to DB.");
throw new Dibi\DriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($this->escape($config['database'], dibi::IDENTIFIER), $this->connection)) { // intentionally @
throw new DibiDriverException("Can't select DB '$config[database]'.");
if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @
throw new Dibi\DriverException("Can't select DB '$config[database]'.");
}
}
@@ -75,26 +78,27 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
*/
public function disconnect()
{
mssql_close($this->connection);
@mssql_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(mssql_get_last_message(), 0, $sql);
throw new Dibi\DriverException(mssql_get_last_message(), 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
@@ -127,7 +131,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -139,7 +143,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -151,7 +155,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -161,7 +165,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -171,18 +175,18 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMsSqlReflector($this);
return new MsSqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -197,35 +201,69 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::IDENTIFIER:
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeBinary($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'");
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -237,40 +275,47 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
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';
}
}
@@ -338,15 +383,15 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'],
'nativetype' => $row['type'],
);
];
}
return $columns;
}
@@ -354,7 +399,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{

View File

@@ -5,20 +5,24 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MsSQL databases.
*
* @package dibi\drivers
* The dibi reflector for MS SQL databases.
* @internal
*/
class DibiMsSqlReflector extends DibiObject implements IDibiReflector
class MsSqlReflector implements Dibi\Reflector
{
/** @var IDibiDriver */
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(IDibiDriver $driver)
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
@@ -34,12 +38,12 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
');
$tables = array();
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
$tables[] = [
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
];
}
return $tables;
}
@@ -58,13 +62,13 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
$result = $this->driver->query("
SELECT MAX(rowcnt)
FROM sys.sysindexes
WHERE id=OBJECT_ID({$this->driver->escape($table, dibi::IDENTIFIER)})
WHERE id=OBJECT_ID({$this->driver->escapeIdentifier($table)})
");
$row = $result->fetch(FALSE);
if (!is_array($row) || count($row) < 1) {
if ($fallback) {
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escape($table, dibi::IDENTIFIER)}")->fetch(FALSE);
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escapeIdentifier($table)}")->fetch(FALSE);
$count = intval($row[0]);
} else {
$count = FALSE;
@@ -87,22 +91,22 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
$res = $this->driver->query("
SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = {$this->driver->escape($table, dibi::TEXT)}
WHERE TABLE_NAME = {$this->driver->escapeText($table)}
ORDER BY TABLE_NAME, ORDINAL_POSITION
");
$columns = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$size = FALSE;
$type = strtoupper($row['DATA_TYPE']);
$size_cols = array(
$size_cols = [
'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]) {
@@ -110,7 +114,7 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
}
}
$columns[] = array(
$columns[] = [
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $type,
@@ -120,7 +124,7 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => FALSE,
'vendor' => $row,
);
];
}
return $columns;
@@ -144,22 +148,22 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
(ic.object_id = col.object_id and ic.column_id = col.column_id)
INNER JOIN sys.tables t ON
(ind.object_id = t.object_id)
WHERE t.name = {$this->driver->escape($table, dibi::TEXT)}
WHERE t.name = {$this->driver->escapeText($table)}
AND t.is_ms_shipped = 0
ORDER BY
t.name, ind.name, ind.index_id, ic.index_column_id
");
$indexes = array();
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$index_name = $row['index_name'];
if (!isset($indexes[$index_name])) {
$indexes[$index_name] = array();
$indexes[$index_name] = [];
$indexes[$index_name]['name'] = $index_name;
$indexes[$index_name]['unique'] = (bool) $row['is_unique'];
$indexes[$index_name]['primary'] = (bool) $row['is_primary_key'];
$indexes[$index_name]['columns'] = array();
$indexes[$index_name]['columns'] = [];
}
$indexes[$index_name]['columns'][] = $row['column_name'];
}
@@ -187,18 +191,18 @@ class DibiMsSqlReflector extends DibiObject implements IDibiReflector
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escape($table, dibi::TEXT)}
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escapeText($table)}
");
$keys = array();
$keys = [];
while ($row = $res->fetch(TRUE)) {
$key_name = $row['foreign_key'];
if (!isset($keys[$key_name])) {
$keys[$key_name]['name'] = $row['foreign_key']; // foreign key name
$keys[$key_name]['local'] = array($row['column_name']); // local columns
$keys[$key_name]['local'] = [$row['column_name']]; // local columns
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
$keys[$key_name]['foreign'] = array($row['reference_column_name']); // referenced columns
$keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns
$keys[$key_name]['onDelete'] = FALSE;
$keys[$key_name]['onUpdate'] = FALSE;
} else {

View File

@@ -5,8 +5,9 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
use Dibi;
/**
@@ -25,20 +26,20 @@ require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
* - 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 DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var resource Connection resource */
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -49,12 +50,12 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysql')) {
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'mysql' is not loaded.");
}
}
@@ -62,22 +63,22 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
DibiConnection::alias($config, 'flags', 'options');
$config += array(
Dibi\Helpers::alias($config, 'flags', 'options');
$config += [
'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) {
@@ -105,23 +106,18 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(mysql_error(), mysql_errno());
throw new Dibi\DriverException(mysql_error(), mysql_errno());
}
if (isset($config['charset'])) {
$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) {
if (!@mysql_set_charset($config['charset'], $this->connection)) { // intentionally @
$this->query("SET NAMES '$config[charset]'");
}
}
if (isset($config['database'])) {
if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
throw new Dibi\DriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
}
@@ -143,15 +139,15 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
*/
public function disconnect()
{
mysql_close($this->connection);
@mysql_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -161,8 +157,8 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
}
if (mysql_errno($this->connection)) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
if ($code = mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -176,10 +172,10 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
*/
public function getInfo()
{
$res = array();
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new DibiPcreException;
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
@@ -213,7 +209,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -225,7 +221,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -237,7 +233,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -247,7 +243,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -257,18 +253,18 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -283,43 +279,75 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
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.');
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
}
/**
* @param string
* @return string
*/
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) . "'";
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? 1 : 0;
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -338,30 +366,39 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit >= 0 || $offset > 0) {
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -386,7 +423,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysql_num_rows($this->resultSet);
}
@@ -407,12 +444,12 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
* 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
* @throws Dibi\Exception
*/
public function seek($row)
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
return mysql_data_seek($this->resultSet, $row);
@@ -437,16 +474,17 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'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;
}
@@ -454,7 +492,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{

View File

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

View File

@@ -5,8 +5,9 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
use Dibi;
/**
@@ -26,20 +27,20 @@ require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
* - 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 DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
/** @var mysqli Connection resource */
/** @var \mysqli|NULL */
private $connection;
/** @var mysqli_result Resultset resource */
/** @var \mysqli_result|NULL */
private $resultSet;
/** @var bool */
@@ -50,12 +51,12 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysqli')) {
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
}
}
@@ -63,9 +64,9 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) {
@@ -73,14 +74,14 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} else {
// default values
$config += array(
$config += [
'charset' => 'utf8',
'timezone' => date('P'),
'username' => ini_get('mysqli.default_user'),
'password' => ini_get('mysqli.default_pw'),
'socket' => (string) ini_get('mysqli.default_socket'),
'port' => NULL,
);
];
if (!isset($config['host'])) {
$host = ini_get('mysqli.default_host');
if ($host) {
@@ -92,8 +93,8 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
}
$foo = & $config['flags'];
$foo = & $config['database'];
$foo = &$config['flags'];
$foo = &$config['database'];
$this->connection = mysqli_init();
if (isset($config['options'])) {
@@ -109,7 +110,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new DibiDriverException(mysqli_connect_error(), $errno);
throw new Dibi\DriverException(mysqli_connect_error(), $errno);
}
}
@@ -137,26 +138,47 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function disconnect()
{
mysqli_close($this->connection);
@mysqli_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
if ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
}
return NULL;
}
/**
* @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);
}
}
@@ -166,10 +188,10 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getInfo()
{
$res = array();
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new DibiPcreException;
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
@@ -203,7 +225,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -215,7 +237,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -227,7 +249,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -237,7 +259,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the connection resource.
* @return mysqli
* @return \mysqli
*/
public function getResource()
{
@@ -247,20 +269,19 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver(mysqli_result $resource)
public function createResultDriver(\mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -273,36 +294,68 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
case dibi::BINARY:
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::IDENTIFIER:
return '`' . str_replace('`', '``', $value) . '`';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "_binary'" . mysqli_real_escape_string($this->connection, $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'");
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '`' . str_replace('`', '``', $value) . '`';
}
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -321,30 +374,39 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit >= 0 || $offset > 0) {
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
}
@@ -369,7 +431,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysqli_num_rows($this->resultSet);
}
@@ -390,12 +452,12 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* 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
* @throws Dibi\Exception
*/
public function seek($row)
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
return mysqli_data_seek($this->resultSet, $row);
}
@@ -421,8 +483,8 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
static $types;
if ($types === NULL) {
$consts = get_defined_constants(TRUE);
$types = array();
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : array() as $key => $value) {
$types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12);
}
@@ -431,16 +493,17 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
$count = mysqli_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'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,
'vendor' => $row,
);
];
}
return $columns;
}
@@ -448,7 +511,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the result set resource.
* @return mysqli_result
* @return \mysqli_result|NULL
*/
public function getResultResource()
{

View File

@@ -5,6 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver interacting with databases via ODBC connections.
@@ -15,16 +19,16 @@
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
/** @var resource Connection resource */
use Dibi\Strict;
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -38,12 +42,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('odbc')) {
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded.");
}
}
@@ -51,19 +55,19 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$config += array(
$config += [
'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 @
@@ -73,7 +77,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
}
}
@@ -84,15 +88,15 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function disconnect()
{
odbc_close($this->connection);
@odbc_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -100,12 +104,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
return $this->createResultDriver($res);
}
return NULL;
}
@@ -125,7 +130,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function getInsertId($sequence)
{
throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.');
}
@@ -133,12 +138,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!odbc_autocommit($this->connection, FALSE)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
}
@@ -147,12 +152,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!odbc_commit($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
@@ -162,12 +167,12 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!odbc_rollback($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
@@ -185,7 +190,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -195,7 +200,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -206,7 +211,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -221,34 +226,68 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::IDENTIFIER:
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeBinary($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 ? "#m/d/Y H:i:s#" : "#m/d/Y#");
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("#m/d/Y#");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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#");
}
@@ -260,40 +299,47 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
if ($offset) {
throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
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';
}
}
@@ -337,7 +383,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
return FALSE;
}
$count = odbc_num_fields($set);
$cols = array();
$cols = [];
for ($i = 1; $i <= $count; $i++) {
$cols[] = odbc_result($set, $i);
}
@@ -376,14 +422,14 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$columns[] = array(
$columns[] = [
'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;
}
@@ -391,7 +437,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{
@@ -400,7 +446,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -410,13 +456,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getTables()
{
$res = odbc_tables($this->connection);
$tables = array();
$tables = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = array(
$tables[] = [
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
);
];
}
}
odbc_free_result($res);
@@ -432,17 +478,17 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getColumns($table)
{
$res = odbc_columns($this->connection);
$columns = array();
$columns = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$columns[] = array(
$columns[] = [
'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);
@@ -457,7 +503,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function getIndexes($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
@@ -468,7 +514,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
}

View File

@@ -5,6 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Oracle database.
@@ -19,16 +23,16 @@
* - 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 DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
/** @var resource Connection resource */
use Dibi\Strict;
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -40,14 +44,17 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var int|FALSE Number of affected rows */
private $affectedRows = FALSE;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('oci8')) {
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded.");
}
}
@@ -55,11 +62,11 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
$foo = & $config['charset'];
$foo = &$config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
@@ -73,7 +80,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
if (!$this->connection) {
$err = oci_error();
throw new DibiDriverException($err['message'], $err['code']);
throw new Dibi\DriverException($err['message'], $err['code']);
}
if (isset($config['schema'])) {
@@ -88,31 +95,54 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function disconnect()
{
oci_close($this->connection);
@oci_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = oci_parse($this->connection, $sql);
if ($res) {
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res);
if ($err) {
throw new DibiDriverException($err['message'], $err['code'], $sql);
throw self::createException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = oci_num_rows($res);
return $this->createResultDriver($res);
}
} else {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code'], $sql);
throw new Dibi\DriverException($err['message'], $err['code'], $sql);
}
return NULL;
}
/**
* @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);
}
}
@@ -123,7 +153,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getAffectedRows()
{
throw new DibiNotImplementedException;
return $this->affectedRows;
}
@@ -153,13 +183,13 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!oci_commit($this->connection)) {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code']);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
@@ -169,13 +199,13 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code']);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
@@ -183,7 +213,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -193,7 +223,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -204,7 +234,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -219,35 +249,69 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
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) . '"';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
return $value->format($type === dibi::DATETIME ? $this->fmtDateTime : $this->fmtDate);
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
@@ -267,33 +331,42 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($offset > 0) {
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($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 >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
}
@@ -318,7 +391,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -340,7 +413,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function seek($row)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
@@ -362,15 +435,15 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$type = oci_field_type($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'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,
);
];
}
return $columns;
}
@@ -378,7 +451,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{
@@ -387,7 +460,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -397,13 +470,13 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getTables()
{
$res = $this->query('SELECT * FROM cat');
$tables = array();
$tables = [];
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = array(
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'VIEW',
);
];
}
}
return $tables;
@@ -417,7 +490,20 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getColumns($table)
{
throw new DibiNotImplementedException;
$res = $this->query('SELECT * FROM "ALL_TAB_COLUMNS" WHERE "TABLE_NAME" = ' . $this->escapeText($table));
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[] = [
'table' => $row['TABLE_NAME'],
'name' => $row['COLUMN_NAME'],
'nativetype' => $row['DATA_TYPE'],
'size' => isset($row['DATA_LENGTH']) ? $row['DATA_LENGTH'] : NULL,
'nullable' => $row['NULLABLE'] === 'Y',
'default' => $row['DATA_DEFAULT'],
'vendor' => $row,
];
}
return $columns;
}
@@ -428,7 +514,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getIndexes($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
@@ -439,7 +525,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
}

View File

@@ -5,9 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/DibiMySqlReflector.php';
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
use Dibi;
use PDO;
/**
@@ -20,16 +21,16 @@ require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - version
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var PDO Connection resource */
private $connection;
/** @var PDOStatement Resultset resource */
/** @var \PDOStatement|NULL Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
@@ -39,16 +40,16 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
private $driverName;
/** @var string */
private $serverVersion;
private $serverVersion = '';
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
@@ -56,25 +57,25 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
$foo = & $config['dsn'];
$foo = & $config['options'];
DibiConnection::alias($config, 'resource', 'pdo');
$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) {
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new DibiNotSupportedException('PHP extension for PDO is not loaded.');
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
}
throw new DibiDriverException($e->getMessage(), $e->getCode());
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
@@ -98,34 +99,46 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @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 = array('UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1);
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) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
if ($this->affectedRows !== FALSE) {
return NULL;
}
} 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 {
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);
}
}
@@ -153,13 +166,13 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
@@ -168,13 +181,13 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
@@ -183,13 +196,13 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
@@ -206,30 +219,29 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new DibiMySqlReflector($this);
return new MySqlReflector($this);
case 'sqlite':
case 'sqlite2':
return new DibiSqliteReflector($this);
return new SqliteReflector($this);
default:
throw new DibiNotSupportedException;
throw new Dibi\NotSupportedException;
}
}
/**
* Result set driver factory.
* @param PDOStatement
* @return IDibiResultDriver
* @param \PDOStatement
* @return Dibi\ResultDriver
*/
public function createResultDriver(PDOStatement $resource)
public function createResultDriver(\PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -242,71 +254,104 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, $type === dibi::TEXT ? PDO::PARAM_STR : PDO::PARAM_LOB);
}
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_STR);
}
}
case dibi::IDENTIFIER:
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
}
case 'sqlite':
case 'sqlite2':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'dblib':
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
default:
return $value;
}
case 'sqlite':
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? 1 : 0;
}
case 'odbc':
case 'mssql':
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
case dibi::DATE:
case dibi::DATETIME:
if (!$value instanceof DateTime && !$value instanceof DateTimeInterface) {
$value = new DibiDateTime($value);
}
if ($this->driverName === 'odbc') {
return $value->format($type === dibi::DATETIME ? '#m/d/Y H:i:s#' : '#m/d/Y#');
} else {
return $value->format($type === dibi::DATETIME ? "'Y-m-d H:i:s'" : "'Y-m-d'");
}
case 'dblib':
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? '1' : '0';
}
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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
@@ -328,11 +373,10 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
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, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite':
case 'sqlite2':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
@@ -340,67 +384,80 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
case 'mssql':
case 'dblib':
case 'sqlsrv':
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default:
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) {
return;
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
switch ($this->driverName) {
case 'mysql':
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
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 >= 0) {
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) {
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
case 'sqlite2':
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
if ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'oci':
if ($offset > 0) {
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 >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit >= 0) {
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
@@ -408,24 +465,29 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
case 'mssql':
case 'sqlsrv':
case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) {
if ($offset >= 0 || $limit >= 0) {
$sql .= ' OFFSET ' . (int) $offset . ' ROWS'
. ($limit > 0 ? ' FETCH NEXT ' . (int) $limit . ' ROWS ONLY' : '');
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 < 1) {
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 DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
@@ -461,7 +523,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -478,31 +540,30 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws DibiException
* @throws Dibi\Exception
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new DibiNotSupportedException('Driver does not support meta data.');
throw new Dibi\NotSupportedException('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(
$row = $row + [
'table' => NULL,
'native_type' => 'VAR_STRING',
);
];
$columns[] = array(
$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;
}
@@ -510,7 +571,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
/**
* Returns the result set resource.
* @return PDOStatement
* @return \PDOStatement|NULL
*/
public function getResultResource()
{

View File

@@ -5,6 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for PostgreSQL database.
@@ -16,16 +20,16 @@
* - 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 DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
/** @var resource Connection resource */
use Dibi\Strict;
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -36,12 +40,12 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pgsql')) {
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
}
}
@@ -49,55 +53,54 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
$error = NULL;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$config += array(
$config += [
'charset' => 'utf8',
);
];
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
DibiConnection::alias($config, 'user', 'username');
DibiConnection::alias($config, 'dbname', 'database');
foreach (array('host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service') as $key) {
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) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
DibiDriverException::tryError();
set_error_handler(function($severity, $message) use (&$error) {
$error = $message;
});
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
restore_error_handler();
}
if (!is_resource($this->connection)) {
throw new DibiDriverException('Connecting error.');
throw new Dibi\DriverException($error ?: 'Connecting error.');
}
if (isset($config['charset'])) {
DibiDriverException::tryError();
pg_set_client_encoding($this->connection, $config['charset']);
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
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['schema'])) {
$this->query('SET search_path TO "' . $config['schema'] . '"');
$this->query('SET search_path TO "' . implode('", "', (array) $config['schema']) . '"');
}
}
@@ -108,7 +111,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function disconnect()
{
pg_close($this->connection);
@pg_close($this->connection); // @ - connection can be already disconnected
}
@@ -125,8 +128,8 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -134,7 +137,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
throw self::createException(pg_last_error($this->connection), NULL, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
@@ -142,6 +145,35 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
return $this->createResultDriver($res);
}
}
return NULL;
}
/**
* @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);
}
}
@@ -181,7 +213,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -193,7 +225,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -205,7 +237,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -219,13 +251,13 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE);
}
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -235,7 +267,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -246,7 +278,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -261,43 +293,75 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
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.');
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
}
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? 'TRUE' : 'FALSE';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -311,38 +375,46 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
{
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value);
$value = strtr($value, array('%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\'));
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return pg_unescape_bytea($value);
}
throw new InvalidArgumentException('Unsupported 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);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit >= 0) {
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset > 0) {
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
}
@@ -411,13 +483,13 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
public function getResultColumns()
{
$count = pg_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = array(
$row = [
'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;
}
@@ -427,7 +499,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{
@@ -436,7 +508,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -445,9 +517,9 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getTables()
{
$version = pg_parameter_status($this->resource, 'server_version');
$version = pg_parameter_status($this->getResource(), 'server_version');
if ($version < 7.4) {
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
}
$query = "
@@ -475,7 +547,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
$res = $this->query($query);
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : array();
return $tables ? $tables : [];
}
@@ -486,7 +558,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getColumns($table)
{
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT indkey
FROM pg_class
@@ -528,10 +600,10 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
");
}
$columns = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = array(
$columns[] = [
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
@@ -540,7 +612,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row,
);
];
}
return $columns;
}
@@ -553,7 +625,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getIndexes($table)
{
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT
a.attnum AS ordinal_position,
@@ -568,7 +640,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
ORDER BY ordinal_position
");
$columns = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
@@ -581,7 +653,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
WHERE pg_class.oid = $_table::regclass
");
$indexes = array();
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
@@ -601,7 +673,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getForeignKeys($table)
{
$_table = $this->escape($this->escape($table, dibi::IDENTIFIER), dibi::TEXT);
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT
@@ -642,17 +714,17 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
c.conrelid = $_table::regclass
");
$fKeys = $references = array();
$fKeys = $references = [];
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = array(
$fKeys[$row['name']] = [
'name' => $row['name'],
'table' => $row['table'],
'local' => array(),
'foreign' => array(),
'local' => [],
'foreign' => [],
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
);
];
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));

View File

@@ -5,8 +5,10 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
use Dibi;
use SQLite3;
/**
@@ -19,16 +21,16 @@ require_once dirname(__FILE__) . '/DibiSqliteReflector.php';
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
{
/** @var SQLite3 Connection resource */
use Dibi\Strict;
/** @var SQLite3|NULL */
private $connection;
/** @var SQLite3Result Resultset resource */
/** @var \SQLite3Result|NULL */
private $resultSet;
/** @var bool */
@@ -42,12 +44,12 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new DibiNotSupportedException("PHP extension 'sqlite3' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
}
@@ -55,11 +57,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
DibiConnection::alias($config, 'database', 'file');
Dibi\Helpers::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
@@ -68,8 +70,8 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (Exception $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
@@ -100,8 +102,8 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -110,12 +112,43 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
$res = @$this->connection->query($sql); // intentionally @
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof SQLite3Result) {
} elseif ($res instanceof \SQLite3Result) {
return $this->createResultDriver($res);
}
return NULL;
}
/**
* @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);
}
}
@@ -143,7 +176,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -155,7 +188,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -167,7 +200,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -177,7 +210,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the connection resource.
* @return mixed
* @return SQLite3
*/
public function getResource()
{
@@ -187,20 +220,20 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
return new SqliteReflector($this);
}
/**
* Result set driver factory.
* @param SQLite3Result
* @return IDibiResultDriver
* @param \SQLite3Result
* @return Dibi\ResultDriver
*/
public function createResultDriver(SQLite3Result $resource)
public function createResultDriver(\SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -213,36 +246,68 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
return "'" . $this->connection->escapeString($value) . "'";
return "'" . $this->connection->escapeString($value) . "'";
}
case dibi::BINARY:
return "X'" . bin2hex((string) $value) . "'";
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
/**
* @param string
* @return string
*/
public function escapeBinary($value)
{
return "X'" . bin2hex((string) $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);
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
return '[' . strtr($value, '[]', ' ') . ']';
}
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
@@ -261,28 +326,38 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
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.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
if ($limit >= 0 || $offset > 0) {
$sql .= ' LIMIT ' . (int) $limit . ($offset > 0 ? ' OFFSET ' . (int) $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 : '');
}
}
@@ -303,11 +378,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the number of rows in a result set.
* @return int
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -321,12 +396,12 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = array();
$tmp = [];
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
$tmp[str_replace(['[', ']'], '', $k)] = $v;
}
return $tmp;
}
@@ -338,11 +413,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* 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 DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -364,15 +439,15 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = array();
static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
for ($i = 0; $i < $count; $i++) {
$columns[] = array(
$columns[] = [
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
);
];
}
return $columns;
}
@@ -380,7 +455,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
/**
* Returns the result set resource.
* @return mixed
* @return \SQLite3Result|NULL
*/
public function getResultResource()
{
@@ -399,7 +474,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* @param int num of arguments
* @return void
*/
public function registerFunction($name, $callback, $numArgs = -1)
public function registerFunction($name, callable $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
@@ -413,7 +488,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
public function registerAggregateFunction($name, callable $rowCallback, callable $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}

View File

@@ -5,20 +5,24 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for SQLite database.
*
* @package dibi\drivers
* @internal
*/
class DibiSqliteReflector extends DibiObject implements IDibiReflector
class SqliteReflector implements Dibi\Reflector
{
/** @var IDibiDriver */
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(IDibiDriver $driver)
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
@@ -36,7 +40,7 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = array();
$tables = [];
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
@@ -51,18 +55,12 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getColumns($table)
{
$meta = $this->driver->query("
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
UNION ALL
SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = {$this->driver->escape($table, dibi::TEXT)}
")->fetch(TRUE);
$res = $this->driver->query("PRAGMA table_info({$this->driver->escape($table, dibi::IDENTIFIER)})");
$columns = array();
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$column = $row['name'];
$type = explode('(', $row['type']);
$columns[] = array(
$columns[] = [
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
@@ -72,7 +70,7 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'vendor' => $row,
);
];
}
return $columns;
}
@@ -85,15 +83,15 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$indexes = array();
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info({$this->driver->escape($index, dibi::IDENTIFIER)})");
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
@@ -114,12 +112,12 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = array(
$indexes[] = [
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => array($column['name']),
);
'columns' => [$column['name']],
];
break;
}
}
@@ -136,11 +134,8 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getForeignKeys($table)
{
if (!($this->driver instanceof DibiSqlite3Driver)) {
// throw new DibiNotSupportedException; // @see http://www.sqlite.org/foreignkeys.html
}
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escape($table, dibi::IDENTIFIER)})");
$keys = array();
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})");
$keys = [];
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,12 +5,15 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
use Dibi;
use Dibi\Connection;
use Dibi\Helpers;
/**
* The dibi driver for MS SQL Driver 2005 database.
* The dibi driver for Microsoft SQL Server and SQL Azure databases.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
@@ -20,16 +23,16 @@ require_once dirname(__FILE__) . '/DibiMsSql2005Reflector.php';
* - 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 DibiConnection options
*
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
{
/** @var resource Connection resource */
use Dibi\Strict;
/** @var resource|NULL */
private $connection;
/** @var resource Resultset resource */
/** @var resource|NULL */
private $resultSet;
/** @var bool */
@@ -38,14 +41,17 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $version = '';
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
@@ -53,31 +59,37 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array & $config)
public function connect(array &$config)
{
DibiConnection::alias($config, 'options|UID', 'username');
DibiConnection::alias($config, 'options|PWD', 'password');
DibiConnection::alias($config, 'options|Database', 'database');
DibiConnection::alias($config, 'options|CharacterSet', 'charset');
Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// Default values
if (!isset($config['options']['CharacterSet'])) {
$config['options']['CharacterSet'] = 'UTF-8';
}
$options = $config['options'];
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
// Default values
if (!isset($options['CharacterSet'])) {
$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);
}
if (!is_resource($this->connection)) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code']);
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
}
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
}
@@ -87,15 +99,15 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function disconnect()
{
sqlsrv_close($this->connection);
@sqlsrv_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -104,12 +116,13 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
if ($res === FALSE) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res);
return $this->createResultDriver($res);
}
return NULL;
}
@@ -129,7 +142,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function getInsertId($sequence)
{
$res = sqlsrv_query($this->connection, 'SELECT @@IDENTITY');
$res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0];
@@ -142,7 +155,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -154,7 +167,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -166,7 +179,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -176,7 +189,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/**
* Returns the connection resource.
* @return mixed
* @return resource|NULL
*/
public function getResource()
{
@@ -186,18 +199,18 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMssql2005Reflector($this);
return new SqlsrvReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -212,35 +225,69 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param string value
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
return "'" . str_replace("'", "''", $value) . "'";
}
case dibi::IDENTIFIER:
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
/**
* @param string
* @return string
*/
public function escapeBinary($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'");
default:
throw new InvalidArgumentException('Unsupported type.');
/**
* @param string
* @return string
*/
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
}
/**
* @param bool
* @return string
*/
public function escapeBool($value)
{
return $value ? '1' : '0';
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
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'");
}
@@ -252,40 +299,56 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
public function applyLimit(&$sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') AS T ';
}
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
} 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);
}
}
@@ -309,7 +372,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -331,7 +394,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -352,13 +415,13 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function getResultColumns()
{
$columns = array();
$columns = [];
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = array(
$columns[] = [
'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'],
);
];
}
return $columns;
}
@@ -366,7 +429,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/**
* Returns the result set resource.
* @return mixed
* @return resource|NULL
*/
public function getResultResource()
{

View File

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

View File

@@ -5,14 +5,16 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Profiler & logger event.
*
* @package dibi
*/
class DibiEvent
class Event
{
use Strict;
/** event type */
const CONNECT = 1,
SELECT = 4,
@@ -26,7 +28,7 @@ class DibiEvent
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var DibiConnection */
/** @var Connection */
public $connection;
/** @var int */
@@ -35,7 +37,7 @@ class DibiEvent
/** @var string */
public $sql;
/** @var DibiResult|DibiDriverException|NULL */
/** @var Result|DriverException|NULL */
public $result;
/** @var float */
@@ -48,7 +50,7 @@ class DibiEvent
public $source;
public function __construct(DibiConnection $connection, $type, $sql = NULL)
public function __construct(Connection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
@@ -56,25 +58,25 @@ class DibiEvent
$this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = array(
static $types = [
'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 = array($row['file'], (int) $row['line']);
$this->source = [$row['file'], (int) $row['line']];
break;
}
}
dibi::$elapsedTime = FALSE;
dibi::$numOfQueries++;
dibi::$sql = $sql;
\dibi::$elapsedTime = FALSE;
\dibi::$numOfQueries++;
\dibi::$sql = $sql;
}
@@ -82,14 +84,14 @@ class DibiEvent
{
$this->result = $result;
try {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = $result instanceof Result ? count($result) : NULL;
} catch (Exception $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,39 +5,47 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @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)
* @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)
*/
class DibiFluent extends DibiObject implements IDataSource
class Fluent implements IDataSource
{
use Strict;
const REMOVE = FALSE;
/** @var array */
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'),
);
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'],
];
/** @var array default modifiers for arrays */
public static $modifiers = array(
public static $modifiers = [
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%in',
@@ -47,10 +55,10 @@ class DibiFluent extends DibiObject implements IDataSource
'HAVING' => '%and',
'ORDER BY' => '%by',
'GROUP BY' => '%by',
);
];
/** @var array clauses separators */
public static $separators = array(
public static $separators = [
'SELECT' => ',',
'FROM' => ',',
'WHERE' => 'AND',
@@ -62,47 +70,47 @@ class DibiFluent extends DibiObject implements IDataSource
'SET' => ',',
'VALUES' => ',',
'INTO' => FALSE,
);
];
/** @var array clauses */
public static $clauseSwitches = array(
public static $clauseSwitches = [
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
);
];
/** @var DibiConnection */
/** @var Connection */
private $connection;
/** @var array */
private $setups = array();
private $setups = [];
/** @var string */
private $command;
/** @var array */
private $clauses = array();
private $clauses = [];
/** @var array */
private $flags = array();
private $flags = [];
/** @var array */
private $cursor;
/** @var DibiHashMap normalized clauses */
/** @var HashMap normalized clauses */
private static $normalizer;
/**
* @param DibiConnection
* @param Connection
*/
public function __construct(DibiConnection $connection)
public function __construct(Connection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
}
}
@@ -122,22 +130,22 @@ class DibiFluent extends DibiObject implements IDataSource
if (isset(self::$masks[$clause])) {
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
}
$this->cursor = & $this->clauses[$clause];
$this->cursor = array();
$this->cursor = &$this->clauses[$clause];
$this->cursor = [];
$this->command = $clause;
}
// auto-switch to a clause
if (isset(self::$clauseSwitches[$clause])) {
$this->cursor = & $this->clauses[self::$clauseSwitches[$clause]];
$this->cursor = &$this->clauses[self::$clauseSwitches[$clause]];
}
if (array_key_exists($clause, $this->clauses)) {
// append to clause
$this->cursor = & $this->clauses[$clause];
$this->cursor = &$this->clauses[$clause];
// TODO: really delete?
if ($args === array(self::REMOVE)) {
if ($args === [self::REMOVE]) {
$this->cursor = NULL;
return $this;
}
@@ -145,7 +153,7 @@ class DibiFluent extends DibiObject implements IDataSource
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace
$this->cursor = array();
$this->cursor = [];
} elseif (!empty($this->cursor)) {
$this->cursor[] = $sep;
@@ -154,7 +162,7 @@ class DibiFluent extends DibiObject implements IDataSource
} else {
// append to currect flow
if ($args === array(self::REMOVE)) {
if ($args === [self::REMOVE]) {
return $this;
}
@@ -162,7 +170,7 @@ class DibiFluent extends DibiObject implements IDataSource
}
if ($this->cursor === NULL) {
$this->cursor = array();
$this->cursor = [];
}
// special types or argument
@@ -173,21 +181,21 @@ class DibiFluent extends DibiObject implements IDataSource
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = array('%n', $arg);
$args = [$clause === 'AS' ? '%N' : '%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 = array(self::$modifiers[$clause], $arg);
$args = [self::$modifiers[$clause], $arg];
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
$args = ['%a', $arg];
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = "($arg)";
$arg = new Literal("($arg)");
}
$this->cursor[] = $arg;
}
@@ -203,9 +211,9 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function clause($clause)
{
$this->cursor = & $this->clauses[self::$normalizer->$clause];
$this->cursor = &$this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) {
$this->cursor = array();
$this->cursor = [];
}
return $this;
@@ -265,7 +273,7 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Returns the dibi connection.
* @return DibiConnection
* @return Connection
*/
final public function getConnection()
{
@@ -274,7 +282,7 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Adds DibiResult setup.
* Adds Result setup.
* @param string method
* @param mixed args
* @return self
@@ -292,16 +300,16 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Generates and executes SQL query.
* @param mixed what to return?
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set or number of affected rows
* @throws Exception
*/
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;
@@ -311,12 +319,12 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Generates, executes SQL query and fetches the single row.
* @return DibiRow|FALSE array on success, FALSE if no next record
* @return Row|FALSE
*/
public function fetch()
{
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
@@ -329,8 +337,8 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchSingle()
{
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
@@ -345,7 +353,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchAll($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll();
}
@@ -376,11 +384,11 @@ class DibiFluent extends DibiObject implements IDataSource
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return DibiResultIterator
* @return ResultIterator
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator();
}
@@ -400,20 +408,20 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function count()
{
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]',
))->fetchSingle();
return (int) $this->query([
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle();
}
/**
* @return DibiResult
* @return Result
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array(array($res, array_shift($setup)), $setup);
call_user_func_array([$res, array_shift($setup)], $setup);
}
return $res;
}
@@ -423,11 +431,11 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* @return DibiDataSource
* @return DataSource
*/
public function toDataSource()
{
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
return new DataSource($this->connection->translate($this->_export()), $this->connection);
}
@@ -439,28 +447,32 @@ class DibiFluent extends DibiObject 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 DibiTranslator.
* Generates parameters for Translator.
* @param string clause name
* @return array
*/
protected function _export($clause = NULL, $args = array())
protected function _export($clause = NULL, $args = [])
{
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 = array($clause => $this->clauses[$clause]);
$data = [$clause => $this->clauses[$clause]];
} else {
return array();
return [];
}
}
@@ -500,10 +512,10 @@ class DibiFluent extends DibiObject implements IDataSource
{
// remove references
foreach ($this->clauses as $clause => $val) {
$this->clauses[$clause] = & $val;
$this->clauses[$clause] = &$val;
unset($val);
}
$this->cursor = & $foo;
$this->cursor = &$foo;
}
}

View File

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

277
src/Dibi/Helpers.php Normal file
View File

@@ -0,0 +1,277 @@
<?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((string) $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((string) $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

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

View File

@@ -5,14 +5,18 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi file logger.
*
* @package dibi
*/
class DibiFileLogger extends DibiObject
class FileLogger
{
use Dibi\Strict;
/** @var string Name of the file where SQL errors should be logged */
public $file;
@@ -23,7 +27,7 @@ class DibiFileLogger extends DibiObject
public function __construct($file, $filter = NULL)
{
$this->file = $file;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
}
@@ -31,7 +35,7 @@ class DibiFileLogger extends DibiObject
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
public function logEvent(Dibi\Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -43,7 +47,7 @@ class DibiFileLogger extends DibiObject
}
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,22 +5,26 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi FirePHP logger.
*
* @package dibi
*/
class DibiFirePhpLogger extends DibiObject
class FirePhpLogger
{
use Dibi\Strict;
/** maximum number of rows */
static public $maxQueries = 30;
public static $maxQueries = 30;
/** maximum SQL length */
static public $maxLength = 1000;
public static $maxLength = 1000;
/** size of json stream chunk */
static public $streamChunkSize = 4990;
public static $streamChunkSize = 4990;
/** @var int */
public $filter;
@@ -32,7 +36,7 @@ class DibiFirePhpLogger extends DibiObject
public $numOfQueries = 0;
/** @var array */
private static $fireTable = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
private static $fireTable = [['Time', 'SQL Statement', 'Rows', 'Connection']];
/**
@@ -46,7 +50,7 @@ class DibiFirePhpLogger extends DibiObject
public function __construct($filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
}
@@ -54,7 +58,7 @@ class DibiFirePhpLogger extends DibiObject
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
public function logEvent(Dibi\Event $event)
{
if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) {
return;
@@ -67,20 +71,20 @@ class DibiFirePhpLogger extends DibiObject
}
$this->totalTime += $event->time;
$this->numOfQueries++;
self::$fireTable[] = array(
self::$fireTable[] = [
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(array(
array(
$payload = json_encode([
[
'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) {
$num++;
header("X-Wf-dibi-1-1-d$num: |$s|\\"); // protocol-, structure-, plugin-, message-index

View File

@@ -0,0 +1,166 @@
<?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|NULL $size
* @property-read bool|NULL $unsigned
* @property-read bool|NULL $nullable
* @property-read bool|NULL $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|NULL
*/
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 string
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int|NULL
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool|NULL
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool|NULL
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool|NULL
*/
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

@@ -0,0 +1,115 @@
<?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

@@ -0,0 +1,54 @@
<?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

@@ -0,0 +1,70 @@
<?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

@@ -0,0 +1,106 @@
<?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

@@ -0,0 +1,201 @@
<?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

@@ -5,6 +5,8 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi result set.
@@ -16,42 +18,42 @@
* $value = $result->fetchSingle();
* $table = $result->fetchAll();
* $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('active,#,id');
* $assoc = $result->fetchAssoc('col1');
* $assoc = $result->fetchAssoc('col1[]col2->col3');
*
* unset($result);
* </code>
*
* @package dibi
*
* @property-read int $rowCount
*/
class DibiResult extends DibiObject implements IDataSource
class Result implements IDataSource
{
/** @var array IDibiResultDriver */
use Strict;
/** @var array ResultDriver */
private $driver;
/** @var array Translate table */
private $types = array();
private $types = [];
/** @var DibiResultInfo */
/** @var Reflection\Result */
private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE;
/** @var string returned object class */
private $rowClass = 'DibiRow';
private $rowClass = 'Dibi\Row';
/** @var Callback returned object factory*/
/** @var callable returned object factory*/
private $rowFactory;
/** @var array format */
private $formats = array();
private $formats = [];
/**
* @param IDibiResultDriver
* @param ResultDriver
*/
public function __construct($driver)
{
@@ -65,6 +67,7 @@ class DibiResult extends DibiObject implements IDataSource
*/
final public function getResource()
{
trigger_error(__METHOD__ . '() is deprecated, use getResultDriver()->getResultResource().', E_USER_DEPRECATED);
return $this->getResultDriver()->getResultResource();
}
@@ -84,13 +87,13 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Safe access to property $driver.
* @return IDibiResultDriver
* @throws RuntimeException
* @return ResultDriver
* @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;
@@ -104,7 +107,7 @@ class DibiResult extends DibiObject 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 DibiException
* @throws Exception
*/
final public function seek($row)
{
@@ -134,11 +137,11 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Required by the IteratorAggregate interface.
* @return DibiResultIterator
* @return ResultIterator
*/
final public function getIterator()
{
return new DibiResultIterator($this);
return new ResultIterator($this);
}
@@ -146,7 +149,7 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Set fetched object class. This class should extend the DibiRow class.
* Set fetched object class. This class should extend the Row class.
* @param string
* @return self
*/
@@ -168,11 +171,10 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Set a factory to create fetched object instances. These should extend the DibiRow class.
* @param callback
* Set a factory to create fetched object instances. These should extend the Row class.
* @return self
*/
public function setRowFactory($callback)
public function setRowFactory(callable $callback)
{
$this->rowFactory = $callback;
return $this;
@@ -182,7 +184,7 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Fetches the row at current position, process optional type conversion.
* and moves the internal cursor to the next position
* @return DibiRow|FALSE array on success, FALSE if no next record
* @return Row|FALSE
*/
final public function fetch()
{
@@ -221,7 +223,7 @@ class DibiResult extends DibiObject implements IDataSource
* Fetches all records from table.
* @param int offset
* @param int limit
* @return DibiRow[]
* @return Row[]
*/
final public function fetchAll($offset = NULL, $limit = NULL)
{
@@ -229,10 +231,10 @@ class DibiResult extends DibiObject implements IDataSource
$this->seek((int) $offset);
$row = $this->fetch();
if (!$row) {
return array(); // empty result set
return []; // empty result set
}
$data = array();
$data = [];
do {
if ($limit === 0) {
break;
@@ -253,8 +255,8 @@ class DibiResult extends DibiObject implements IDataSource
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return DibiRow
* @throws InvalidArgumentException
* @return array
* @throws \InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
@@ -265,17 +267,17 @@ class DibiResult extends DibiObject implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return array(); // empty result set
return []; // empty result set
}
$data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
// check columns
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.");
}
}
@@ -289,12 +291,12 @@ class DibiResult extends DibiObject implements IDataSource
// make associative tree
do {
$x = & $data;
$x = &$data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node
$x = & $x[];
$x = &$x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i + 1]};
@@ -303,14 +305,14 @@ class DibiResult extends DibiObject implements IDataSource
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i + 1]};
$x = &$x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i + 1]};
$x = &$x->{$assoc[$i + 1]};
}
} elseif ($as !== '|') { // associative-array node
$x = & $x[$row->$as];
$x = &$x[$row->$as];
}
}
@@ -333,7 +335,7 @@ class DibiResult extends DibiObject implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return array(); // empty result set
return []; // empty result set
}
$data = NULL;
@@ -354,32 +356,32 @@ class DibiResult extends DibiObject implements IDataSource
}
do {
$x = & $data;
$x = &$data;
foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node
$x = & $x[];
$x = &$x[];
} elseif ($as === '=') { // "record" node
if ($x === NULL) {
$x = $row->toArray();
$x = & $x[ $assoc[$i + 1] ];
$x = &$x[ $assoc[$i + 1] ];
$x = NULL; // prepare child node
} else {
$x = & $x[ $assoc[$i + 1] ];
$x = &$x[ $assoc[$i + 1] ];
}
} elseif ($as === '@') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i + 1]};
$x = &$x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i + 1]};
$x = &$x->{$assoc[$i + 1]};
}
} else { // associative-array node
$x = & $x[$row->$as];
$x = &$x[$row->$as];
}
}
@@ -403,21 +405,21 @@ class DibiResult extends DibiObject 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 array(); // empty result set
return []; // empty result set
}
$data = array();
$data = [];
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
@@ -434,7 +436,7 @@ class DibiResult extends DibiObject 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
@@ -445,7 +447,7 @@ class DibiResult extends DibiObject implements IDataSource
}
if (!property_exists($row, $key)) {
throw new InvalidArgumentException("Unknown key column '$key'.");
throw new \InvalidArgumentException("Unknown key column '$key'.");
}
}
@@ -466,12 +468,12 @@ class DibiResult extends DibiObject implements IDataSource
*/
private function detectTypes()
{
$cache = DibiColumnInfo::getTypeCache();
$cache = Helpers::getTypeCache();
try {
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = $cache->{$col['nativetype']};
$this->types[$col['name']] = isset($col['type']) ? $col['type'] : $cache->{$col['nativetype']};
}
} catch (DibiNotSupportedException $e) {
} catch (NotSupportedException $e) {
}
}
@@ -481,32 +483,52 @@ class DibiResult extends DibiObject implements IDataSource
* @param array
* @return void
*/
private function normalize(array & $row)
private function normalize(array &$row)
{
foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL
continue;
}
$value = $row[$key];
if ($value === FALSE || $type === dibi::TEXT) {
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
} elseif ($type === Type::INTEGER) {
$row[$key] = is_float($tmp = $value * 1)
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === dibi::FLOAT) {
$row[$key] = str_replace(',', '.', ltrim((string) ($tmp = (float) $value), '0')) === ltrim(rtrim(rtrim($value, '0'), '.'), '0') ? $tmp : $value;
} elseif ($type === Type::FLOAT) {
$value = ltrim((string) $value, '0');
$p = strpos($value, '.');
if ($p !== FALSE) {
$value = rtrim(rtrim($value, '0'), '.');
}
if ($value === '' || $value[0] === '.') {
$value = '0' . $value;
}
$row[$key] = $value === str_replace(',', '.', (string) ($float = (float) $value))
? $float
: $value;
} elseif ($type === dibi::BOOL) {
} elseif ($type === Type::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', NULL, FALSE, '0000-00-00', ...
$value = new DibiDateTime($value);
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
if ($value && substr((string) $value, 0, 3) !== '000') { // '', NULL, FALSE, '0000-00-00', ...
$value = new DateTime($value);
$row[$key] = empty($this->formats[$type]) ? $value : $value->format($this->formats[$type]);
} else {
$row[$key] = NULL;
}
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
} 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);
}
}
}
@@ -515,7 +537,7 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Define column type.
* @param string column
* @param string type (use constant Dibi::*)
* @param string type (use constant Type::*)
* @return self
*/
final public function setType($col, $type)
@@ -536,9 +558,9 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Sets data format.
* @param string type (use constant Dibi::*)
* @param string format
* Sets date format.
* @param string
* @param string|NULL format
* @return self
*/
final public function setFormat($type, $format)
@@ -550,7 +572,7 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Returns data format.
* @return string
* @return string|NULL
*/
final public function getFormat($type)
{
@@ -563,19 +585,19 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Returns a meta information about the current result set.
* @return DibiResultInfo
* @return Reflection\Result
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new DibiResultInfo($this->getResultDriver());
$this->meta = new Reflection\Result($this->getResultDriver());
}
return $this->meta;
}
/**
* @deprecated
* @return Reflection\Column[]
*/
final public function getColumns()
{
@@ -592,66 +614,7 @@ class DibiResult extends DibiObject implements IDataSource
*/
final public function dump()
{
$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";
}
}
echo Helpers::dump($this);
}
}

View File

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

View File

@@ -5,13 +5,13 @@
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Result set single row.
*
* @package dibi
*/
class DibiRow implements ArrayAccess, IteratorAggregate, Countable
class Row implements \ArrayAccess, \IteratorAggregate, \Countable
{
public function __construct($arr)
@@ -32,21 +32,28 @@ class DibiRow 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 DibiDateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
if (!$time instanceof DateTime) {
if (!$time || substr((string) $time, 0, 3) === '000') { // '', NULL, FALSE, '0000-00-00', ...
return NULL;
}
$time = new DibiDateTime($time);
$time = new DateTime($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**/
@@ -58,7 +65,7 @@ class DibiRow implements ArrayAccess, IteratorAggregate, Countable
final public function getIterator()
{
return new ArrayIterator($this);
return new \ArrayIterator($this);
}

152
src/Dibi/Strict.php Normal file
View File

@@ -0,0 +1,152 @@
<?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 callable
* @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

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

32
src/Dibi/Type.php Normal file
View File

@@ -0,0 +1,32 @@
<?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

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

156
src/Dibi/exceptions.php Normal file
View File

@@ -0,0 +1,156 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi common exception.
*/
class Exception extends \Exception
{
/** @var string|NULL */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param mixed
* @param string SQL command
*/
public function __construct($message = '', $code = 0, $sql = NULL)
{
parent::__construct($message);
$this->code = $code;
$this->sql = $sql;
}
/**
* @return string The SQL passed to the constructor
*/
final public function getSql()
{
return $this->sql;
}
/**
* @return string string represenation of exception with SQL command
*/
public function __toString()
{
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
}
/**
* database server exception.
*/
class DriverException extends Exception
{
}
/**
* PCRE exception.
*/
class PcreException extends Exception
{
public function __construct($message = '%msg.')
{
static $messages = [
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
{
}
/**
* Database procedure exception.
*/
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
{
}

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 IDibiDriver
interface Driver
{
/**
* Connects to a database.
* @param array
* @return void
* @throws DibiException
* @throws Exception
*/
function connect(array & $config);
function connect(array &$config);
/**
* Disconnects from a database.
* @return void
* @throws DibiException
* @throws Exception
*/
function disconnect();
/**
* Internal: Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return ResultDriver|NULL
* @throws DriverException
*/
function query($sql);
@@ -63,7 +63,7 @@ interface IDibiDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function begin($savepoint = NULL);
@@ -71,7 +71,7 @@ interface IDibiDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function commit($savepoint = NULL);
@@ -79,7 +79,7 @@ interface IDibiDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function rollback($savepoint = NULL);
@@ -91,18 +91,46 @@ interface IDibiDriver
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Reflector
*/
function getReflector();
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
function escape($value, $type);
function escapeText($value);
/**
* @param string
* @return string
*/
function escapeBinary($value);
/**
* @param string
* @return string
*/
function escapeIdentifier($value);
/**
* @param bool
* @return string
*/
function escapeBool($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDate($value);
/**
* @param \DateTime|\DateTimeInterface|string|int
* @return string
*/
function escapeDateTime($value);
/**
* Encodes string for use in a LIKE statement.
@@ -114,18 +142,20 @@ interface IDibiDriver
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string
* @param int|NULL
* @param int|NULL
* @return void
*/
function applyLimit(& $sql, $limit, $offset);
function applyLimit(&$sql, $limit, $offset);
}
/**
* dibi result set driver interface.
* @package dibi
*/
interface IDibiResultDriver
interface ResultDriver
{
/**
@@ -138,7 +168,7 @@ interface IDibiResultDriver
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @throws Exception
*/
function seek($row);
@@ -171,22 +201,18 @@ interface IDibiResultDriver
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
function unescape($value, $type);
function unescapeBinary($value);
}
/**
* dibi driver reflection.
*
* @package dibi
*/
interface IDibiReflector
interface Reflector
{
/**

147
src/loader.php Normal file
View File

@@ -0,0 +1,147 @@
<?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\DibiExtension21' => 'Bridges/Nette/DibiExtension21.php',
'Dibi\Bridges\Nette\DibiExtension22' => 'Bridges/Nette/DibiExtension22.php',
'Dibi\Bridges\Nette\Panel' => 'Bridges/Nette/Panel.php',
'Dibi\Bridges\Tracy\Panel' => 'Bridges/Tracy/Panel.php',
'Dibi\Connection' => 'Connection.php',
'Dibi\ConstraintViolationException' => 'exceptions.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\MsSqlDriver' => 'Drivers/MsSqlDriver.php',
'Dibi\Drivers\MsSqlReflector' => 'Drivers/MsSqlReflector.php',
'Dibi\Drivers\MySqlDriver' => 'Drivers/MySqlDriver.php',
'Dibi\Drivers\MySqlReflector' => 'Drivers/MySqlReflector.php',
'Dibi\Drivers\MySqliDriver' => 'Drivers/MySqliDriver.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\Drivers\SqlsrvDriver' => 'Drivers/SqlsrvDriver.php',
'Dibi\Drivers\SqlsrvReflector' => 'Drivers/SqlsrvReflector.php',
'Dibi\Event' => 'Event.php',
'Dibi\Exception' => 'exceptions.php',
'Dibi\Fluent' => 'Fluent.php',
'Dibi\ForeignKeyConstraintViolationException' => 'exceptions.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\NotNullConstraintViolationException' => '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',
'Dibi\UniqueConstraintViolationException' => 'exceptions.php',
], $old2new = [
'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/.coveralls.yml Normal file
View File

@@ -0,0 +1,4 @@
# for php-coveralls
service_name: travis-ci
coverage_clover: coverage.xml
json_path: coverage.json

View File

@@ -0,0 +1,89 @@
[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,3 +1,13 @@
[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
@@ -6,7 +16,7 @@ password =
charset = utf8
system = mysql
[mysqli]
[mysql improved]
driver = mysqli
host = 127.0.0.1
username = root
@@ -14,26 +24,37 @@ password =
charset = utf8
system = mysql
[sqlite2]
driver = sqlite
database = :memory:
system = sqlite
[mysql pdo]
driver = pdo
dsn = "mysql:host=127.0.0.1"
username = root
password =
system = mysql
[sqlite3] ; default
driver = sqlite3
database = :memory:
system = sqlite
[pgsql]
[postgre]
driver = postgre
host = 127.0.0.1
username = postgres
password =
system = pgsql
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_tmp.mdb"
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]
@@ -43,34 +64,43 @@ username = dibi
password =
system = mssql
[mssql2005]
driver = mssql2005
host = (local)
[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
[sqlite-pdo]
[oracle pdo]
driver = pdo
dsn = "sqlite::memory:"
system = sqlite
[mysql-pdo]
driver = pdo
dsn = "mysql:host=127.0.0.1"
username = root
dsn = "oci:dbname=dibi"
username = dibi
password =
system = mysql
system = oracle
[pgsql-pdo]
driver = pdo
dsn = "pgsql:host=127.0.0.1;dbname=dibi_test"
username = postgres
[firebird]
driver = firebird
database = database.fdb
username = dibi
password =
system = pgsql
system = firebird

View File

@@ -0,0 +1,46 @@
[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

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

View File

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

View File

@@ -0,0 +1,320 @@
<?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

@@ -0,0 +1,56 @@
<?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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,70 +0,0 @@
<?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,57 +0,0 @@
<?php
/**
* @dataProvider ../databases.ini
*/
use Tester\Assert;
require __DIR__ . '/bootstrap.php';
if ($config['system'] === 'odbc') {
Tester\Environment::skip('Not supported.');
}
$conn = new DibiConnection($config);
$conn->loadFile(__DIR__ . "/data/$config[system].sql");
$info = $conn->query('
SELECT products.product_id, orders.order_id, customers.name, products.product_id + 1 AS xxx
FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
')->getInfo();
Assert::same(
array('product_id', 'order_id', 'name', 'xxx'),
$info->getColumnNames()
);
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
Assert::same(
array('products.product_id', 'orders.order_id', 'customers.name', 'xxx'),
$info->getColumnNames(TRUE)
);
}
$columns = $info->getColumns();
Assert::same('product_id', $columns[0]->name);
if ($config['driver'] !== 'sqlite3' && $config['driver'] !== 'pdo') {
Assert::same('products', $columns[0]->tableName);
}
Assert::null($columns[0]->getVendorInfo('xxx'));
if ($config['system'] !== 'sqlite') {
Assert::same('i', $columns[0]->type);
}
Assert::null($columns[0]->nullable);
Assert::same('xxx', $columns[3]->name);
Assert::null($columns[3]->tableName);
if ($config['system'] !== 'sqlite') {
Assert::same('i', $columns[0]->type);
}
Assert::null($columns[3]->nullable);

View File

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

View File

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

View File

@@ -0,0 +1,101 @@
<?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

@@ -0,0 +1,97 @@
<?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

@@ -0,0 +1,50 @@
<?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

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

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