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

Compare commits

...

745 Commits
v0.6 ... v3.0.5

Author SHA1 Message Date
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
David Grudl
d708ac2aeb tests: added new, typo 2015-10-06 15:39:37 +02:00
David Grudl
806ee1ccd1 DibiObject: fixed compatibility with PHP 7 2015-10-06 15:35:55 +02:00
David Grudl
0ec544043f removed rarely used @property 2015-10-06 12:10:22 +02:00
David Grudl
6dcdd68d6d removed @author 2015-10-06 12:10:21 +02:00
David Grudl
9ad502887b used https 2015-10-06 12:10:20 +02:00
David Grudl
f06425b9a0 travis: migrating to container-based infrastructure 2015-07-23 18:54:35 +02:00
David Grudl
ca99b0b822 improved coding style 2015-06-19 15:00:23 +02:00
David Grudl
462ef6934b readme.md: added badges 2015-06-15 15:14:45 +02:00
Jan Tvrdik
84c85bc536 travis: run tests on PHP 7 2015-06-15 15:14:44 +02:00
David Grudl
d3151f7c65 DibiTranslator: deprecated support for hex number in strings '0xFF' (BC break)
related to PHP7 and https://wiki.php.net/rfc/remove_hex_support_in_numeric_strings
2015-06-15 14:57:38 +02:00
David Grudl
66afffcddc DibiTranslator: small refactoring 2015-06-15 14:42:48 +02:00
David Grudl
7762da1bbb Released version 2.3.2 2015-04-18 16:26:53 +02:00
David Grudl
6f6a63881a Merge pull request #176 from milo/pull-oracle
Oracle fixes
2015-04-17 15:47:00 +02:00
Miloslav Hůla
999f51a7bd OracleDriver: cast type NUMBER(p, 0) as an INTEGER
In many cases, Oracle returns integers as a NUMBER(8,0). Type NUMBER is casted to (float) which is unnecessary in this case.
2015-04-17 15:22:45 +02:00
Miloslav Hůla
64b3d0c6f4 Tracy panels: Oracle uses EXPLAIN PLAN FOR
Warning 'ORA-00905: missing keyword' is emmited otherwise.
2015-04-17 15:21:21 +02:00
Miloslav Hůla
06440ab6dc OracleDriver: shut-up oci_execute()
The oci_execute() emits warnings e.g. on denied permissions when EXPLAIN.
2015-04-17 15:17:14 +02:00
David Grudl
b341b66d43 Merge pull request #175 from milo/pull-autowired
Nette extensions: added 'autowired' configuration
2015-04-16 16:47:39 +02:00
Miloslav Hůla
aac83ba849 Nette extensions: added 'autowired' configuration 2015-04-16 15:01:00 +02:00
David Grudl
2de32b66e1 Merge pull request #174 from baasha/baasha-patch-1
DibiPdoDriver::escape() added support for dblib
2015-03-27 00:05:09 +01:00
baasha
1829366fc9 DibiPdoDriver::escape() added support for dblib
Fixed escaping for linux version of mssql driver - dblib.
2015-03-26 23:47:06 +01:00
Radovan Kepák
94f34a2a33 DibiPdoDriver: added support for version specified in options
If driver do not support ATTR_SERVER_VERSION (like dblib) fallback to the version from config, or set null as we do not know what is the driver version.

I belive this is better solution then add some creazy switch with queries for all possible versions on all drivers, and becouse it is faster to pass variable then run query asking about version.
2015-03-12 15:11:04 +01:00
David Grudl
a3e5ac86f7 Released version 2.3.1 2015-02-25 15:22:04 +01:00
David Grudl
89d7148b04 removed version.txt 2015-02-25 15:21:48 +01:00
David Grudl
38aa393dc3 dibi: named connections are allowed [Closes #161]
Partially reverts commit a923ce7ecb.
2015-02-25 15:13:52 +01:00
David Grudl
84f9a6fdd8 Merge pull request #164 from bckp/patch-2
Added support for MsSQL2012 ( allow offset )
2015-02-20 12:11:02 +01:00
Radovan Kepák
eb64adeb05 Dibi: Dump now recognize MsSql2012 offset as keyword
Now $connection->test send proper highlighted code
2015-02-20 09:09:37 +01:00
Radovan Kepák
3779a5034a DibiPdoDriver: added support for MsSql2012 Offset
Since MsSql2012 allow using of OFFSET, I added this option to the driver for PDO. Driver will automaticly recognize this is proper version and use the new offset.
2015-02-20 09:04:43 +01:00
David Grudl
090bb2f182 Merge pull request #163 from bckp/patch-1
Tracy\Panel: added vector icon
2015-02-20 00:19:37 +01:00
Radovan Kepák
056c0702a1 Tracy\Panel: added vector icon 2015-02-20 00:16:05 +01:00
David Grudl
020b15c0e2 added contributing.md
inspiration https://github.com/necolas/issue-guidelines
2015-01-27 14:51:42 +01:00
David Grudl
5970db58aa Released version 2.3.0 2015-01-25 17:24:28 +01:00
David Grudl
5ea37c9894 Merge pull request #160 from milo/pull-pglike
Fix DibiPostgreDriver::escapeLike()
2015-01-23 17:26:05 +01:00
Miloslav Hůla
2892e3eae3 Postgre: added test for matching by %like 2015-01-23 09:29:58 +01:00
Miloslav Hůla
91e2d76a0a Postgre: fixed %like escaping [Closes #159] 2015-01-23 09:13:05 +01:00
David Grudl
97b50bd243 Dibi: $defaultDriver changed to mysqli [Closes #156] 2015-01-13 15:45:17 +01:00
David Grudl
97d4c8c35f Released 2.3.0-RC1 2015-01-13 15:35:18 +01:00
David Grudl
f7fd9104e9 removed bridge for Nette 2.0 (BC break) 2015-01-13 15:35:18 +01:00
David Grudl
a923ce7ecb dibi: named connections and activate() are deprecated (BC break) 2015-01-13 15:31:03 +01:00
David Grudl
9a95edf003 Merge pull request #149 from JirkaChadima/oracle-schema
Oracle: adds support for login schema option
2015-01-13 05:25:33 +01:00
MartyIX
23efb97b0c DibiFluent: add leftJoin and on to phpdoc. 2015-01-13 05:24:23 +01:00
Ondrej Brablc
3b96dc7012 DibiFirePhpLogger: save some header operations for sites with hundreds of sql queries. 2015-01-13 05:22:45 +01:00
Ondrej Brablc
39be00e08b DibiFirePhpLogger: Allow user defined size of json stream chunks [Closes #148] 2015-01-13 05:22:34 +01:00
Martin Hradil
d6826d62ed DibiTranslator: respect %if blocks for %lmt and %ofs as well [Closes #145][Closes #87] 2015-01-13 05:14:42 +01:00
Petr BAGR Smrkovský
9189d56c05 DibiResult: float detection locale fix [Closes #154] 2015-01-13 04:40:36 +01:00
David Grudl
26b167fe13 DibiMySqliDriver.php: fixes for HHVM 2015-01-12 11:01:46 +01:00
David Grudl
100f978b9b DibiPdoDriver: missing driver throws DibiNotSupportedException exception 2015-01-12 11:01:12 +01:00
David Grudl
8395abb04f added new tests 2015-01-12 10:41:07 +01:00
David Grudl
f5f4f786f1 tests: improved testing environment 2015-01-12 10:41:06 +01:00
David Grudl
59da4bd66a DibiDatabaseInfo: no table is returned as NULL 2015-01-12 08:58:28 +01:00
David Grudl
50782c037c DibiSqliteReflector: fixed detection of autoincrement 2015-01-12 08:58:27 +01:00
David Grudl
ddbf8c779e DibiOdbcDriver: compatible applyLimit 2015-01-12 04:45:30 +01:00
David Grudl
e2fe4d122e DibiPdoDriver: improved and fixed escaping 2015-01-12 04:45:30 +01:00
David Grudl
2082357f0c * .travis: added code checker 2015-01-10 19:11:30 +01:00
David Grudl
c11a97294a examples: improved Tracy examples 2015-01-10 19:07:29 +01:00
Ing. Andrej Poliak
4f315a0d74 DibiPostgreDriver: added support for pg_ping [Closes #144] 2015-01-10 18:41:56 +01:00
David Grudl
34deb6c04f Merge pull request #158 from paranoiq/patch-1
dump: added bunch of reserved words
2015-01-05 12:49:25 +01:00
Vlasta Neubauer
31122c1969 dump: added bunch of reserved words 2015-01-05 12:40:41 +01:00
David Grudl
f534c15f0e Merge pull request #155 from JirkaChadima/dbliblimitsupport
MSSQL: Adds limit support for PDO dblib driver on unix
2014-11-04 23:47:23 +01:00
Jirka Chadima
985f59a2b2 MSSQL: Adds limit support for PDO dblib driver on unix 2014-11-04 11:43:05 +01:00
David Grudl
6fc99254ab Merge pull request #152 from zeleznypa/master
Oracle does not support any brackets around table name
2014-10-26 14:01:41 +01:00
Pavel Zelezny
dc688f3ee7 Oracle use double quotes for escaping 2014-10-26 13:56:34 +01:00
Jirka Chadima
4e99d7821c Oracle: adds support for login schema option 2014-10-21 17:01:59 +02:00
David Grudl
cde5af7cbe Merge pull request #142 from JanRossler/multi-search-path
PostgreSQL: fixed identifier escaping in reflection.
2014-07-16 17:46:08 +02:00
Rossler Jan
7c35e49a1c PostgreSQL: fixed identifier escaping in reflection. 2014-07-16 14:06:14 +02:00
David Grudl
6b08cf0711 Merge pull request #140 from JanRossler/multi-search-path
PostgreSQL: fixed search path resolution in table and column reflection.
2014-07-16 11:46:38 +02:00
David Grudl
4b0ebc76b0 Merge pull request #141 from JanRossler/php52
DibiDateTime: Restored php 5.2 support.
2014-07-15 12:46:42 +02:00
Rossler Jan
5993ea8aaf DibiDateTime: Restored php 5.2 support. 2014-07-15 12:39:51 +02:00
Rossler Jan
f89a2310cc PostgreSQL: fixed search path resolution in table and column reflection. 2014-07-07 01:51:56 +02:00
David Grudl
a118c2cf96 Released version 2.2.2 2014-06-30 17:08:04 +02:00
castamir
9b0e64220b Tracy loaded from composer instead of a local copy [Closes #139] 2014-06-30 14:52:44 +02:00
David Grudl
1c386d5582 typos 2014-06-30 14:52:43 +02:00
David Grudl
c9944b3886 Partially reverts "DibiTranslator: object is initialized in constructor", fixes lazy connection [Closes #138]
This reverts commit 0071b80938.
2014-06-24 21:40:54 +02:00
David Grudl
d0fd009dda readme.md: buy me a coffee 2014-06-13 17:54:46 +02:00
David Grudl
c32251357d fixed bug 'interface IBarPanel not found' [Closes #137] 2014-06-11 16:16:32 +02:00
David Grudl
c23d9c2866 Released version 2.2.0 2014-06-02 16:35:20 +02:00
David Grudl
60893a1c11 removed some old and deprecated stuff 2014-06-02 16:35:19 +02:00
David Grudl
f31d4a9afa tests: Nette\Debugger replaced with Tracy, examples requires PHP 5.3 2014-06-02 16:03:04 +02:00
David Grudl
abd3e2116c added bridge for Nette 2.2 & Tracy 2014-06-02 16:03:03 +02:00
David Grudl
e4e767048f fixed example 2014-06-02 16:03:03 +02:00
David Grudl
1ebb2deb83 file dibi.php split to Dibi class and loader dibi.php 2014-06-02 16:03:02 +02:00
David Grudl
47f8a6f88d bridges: changed file structure 2014-05-13 17:45:47 +02:00
Rossler Jan
fee5c294d8 PostgreSQL: added support for reflection of materialized views. 2014-05-13 17:45:46 +02:00
David Grudl
b14a4efbbb typos 2014-05-13 17:44:07 +02:00
David Grudl
6949f37a7a removed magic_quotes_runtime checking 2014-05-13 17:44:06 +02:00
David Grudl
e99ce9d053 updated test support files 2014-05-13 02:59:34 +02:00
Emmanuel еΜanwʬĔbdƎv
d4c72bbd4d readme.md: typos 2014-05-13 02:42:36 +02:00
Caspern
c802f9343a .gitattributes: ignoring some paths when downloading from github 2014-05-13 02:34:35 +02:00
David Grudl
771bdbe124 fixed test support files 2014-03-24 19:18:11 +01:00
David Grudl
1a4fca41a7 composer: added branch alias & PHP version 2014-03-24 19:08:18 +01:00
Patrik Votocek
176b1a8895 Add support for DateTimeInterface 2014-03-24 19:03:31 +01:00
David Grudl
0071b80938 DibiTranslator: object is initialized in constructor 2014-03-24 19:03:31 +01:00
David Grudl
4f64bd726b Merge pull request #129 from erikfercak/add_apostrophes_to_dates
Add aposthrophes back to dates
2014-02-22 04:08:22 +01:00
Erik Fercak
7fc3d76072 Add aposthrophes back to dates
Commit 7318658017 removed apostrophes from dates and that caused
dibi to build different queries. Compare:
...WHERE `date_created` < '2014-02-21';
vs.
...WHERE `date_created` < 2014-02-21;
2014-02-21 14:08:07 +01:00
David Grudl
a6cc588d91 Merge pull request #128 from brablc/master
Avoid error handler invocation
2014-02-20 19:38:54 +01:00
Ondrej Brablc
6666d71e5b Avoid error handler invocation 2014-02-20 18:47:35 +01:00
David Grudl
5082282e35 Merge pull request #127 from Ciki/patch-1
fix casting to float
2014-02-11 17:34:17 +01:00
Ciki
7cee7997e2 fix casting to float
Now, when sql returns float from (0,1) interval, e.g. '.842' - the decimal part is missing & it won't get cast to float.
This is fix for that situation
2014-02-11 17:04:19 +01:00
David Grudl
738c0c91ed DibiConnection::loadFile() supports DELIMITER [Closes #119] 2014-02-04 03:09:31 +01:00
David Grudl
2769f1ae0a typos 2014-02-04 03:09:31 +01:00
David Grudl
bf8fb69b9a Merge pull request #118 from emanwebdev/patch-1
Fix minor typos
2013-12-27 02:53:48 -08:00
Emmanuel еΜanwʬĔbdƎv
3780a42971 Fix minor typos 2013-12-27 11:33:16 +01:00
David Grudl
791d001bfd DibiDateTime: fixed buggy constructor 2013-12-19 04:21:45 +01:00
David Grudl
367b115ad2 DibiRow: null time checking consistent with DibiResult 2013-12-12 18:14:18 +01:00
Etienne CHAMPETIER
7318658017 Use DateTime instead of date()
On 32bits systems, date() is affected bug the 2038 bug
DateTime always uses 64bits representation [Closes #110]

Signed-off-by: Etienne CHAMPETIER <etienne.champetier@fiducial.net>
2013-12-12 18:14:17 +01:00
David Grudl
ddf7b74bf0 DibiDateTime: works with UNIX timestamps bigger than 32bits 2013-12-12 18:14:17 +01:00
Rossler Jan
30b5290c9d DibiDateTime: fixed unknown timezone error when serialized date includes the timezone (i. e. 'Y-m-d H:i:sP') [Closes #107] 2013-12-12 06:18:12 +01:00
David Grudl
a36678d3db Merge pull request #114 from JanTvrdik/master_array_typo
typo (array of foo -> foo[]) – master
2013-11-05 12:10:20 -08:00
Jan Tvrdik
530e7d30c9 typo 2013-10-24 21:15:48 +02:00
David Grudl
3b1e9e2632 typos 2013-10-17 01:20:44 +02:00
Rossler Jan
1b38d13422 DibiResult: fixed illegal offset type in fetchPairs. 2013-10-17 01:20:43 +02:00
daliborcaja
f348828223 DibiFluent::execute(dibi::AFFECTED_ROWS) returns number of affected rows 2013-10-17 01:20:42 +02:00
Jiří Pudil
5599dde525 Implemented getForeignKeys() in DibiMySqlReflector 2013-10-17 01:20:42 +02:00
Jan Tvrdik
32518eca54 composer.json: fix compatibility with dg/dibi requirements 2013-10-11 13:05:30 +02:00
David Grudl
a47395d16a Merge pull request #106 from enumag/patch-1
Fixed usage of deprecated parameter
2013-08-29 01:28:25 -07:00
Jáchym Toušek
a1f7413c9f Fixed usage of deprecated parameter 2013-08-29 03:02:11 +02:00
David Grudl
1728437f5d Merge pull request #101 from milo/fix-normalize-time
DibiResult: fixed normalization of time when begins by 00:
2013-08-11 11:59:28 -07:00
Miloslav Hůla
cf942c68ce DibiResult: fixed normalization of time when begins by 00: 2013-07-15 15:22:04 +02:00
David Grudl
e87c112d71 typos & whitespace 2013-07-13 20:23:13 +02:00
David Grudl
9e23730cb0 added examples to readme.md 2013-07-01 12:38:29 +02:00
David Grudl
a388767848 added .travis.yml & other files 2013-06-23 03:36:01 +02:00
David Grudl
bc59eb6a14 updated examples, SQlite3 is used instead of SQLite2 2013-06-23 02:34:15 +02:00
David Grudl
44aba8a986 opened 2.2-dev 2013-06-23 02:34:14 +02:00
David Grudl
dc4fe8b398 Released version 2.1.0 2013-06-23 02:34:14 +02:00
David Grudl
de7bf09eca improved readme & license 2013-06-23 02:34:14 +02:00
David Grudl
aac5ae9932 typos 2013-06-23 02:34:13 +02:00
David Grudl
d055eefb9a Revert "updated Nette\Debugger"
This reverts commit c08918e034.
2013-06-23 02:12:51 +02:00
David Grudl
93d996ef6d DibiNettePanel is compatible with Nette Framework 2.1 [Closes #97] 2013-06-23 01:56:08 +02:00
David Grudl
e5b2ca46d8 added extension for Nette Framework 2.1 (and examples) [Closes #93] 2013-06-23 01:56:07 +02:00
David Grudl
c305b8b674 Nette extension renamed to DibiNette20Extension and moved to bridges/Nette 2013-06-22 18:29:36 +02:00
David Grudl
1382a1021f all file names correspond with class name 2013-06-22 18:29:35 +02:00
David Grudl
d01ede6aee DibiDatabaseInfo: updated patterns in detectType() [Closes #95] 2013-06-22 16:39:54 +02:00
David Grudl
b94b97873a Merge pull request #96 from jasir/firebird-getrowcount
Firebird driver: getRowCount throws NotSupportedException
2013-06-12 05:05:46 -07:00
jasir
849b9b66fa Firebird driver: getRowCount throws NotSupportedException 2013-06-11 17:56:52 +02:00
David Grudl
41c150932c Merge pull request #92 from hrach/patch-1
Fixed toggling explain for latest Nette Framework
2013-05-24 02:10:42 -07:00
Jan Škrášek
981c78a078 Fixed toggling explain for latest Nette Framework 2013-05-24 01:42:38 +02:00
David Grudl
cbdcc3f832 Merge pull request #85 from foglcz/bug-84
Throw entire callstack for interbase-based databases.
2013-04-11 02:39:31 -07:00
David Grudl
593aa0351a Merge pull request #83 from milo/sqlsrv-fixes
Sqlsrv fixes
2013-04-11 02:37:46 -07:00
Pavel Ptacek
9b4d58878c Throw entire callstack for interbase-based databases. The errors given by firebird are multiline, exceptions get thrown from inside the logic. (.*) matches everything up until first line by default - this mitigates the problem and mathes the entire rest of the string (= including multilines) 2013-04-11 04:51:09 +02:00
Miloslav Hůla
0ebe7ad84f DibiMsSql2005Driver: fixed identifier escaping
Annoted link to MS SQL doc does not talk about open bracket '[' escaping, only closing bracket ']'.

dibi::query('CREATE TABLE %n (id INT)', 'abc[]def');
 - old: creates table 'abc[[]def'
 - new: creates table 'abc[]def'
2013-04-10 12:08:11 +02:00
Miloslav Hůla
4ea885f2b9 DibiPdoDriver: added identifier escaping for sqlsrv 2013-04-10 12:08:11 +02:00
Miloslav Hůla
3634673ffa DibiPdoDriver: applyLimit() for sqlsrv driver
Added table alias has been tested with MS SQL Server 2012 directly and over ODBC.
2013-04-10 12:08:10 +02:00
Miloslav Hůla
8e5898a11b DibiMsSql2005Driver: default charset set to UTF-8
If charset is not specified in config, NULL is passed as connection option and 'Invalid value type for option CharacterSet was specified.  String type was expected.' is thrown.
2013-04-10 12:07:27 +02:00
David Grudl
2c9cbe9b0c DibiResult: fixed detection of "123.000" as float [Closes #67] 2013-04-03 14:38:12 +02:00
David Grudl
326376159f composer: renamed to dibi/dibi 2013-04-03 14:36:18 +02:00
David Grudl
d09fc7d837 MySQLI: mysqli_affected_rows() returns -1 on error [Closes #80] 2013-04-03 13:57:48 +02:00
Teyras
388067cb5d DibiResult: added setRowFactory() [Closes #26] 2013-04-02 00:51:57 +02:00
David Grudl
8a07e1fbe4 renamed directory tools -> vendor 2013-03-15 05:14:09 +01:00
David Grudl
ff675e32d1 Merge pull request #81 from HosipLan/bugfix/notice
PdoDriver: fix notice undefined index native_type
2013-03-14 05:59:34 -07:00
Filip Procházka
cbca529ff8 PdoDriver: fix notice undefined index native_type 2013-03-01 17:31:21 +01:00
Daniel Kouba
9001afe7ca MSSQL2005 driver: autoincrement determination fixed 2012-12-30 02:56:21 +01:00
Daniel Kouba
16b254fce1 MSSQL2005 applyLimit fixed
Conflicts:
	dibi/drivers/mssql2005.php
	dibi/drivers/mssql2005.reflector.php
2012-12-16 23:42:09 +01:00
David Grudl
10fdecfc13 updated examples 2012-12-16 23:39:32 +01:00
David Grudl
322a110f75 Merge pull request #75 from whipsterCZ/patch-3
Column size is string or NULL
2012-12-16 14:37:50 -08:00
Daniel Kouba
188acf1f76 Column size is string or NULL
Issue #74
2012-12-11 15:09:21 +01:00
David Grudl
8cda1401ff Merge pull request #70 from DragonJake/cli-enhancements
DibiResult::dump() a obarvování v CLI módu
2012-12-04 05:50:44 -08:00
David Grudl
9a605573a2 released 2.0.1 2012-12-04 14:40:06 +01:00
David Grudl
d09e490f1b reflectors: table names are correctly escaped 2012-12-04 14:35:53 +01:00
David Grudl
865e44c30f DibiTranslator: number of decimal points changed to 10 2012-12-04 14:22:50 +01:00
David Grudl
c08918e034 updated Nette\Debugger 2012-12-04 14:15:32 +01:00
David Grudl
c385888f9a dibi::$sql is always set 2012-12-04 14:15:31 +01:00
David Grudl
6c82e777e1 added Nette\Tester 2012-12-04 14:15:31 +01:00
David Grudl
b4de8daed3 fixed invalid escaping sequences in double quoted strings, used \z instead of $ 2012-12-04 14:15:31 +01:00
Daniel Kouba
7696fc36e1 Added MSSQL 2005 Reflector 2012-12-04 14:15:26 +01:00
Jakub Krčma
627c102add dibi: colorized SQL dump in CLI mode 2012-10-03 23:08:10 +02:00
Jakub Krčma
6ced2d3af2 DibiResult: added simple dump in CLI mode 2012-10-03 23:06:54 +02:00
Jakub Krčma
927fc858d2 MySQL & MySQLi: added timezone configuration option 2012-10-03 21:13:03 +02:00
Jirka Chadima
f355b8ede7 Allow forcing a persistent connection via driver configuration 2012-08-28 15:38:12 +02:00
David Grudl
dfda0b4d96 DibiConnection: fixed loadFromFile() and loading file without semicolon [Closes #63] 2012-04-05 20:08:51 +02:00
Jan Marek
c1537c1d36 added composer.json 2012-03-30 22:15:57 +02:00
David Grudl
973c2452a7 DibiTranslator: number of decimal points changed to 20 2012-02-29 00:08:31 +01:00
David Grudl
38742aee57 Merge pull request #61 from milo/fix-fire-logger
DibiFirePhpLogger: Fix undefined property access
2012-02-12 14:32:15 -08:00
Miloslav Hůla
b2744174f7 DibiFirePhpLogger: Fix undefined property access 2012-02-11 17:36:15 +01:00
David Grudl
9b7b964696 numOfQueries and totalTime moved to profilers 2012-02-06 18:23:15 +01:00
David Grudl
06642ec9ee Merge pull request #56 from Andrewsville/multiple-connections-nettepanel-fix
Fixed NettePanel output for multiple connections
2012-02-06 09:04:27 -08:00
David Grudl
a2ad4faf49 Merge pull request #58 from milo/fix-mssql
Fix DibiMsSql2005Driver::getResultColumns()
2012-02-06 07:25:27 -08:00
Miloslav Hůla
4be92b62f8 typos 2012-02-06 13:55:40 +01:00
Miloslav Hůla
52986705d8 Fix DibiMsSql2005Driver::getResultColumns() 2012-02-06 13:43:17 +01:00
Ondrej Nespor
0c51f6c9ea Fixed NettePanel output for multiple connections 2012-02-05 01:25:03 +01:00
David Grudl
7c47f57e30 released 2.0 stable 2012-02-03 13:48:14 +01:00
David Grudl
0327573085 updated Nette Debugger 2012-02-03 13:48:14 +01:00
David Grudl
cf2e08c20f DibiNettePanel: fixed doubled alias 2012-02-01 21:50:35 +01:00
David Grudl
9b7a80e894 DibiConnection ensures class dibi is loaded 2012-02-01 19:18:24 +01:00
Miloslav Hůla
2ae6535899 Consider value of %n array as identifier 2012-02-01 19:18:23 +01:00
Miloslav Hůla
0b2cb3c6e4 Detection of PostgreSQL array types 2012-01-21 16:58:57 +01:00
David Grudl
0723e8ffc8 typos 2012-01-21 16:58:57 +01:00
David Grudl
5b68657842 DibiResult: row-class can be disabled and fetch() returns array 2012-01-21 16:58:57 +01:00
David Grudl
3cde2c7815 DibiTranslator: supported key%~like~ modifier in array keys 2012-01-21 16:58:57 +01:00
David Grudl
5aed9e7e4f MSSQL2005: used native functions for transaction 2012-01-21 16:58:56 +01:00
David Grudl
c2c63615fd DibiFluent: added setupResult() 2012-01-21 16:58:56 +01:00
David Grudl
e4ed284e77 DibiConnection: added config option "result|formatDate" 2012-01-21 16:58:56 +01:00
David Grudl
0f21b6ca5b DibiEvent: (SELECT ...) UNION (SELECT ...) is classified as SELECT [Closes #35] 2012-01-21 16:58:56 +01:00
David Grudl
b87d3cab81 DibiConnection::test() shows complete error message 2012-01-21 16:58:55 +01:00
David Grudl
3f2e383b47 __toString() do not throw an exception [Closes #19] 2012-01-21 16:58:55 +01:00
David Grudl
bf49cec002 DibiNettePanel: Oracle explains using EXPLAIN PLAN 2012-01-21 16:58:55 +01:00
David Grudl
70fd2368aa DibiNettePanel: explain SQL command can be altered 2012-01-21 16:57:33 +01:00
David Grudl
5d41128752 calling getResultResource() disables auto-free in __destruct() 2012-01-19 04:23:00 +01:00
David Grudl
5efb11d780 DibiMysqlDriver & DibiPostgreSql: escape() checks if resource is alive [Closes #46] 2012-01-19 04:21:09 +01:00
David Grudl
660a9db522 drivers: getResource() and getResultResource() checks if resource is alive 2012-01-19 04:20:57 +01:00
David Grudl
c7b9a91fc2 DibiResult: added getResultDriver() 2012-01-19 00:14:07 +01:00
David Grudl
ea0d6d02ba DibiResult: added setFormat() 2012-01-18 21:15:22 +01:00
David Grudl
b964887f9d released 1.5-rc2 2012-01-12 15:25:19 +01:00
David Grudl
a8f5e09b69 DibiDateTime: added __toString(), modify(), modifyClone() 2012-01-12 15:25:04 +01:00
David Grudl
8c481bc128 DibiRow: asBool(), asDate() and asTimestamp() deprecated 2012-01-12 01:19:15 +01:00
David Grudl
4c85d1d55c DibiNettePanel: works with disabled bar [Closes #43] 2012-01-12 01:06:15 +01:00
David Grudl
2a5934c385 removed IDibiVariable & DibiVariable, replaced with DibiLiteral 2012-01-12 00:46:50 +01:00
David Grudl
a8672bb6ff DibiFluent passed as argument to DibiFluent is converted to string [Closes #44] 2012-01-12 00:46:49 +01:00
Steven Bredenberg
9bc53109a0 Adding reflector code for MSSQL. 2012-01-12 00:16:22 +01:00
Radek Dostál
3288b38b6c Implemented escapeLike() for Oracle driver 2012-01-12 00:06:39 +01:00
David Grudl
8c8b3c3dc1 dibi: new keywords in dump() 2012-01-12 00:03:33 +01:00
David Grudl
2c1950eacf DibiDatabaseInfo::detectTypes: adjusted YEAR, BIGINT 2012-01-12 00:03:33 +01:00
David Grudl
baf08d3c71 DibiConnection: types detection + normalization is always TRUE 2012-01-12 00:03:32 +01:00
David Grudl
e60dc2d2d0 Merge pull request #48 from mil0/master
Implemented getForeignKeys() for PostgreSQL driver
2012-01-11 13:31:15 -08:00
David Grudl
eee105f607 class DibiNettePanel is included in dibi.php [Closes #49] 2012-01-11 00:18:37 +01:00
Miloslav Hůla
c65294075e Merge remote-tracking branch 'upstream/master' 2012-01-08 20:03:30 +01:00
David Grudl
1d63af75f9 Nette: getContainer() renamed to getContainerBuilder() 2012-01-06 04:02:43 +01:00
David Grudl
9be3bd7a53 fix: DibiNettePanel is called only if required interface is available 2012-01-04 18:50:42 +01:00
Miloslav Hůla
96d7236a4a Implemented getForeignKeys() for PostgreSQL driver 2012-01-03 13:57:37 +01:00
David Grudl
9ba0cf62d1 All exceptions are DibiException descendants 2012-01-03 05:46:59 +01:00
David Grudl
d1a63ce757 FireBird driver: implemented getResultColumns() 2012-01-03 05:34:56 +01:00
David Grudl
0a17cc50ae updated Nette\Debugger 2012-01-03 05:15:44 +01:00
David Grudl
74a139974c DibiTranslator: fixed usage of modifier ? 2012-01-03 05:14:44 +01:00
David Grudl
dbb72b769b updated phpDoc @package 2012-01-03 04:50:11 +01:00
David Grudl
dd07d50434 added DibiNetteExtension 2012-01-03 04:34:01 +01:00
David Grudl
53c2ef55d8 changed profiler API; IDibiProfiler replaced with DibiConnection::$onEvent; DibiProfiler split to DibiFileLogger, DibiFirePhpLogger and DibiNettePanel (BC break!) 2012-01-03 04:34:01 +01:00
David Grudl
1d4e86fe2b Updated copyright notices for 2012 2012-01-03 04:17:30 +01:00
David Grudl
183a215fc5 updated DibiProfiler CSS 2011-07-01 08:19:16 +02:00
David Grudl
9c59fee929 updated examples & Debugger 2011-07-01 08:19:15 +02:00
David Grudl
ac1f45b397 updated for Nette 2.0 beta: exceptions 2011-07-01 08:19:15 +02:00
David Grudl
508e8638e8 Merge pull request #34 from mil0/master.
implemented escapeLike() for PostgreSQL driver
2011-05-02 15:35:20 -07:00
David Grudl
0de947883b updated for Nette 2.0 beta 2011-04-21 14:53:22 +02:00
Miloslav Hůla
b979295baa implemented escapeLike() for PostgreSQL driver 2011-03-10 14:33:34 +01:00
David Grudl
8a899c7ddb DibiProfiler logs source files and lines 2011-02-23 02:16:07 +01:00
Lukáš Gavenda
1a4ea39a60 DibiProfiler: iterator fix 2011-02-23 02:12:55 +01:00
David Grudl
acda14ca64 DibiProfiler shows source files and lines 2011-02-17 22:19:06 +01:00
David Grudl
f09e4b9b3e updated Nette\Debug 2011-02-17 22:18:30 +01:00
David Grudl
1103714b9f typos 2011-02-16 19:38:05 +01:00
David Grudl
e37af6f99d added IDibiVariable 2011-02-16 18:01:22 +01:00
David Grudl
b148291ca2 DibiDataSource: removed keyword 'AS' 2011-02-16 17:48:47 +01:00
David Grudl
713918ae3c typos 2011-02-06 15:48:44 +01:00
David Grudl
617cb42e8e fixed PHP bug #53915 2011-02-03 04:13:01 +01:00
David Grudl
4ea5ddae8e DibiResult::convert() correctly handles too big INT [Closes #18] 2011-02-02 22:49:52 +01:00
David Grudl
60b62c50fe MySQLi driver: added support for persistent connection [Closes #27] 2011-02-02 22:39:52 +01:00
David Grudl
568ec80994 DibiProfiler: EXPLAIN is executed at shutdown [Closes #17] 2011-02-02 22:34:13 +01:00
David Grudl
23e3ba6db4 PDO driver: getReflector() is implemented for MySQL and SQLite 2011-02-02 20:36:54 +01:00
David Grudl
bdb5b217c7 License changed to the New BSD License or the GNU General Public License (GPL) version 2 or 3. 2011-02-02 01:20:30 +01:00
David Grudl
faf444567b released 1.5-rc1 2011-01-25 18:26:51 +01:00
David Grudl
018b9da06d used NotSupportedException at appropriate places 2011-01-25 18:23:30 +01:00
David Grudl
9c52b8ea9d renamed DibiLazyStorage to DibiHashMap 2011-01-25 18:00:29 +01:00
David Grudl
1ada6fab97 removed DibiConnection::sql() 2011-01-25 17:50:50 +01:00
David Grudl
0dc9db1d77 some stuff is deprecated and throws E_USER_WARNING: dibi::datetime(), dibi::date(), dibi::addSubst(), dibi::removeSubst(), dibi::setSubstFallback(), DibiResult::rowCount(), DibiResult::getColumnNames(), DibiVariable 2011-01-25 17:49:37 +01:00
David Grudl
08099816d5 DibiDatabaseInfo: removed support for substitutions 2011-01-25 17:42:13 +01:00
David Grudl
26de1aebc0 Substitution moved from class dibi to DibiConnection 2011-01-25 17:41:44 +01:00
David Grudl
88cccc0543 DibiConnection refactoring 2011-01-25 17:37:49 +01:00
David Grudl
0b1547818d DibiDateTime::__construct accepts unix timestamp 2011-01-25 17:37:35 +01:00
David Grudl
e0d110962e MSSQL2005 driver: added alias charset -> options|CharacterSet 2011-01-25 17:37:35 +01:00
David Grudl
ae00fed718 fixed regexp for profiler 2011-01-24 15:20:50 +01:00
Filip Procházka
af715a8044 fixed sql injection vulnerability through conditions and comments [closes #30] 2011-01-24 22:01:51 +08:00
David Grudl
af6352d0af removed czech license 2010-11-10 00:55:22 +01:00
David Grudl
14fee47d54 DibiProfiler: added HTML title for Debug bar 2010-11-07 23:06:21 +01:00
David Grudl
ec82eda864 DibiResult destructor moved to individual IDibiResultDriver drivers 2010-11-02 14:42:15 +01:00
David Grudl
087734fb23 drivers implementing IDibiDriver and IDibiResultDriver together do not use $resultSet in IDibiDriver part 2010-11-02 14:42:14 +01:00
David Grudl
32baabdeac added low-level methods createResultDriver() and DibiConnection::createResultSet() 2010-11-02 13:59:05 +01:00
David Grudl
33ef22b488 fixed compatibility with Nette / RobotLoader 2010-11-02 03:29:19 +01:00
David Grudl
36fe9d42c1 PcreException renamed to DibiPcreException 2010-10-25 16:16:38 +02:00
David Grudl
0c4f343238 Firebird driver: added missing getReflector() 2010-10-06 16:17:29 +02:00
David Grudl
8d6639fa3c DateTime53 renamed to DibiDateTime 2010-10-06 16:16:09 +02:00
David Grudl
90592929ec fixed Nette compatibility [Closes #23] 2010-09-28 17:29:02 +02:00
David Grudl
c052582670 DibiProfiler: the file can be specified in a configuration 2010-09-15 13:25:46 +02:00
David Grudl
e6e7babe22 simplified phpDoc comments 2010-09-14 19:09:56 +02:00
David Grudl
056a680cff DibiProfiler & Debug: compatible with XHTML 2010-09-07 04:05:41 +02:00
David Grudl
6d94afdddd added dibi::setConnection() 2010-09-07 00:16:45 +02:00
David Grudl
b1156e54d8 drivers meta function refactoring 2010-08-27 02:18:07 +02:00
David Grudl
39add9b8a3 dibi::dump() fixed in CLI mode 2010-08-27 01:06:05 +02:00
David Grudl
b8e518f44b implemented escapeLike() and modifiers %~like, %like~, %~like~ 2010-08-27 01:00:53 +02:00
David Grudl
46a3b8a42c now compatible with Debug / NDebug / Nette\Debug 2010-08-25 07:21:49 +02:00
David Grudl
310d43f404 DibiTranslator: temporary removed delimite() cache [Closes #21] 2010-08-25 01:27:05 +02:00
David Grudl
a64c2270d8 Revert "DibiResult: removed destructor"
This reverts commit 6d353c0b5d.
2010-08-25 01:27:05 +02:00
David Grudl
16dd0c7230 dibi::$substs & fall-back provided via DibiLazyStorage 2010-08-25 01:27:04 +02:00
David Grudl
79735e96d1 DibiLazyStorage: allows empty string as property name for reading 2010-08-25 01:27:04 +02:00
David Grudl
325305326b missing substitutions no longer throw exception 2010-08-25 01:27:04 +02:00
David Grudl
ff3ca38504 typos 2010-08-25 01:27:04 +02:00
David Grudl
f651d9f473 Revert "empty substitutions are going to be deprecated" commit e50b1a0b5a. 2010-08-25 01:27:04 +02:00
David Grudl
3568e041c5 MSSQL: delimited name in mssql_select_db 2010-08-25 01:27:04 +02:00
David Grudl
6430573d61 MySQL & MySQLi, SQLite & SQLite3 reflectors moved do external classes DibiMySqlReflector and DibiSqliteReflector 2010-08-25 01:27:04 +02:00
David Grudl
65b5e2eecd added IDibiDriver::getReflector() 2010-08-05 21:03:08 +02:00
David Grudl
9f982cb310 MySQL, MySQLi & PostgreSql drivers: default character set is 'utf8' (BC break) 2010-08-04 15:36:10 +02:00
David Grudl
76783b3872 DibiDatabaseInfo: better IDibiReflector vs. IDibiResultDriver handling 2010-08-04 15:34:49 +02:00
David Grudl
de85d3814e IDibiDriver splitted into IDibiDriver & IDibiResultDriver 2010-08-04 15:34:42 +02:00
David Grudl
5cce595518 DibiConnection::sql() renamed to verb translate() 2010-08-04 12:10:29 +02:00
David Grudl
746a553419 IDibiDriver::getColumnsMeta() renamed to getResultColumns() (BC break!) 2010-08-03 23:33:12 +02:00
David Grudl
0d7b9c32c9 updated phpDoc 2010-08-03 23:27:53 +02:00
David Grudl
9a4f1e6e36 DibiFluent: allowed multiple ->from('table') 2010-08-03 21:54:01 +02:00
David Grudl
204c4cdbd7 DibiConnection: fixed processing of profiler configuration 2010-08-03 21:29:12 +02:00
David Grudl
6b166afffb SQLite & SQLite3: improved primary key detection for ROWID 2010-08-03 21:28:06 +02:00
David Grudl
f3c2c27818 examples: added new 2010-08-03 21:06:56 +02:00
David Grudl
dc62e87629 PostgreSQL: implemented inTransaction() 2010-08-03 20:20:37 +02:00
David Grudl
66e709e846 MySQL & MySQLi drivers: configuration items 'options' renamed to 'flags' (old name is alias); added array 'options' for MySQLi 2010-08-03 19:49:22 +02:00
David Grudl
dc3b1ff399 DibiConnection::getDriver() automatically connects lazy connection; connect() and disconnect() are imperative now! 2010-08-03 18:11:21 +02:00
David Grudl
6bb2bc489d DibiTranslator: removed getDriver() (it is private) 2010-08-03 18:11:21 +02:00
David Grudl
019f6864cf DibiResult::getIterator() - removed optional $offset and $limit parameters (BC break!) 2010-08-03 18:11:20 +02:00
David Grudl
792a25d57b added tests 2010-08-03 17:18:11 +02:00
David Grudl
759b8d4615 removed directory "icon" 2010-08-03 17:18:10 +02:00
David Grudl
3f594756e0 examples: removed unnecessary delimiters [] 2010-08-03 17:18:10 +02:00
David Grudl
beee4ee4b7 examples: files renamed 2010-08-03 17:18:10 +02:00
David Grudl
8f9435b518 examples: better headings and comments 2010-08-03 17:18:02 +02:00
David Grudl
65dba03652 examples: added CSS style 2010-08-03 13:41:06 +02:00
David Grudl
6e35a783c7 examples: data files moved to directory "data" 2010-08-03 13:41:05 +02:00
David Grudl
914f7d3c26 DibiLazyStorage: empty string as property name throws exception; fixed in DibiDatabaseInfo too 2010-08-03 13:41:05 +02:00
David Grudl
e50b1a0b5a empty substitutions are going to be deprecated 2010-08-03 08:20:50 +02:00
David Grudl
8f358ca73d dibi::dump() turns off HTML tags in CLI mode 2010-08-03 08:19:58 +02:00
David Grudl
9d27c7cc4e DibiFluent: speed optimizations 2010-08-03 08:19:57 +02:00
David Grudl
bcee7bba91 DibiFluent::__class() small refactoring 2010-08-03 08:19:57 +02:00
David Grudl
8c5dc153f2 DibiDatabaseInfo: uses DibiLazyStorage for column type detecting 2010-08-03 08:19:34 +02:00
David Grudl
c93137340e DibiTranslator: uses DibiLazyStorage as caching layer over delimite() 2010-08-03 08:19:00 +02:00
David Grudl
84e0f0ecc1 added DibiLazyStorage, simple caching layer 2010-08-03 08:18:42 +02:00
David Grudl
3b87d71a68 MySQLi: fixed columns types detection [Closes #6] 2010-08-03 04:04:59 +02:00
David Grudl
df02cf1e3d dibi::dump highlights keyword OFFSET [Closes #15] 2010-08-03 04:04:58 +02:00
PetrP
e8990c9be6 DibiProfiler: explain query doesn't overwrite dibi::$sql 2010-08-03 04:04:58 +02:00
PetrP
e8de6f21c9 Mysql & Mysqli drivers: fixed bug in detection unsigned columns 2010-08-03 04:04:58 +02:00
PetrP
8082489143 DibiConnection: connect() is public 2010-08-03 04:04:57 +02:00
David Grudl
63163de18b DibiDatabaseInfo: BIGINT is treated as string [Closes #18] 2010-08-03 04:04:53 +02:00
David Grudl
1b623855b7 typos 2010-08-03 04:04:47 +02:00
David Grudl
6288dc8cba DibiConnection: removed old deprecated methods 2010-08-03 01:35:23 +02:00
David Grudl
20d0163316 DibiTranslator: speed optimizations 2010-08-03 01:32:00 +02:00
David Grudl
97da612604 DibiConnection: driver name is case insensitive 2010-08-03 00:45:52 +02:00
David Grudl
2ed67c1944 DibiConnection: uses single DibiTranslator object (per-connection) 2010-08-03 00:44:59 +02:00
David Grudl
a0a12701e9 DibiPdoDriver: speed optimization 2010-08-03 00:42:50 +02:00
David Grudl
739994dac6 MySQLi driver: sets mysqli_report(MYSQLI_REPORT_OFF) 2010-07-21 00:15:16 +02:00
David Grudl
8c99f0c04d Driver's reflection capabilities moved to IDibiReflector 2010-05-26 16:26:21 +02:00
David Grudl
7bac2ef3b3 typos, changed nettephp.com -> nette.org 2010-05-25 02:48:51 +02:00
David Grudl
651c0f8c4a DibiProfiler is configurable via DibiConnection $config 2010-05-19 20:48:35 +02:00
David Grudl
88b1a45e42 DibiResult is configured via items 'detectTypes' and 'formatDateTime' in 'result' subarray; removed RESULT_DETECT_TYPES & RESULT_DATE_TIME 2010-05-19 20:25:17 +02:00
David Grudl
9d803869fa updated Nette\Debug 2010-05-19 19:42:04 +02:00
David Grudl
d19afd5790 Removed inTransaction definitely (BC break!) 2010-05-19 18:33:28 +02:00
David Grudl
8b4cd4e689 DibiTranslator: %f modifier converts value to number 2010-05-19 17:14:12 +02:00
David Grudl
286cd7bacd DibiFluent: implemented clause auto-switch for 'JOIN', 'INNER JOIN', 'LEFT JOIN' 2010-05-19 16:38:51 +02:00
David Grudl
1285d9b30a DibiMsSqlDriver: exception message received from mssql_get_last_message() 2010-05-19 15:54:49 +02:00
David Grudl
0748c693ff DibiMsSql2005Driver: added config aliases 'username', 'password', 'database' 2010-05-19 15:40:32 +02:00
David Grudl
550c477797 DibiConnection::alias() refactoring 2010-05-19 15:33:37 +02:00
David Grudl
27d58bff40 DibiDatabaseInfo: provides substitutions 2010-05-19 15:03:48 +02:00
David Grudl
bec559448c rewritten dibi::IDENTIFIER escaping; added support for [table.*] 2010-05-19 14:59:03 +02:00
David Grudl
27930611de REGEXP optimizations 2010-05-16 22:45:55 +02:00
David Grudl
553f7da5f9 implemented PCRE error checking and PcreException 2010-05-16 22:12:19 +02:00
David Grudl
555e825bb2 DibiResult: fixed compatibility with new DibiRow 2010-04-26 21:20:10 +02:00
David Grudl
5d95f0ba0d added DibiRow::toArray() & Countable 2010-04-26 21:18:46 +02:00
David Grudl
26384626ba ArrayObject -> Traversable & iterator_to_array 2010-04-26 20:43:04 +02:00
David Grudl
f048cc4086 added @method phpDoc 2010-04-23 14:55:29 +02:00
David Grudl
784153e98c DibiRow is not longer ArrayObject descendant 2010-04-22 23:19:33 +02:00
David Grudl
a4c5f327de DibiConnection: $config can be Traversable 2010-04-22 22:14:46 +02:00
David Grudl
90d61002fb added @property phpDoc 2010-04-22 12:12:11 +02:00
Roman Sklenar
ea5e1f052f SQLite driver: implemented database reflection 2010-04-22 17:41:28 +08:00
Roman Sklenar
7412f8bf65 DibiPdoDriver::getColumnsMeta PHP < 5.2.3 compatibility 2010-04-22 17:41:28 +08:00
Jakub Vrana
35b8a4f000 Fix MySQL implementation of inTransaction() 2010-04-22 17:41:04 +08:00
Ondřej Mirtes
e8517b43dc PostgreSQL driver now allows 'database' key in config array instead of 'dbname' 2010-04-22 17:32:36 +08:00
paranoiq
7504a51451 enabled 'unsigned' option on mysql and mysqli column info 2010-04-22 17:32:24 +08:00
David Grudl
550be3b10a Debug Bar fix 2010-04-06 12:58:54 +02:00
David Grudl
52e5d43416 DibiProfiler: added [explain] info 2010-04-04 05:58:57 +02:00
David Grudl
57b68131e6 DibiProfiler: $tickets is now static, profiler sets dibi static variables 2010-04-04 00:08:14 +02:00
David Grudl
52f8128a2f profiler fix 2010-04-03 23:55:16 +02:00
David Grudl
3f6a075251 updated for new Nette Debug Bar 2010-04-01 05:30:32 +02:00
David Grudl
22d41ca3c1 DibiProfiler: json_encode in native in PHP 5.2 2010-03-29 15:46:05 +02:00
David Grudl
0b9562497f typos 2010-02-24 07:50:50 +01:00
David Grudl
dbe79ae57b DibiResult: convert() is protected 2010-02-24 06:51:21 +01:00
David Grudl
92ea9f2c5a DibiResult: default type for datetime changed from timestamp -> object DateTime (BC break) 2010-02-24 06:49:05 +01:00
David Grudl
c01bfd792d added new option 'resultDateTime' - replaces parameter $format in DibiResult::setType() 2010-02-24 06:43:08 +01:00
David Grudl
527863fcce added new option 'resultDetectTypes' - calls automatically detectTypes() 2010-02-24 06:23:55 +01:00
David Grudl
4054bdc231 DibiResult: removed setWithTables & 'resultWithTables' configuration option (BC break!) 2010-02-24 04:59:00 +01:00
David Grudl
a2323271d4 DibiResult: small refactoring 2010-02-24 02:43:15 +01:00
David Grudl
576397319a DibiDataSource: better table name detection in __construct 2010-02-16 19:19:32 +01:00
David Grudl
ec1a1ec5b9 DibiRow::asDateTime() added $format parameter 2010-02-15 21:20:19 +01:00
David Grudl
55cd98e9c1 DibiFluent: fixed support for cloning 2010-01-26 19:55:00 +01:00
David Grudl
a8e83ce93d DibiRow: added asTimestamp() & asDateTime(), therefore asDate() is deprecated 2010-01-26 01:38:47 +01:00
David Grudl
e232bf470c DibiFluent: clause() argument can be in upper-case 2010-01-24 18:58:05 +01:00
David Grudl
65965b0943 DibiFluent: added support for cloning 2010-01-24 18:57:04 +01:00
David Grudl
e035c13437 DibiPostgreDriver: getTables() returns always array 2010-01-23 08:00:52 +01:00
David Grudl
b8600ee704 added DibiFluent::REMOVE const 2010-01-23 06:28:23 +01:00
David Grudl
2395b83b03 DibiTranslator: added modifiers %sN & %iN, replacements for %sn & %in 2010-01-23 06:02:03 +01:00
David Grudl
cb1ed71669 DibiPdoDriver: added rowCount() 2010-01-23 05:39:28 +01:00
David Grudl
fa0b146f67 DibiTranslator: empty arrays DO NOT generate NULL (BC break!) & added array modifier %in 2010-01-23 05:25:17 +01:00
David Grudl
bb40e28eb8 DibiTranslator: %by supports inner arrays 2010-01-23 04:57:49 +01:00
David Grudl
1ecc4ab134 DibiMySqlDriver & MySQLi: sets time_zone in connect() 2010-01-23 04:45:59 +01:00
David Grudl
76c02a65da DibiFluent: added removeClause() 2010-01-23 04:43:36 +01:00
David Grudl
b9ec3047b0 small code refactoring 2010-01-15 22:27:06 +01:00
David Grudl
e177436e38 added DateTime53, fix for crappy DateTime in PHP 5.2 2010-01-14 23:41:37 +01:00
David Grudl
9d4a887e7a typos 2010-01-11 17:03:57 +01:00
David Grudl
d61bd79982 DibiConnection: inTransaction() is back, implemented in drivers 2010-01-11 16:58:37 +01:00
David Grudl
c37475838f - year 2009 -> 2010 2010-01-03 15:32:26 +01:00
David Grudl
7688dd81f9 DibSQLite3Driver: improved by Roman Sklenar 2009-12-18 01:46:51 +01:00
Marek Jelen
686b60fb04 DibiOracleDriver: implemented getInsertId() 2009-12-18 01:44:35 +01:00
David Grudl
ccd20b6f17 added SQLite3 driver 2009-12-18 01:43:50 +01:00
David Grudl
20d22dd81b DibiConnection: deprecated inTransaction (BC break!) 2009-12-17 23:53:45 +01:00
David Grudl
f6d4107116 DibiResult: fixed bug in new-syntax fetchAssoc() 2009-12-09 02:34:58 +01:00
David Grudl
75ede18f94 removed PHP 5.1 support; removed DibiVariable & IDibiVariable 2009-11-26 05:38:20 +01:00
David Grudl
8586eb8e29 changed empty-date detection 2009-11-16 02:00:49 +01:00
David Grudl
5e7774404b code smoothing 2009-11-16 02:00:49 +01:00
Jan Vlcek
4b2a329127 DibiDataSource: removed the final keyword of the __toString() method. 2009-11-03 02:03:05 +08:00
Jan Vlcek
22b62ee9a5 MSSQL driver: Added subselect alias in applyLimit. 2009-11-03 02:03:04 +08:00
David Grudl
b00257c6b0 Updated DibiFirebirdDriver & DibiMsSql2005Driver 2009-10-13 17:02:26 +02:00
David Grudl
4235b3564f DibiResult: rewritten associate descriptor syntax 2009-10-06 18:09:13 +02:00
David Grudl
0337cbdba9 DibiResult: fetchAssoc refactoring 2009-10-06 16:51:27 +02:00
David Grudl
d68e526381 updated version number 2009-09-30 22:50:23 +02:00
David Grudl
3b2ca19d42 Released version 1.2 2009-09-18 07:26:02 +02:00
David Grudl
16f6d537e0 All setters provide a fluent interface now (i.e. return $this) 2009-09-18 03:38:10 +02:00
David Grudl
6d353c0b5d DibiResult: removed destructor 2009-09-09 17:04:20 +02:00
David Grudl
698c12eadc DibiColumnInfo: getType() is implemented lazy 2009-09-09 17:03:40 +02:00
David Grudl
70ba2f065c added DibiResultInfo, available via DibiResult::getInfo() 2009-09-09 17:02:46 +02:00
David Grudl
7e539f8f4f typo changes 2009-09-09 17:01:30 +02:00
David Grudl
532ed3a316 MySQL drivers: getColumns() obtains comments 2009-09-08 21:27:57 +02:00
David Grudl
08e70fda61 DibiTranslator: fixed bug in DateTime object usage 2009-08-26 00:09:05 +02:00
David Grudl
217de42d20 DibiPostgreDriver: added user => username alias (thanks to Milan Matejcek) 2009-08-21 13:48:16 +02:00
David Grudl
fa6d771813 dibi internally uses DateTime object in PHP 5.2 2009-08-21 01:34:07 +02:00
David Grudl
3777bacc02 DibiOracleDriver: supports configuration options 'fmtDate' & 'fmtDateTime' 2009-08-20 22:19:01 +02:00
David Grudl
9850a2f78b opened 1.2-dev 2009-08-17 15:58:08 +02:00
David Grudl
031bd2b767 released dibi 1.1 2009-08-17 15:51:36 +02:00
David Grudl
7c6932807d updated for GIT 2009-08-14 00:05:20 +02:00
David Grudl
208a579d6f - last rev fix 2009-08-13 12:33:28 +00:00
David Grudl
eeba6cfc91 - DibiConnection: fixed insert() with modifiers (thanks to Matej Kravjar) 2009-08-13 12:20:03 +00:00
David Grudl
8cc1d8b948 - MS drivers: bool TRUE is now +1
- DibiDatabaseInfo: LONGTEXT is recognized as TEXT
2009-08-13 12:08:31 +00:00
David Grudl
c2a2a4cd18 - added netterobots.txt 2009-08-12 02:28:55 +00:00
David Grudl
5e3bc8d0e9 - added driver for Firebird/InterBase database (thanks to authors Tomáš Kraina, Roman Sklenář) 2009-07-23 10:31:07 +00:00
David Grudl
6f34e56bd6 - updated Nette\Debug 2009-07-15 10:08:46 +00:00
David Grudl
18a7665bec - consolidating the aliases for the connection parameters 2009-07-13 20:13:48 +00:00
David Grudl
fb026ef45d - DibiTranslate: better validation for %m 2009-07-08 12:42:05 +00:00
David Grudl
2d69d4a18e - added new multiinsert modifier 'm' (thanks to Milan Matejcek) 2009-07-08 12:32:01 +00:00
David Grudl
0d79d16d2c - binary unescape doesn't throw exceptions more 2009-07-08 12:10:32 +00:00
David Grudl
a3f673e82b - DibiProfiler: added some limits for Firebug 2009-07-02 17:39:58 +00:00
David Grudl
6ff605ef0a - typo 2009-06-30 14:08:49 +00:00
David Grudl
6f4641c3ee - added example for dibi & Nette\Debug::consoleDump() 2009-06-26 08:24:14 +00:00
David Grudl
77efc27681 - DibiResultIterator: is countable (thanks to Roman Sklenar) 2009-06-19 11:32:24 +00:00
David Grudl
e52cee8cbc - DibiConnection: insert() and update() accepts ArrayObject 2009-06-19 11:28:42 +00:00
David Grudl
74178a5596 - DibiTranslator: added brackets for %and %or (thanks to Jiri Sutera)
- DibiMySqlDriver: added common error constants
2009-06-19 11:03:24 +00:00
David Grudl
c67bcd5598 - DibiDataSource: added escaping 2009-06-13 19:05:23 +00:00
David Grudl
bf15d60fd1 - DibiRow: added helper methods asDate() & asBool()
- DibiTranslator: added new modifier %in - integer or NULL
2009-06-03 13:42:02 +00:00
David Grudl
9c435acd2e - DibiDataSource: fixed count() vs. release() bug (thanks to Roman Sklenář) 2009-06-02 22:04:54 +00:00
David Grudl
1e08a91e66 - DibiTranslator: fixed strange security bug! (thanks to Matej Kravjar) 2009-06-02 09:05:13 +00:00
David Grudl
43c7e70e90 - DibiResult: setWithTables() and setRowClass() provide a fluent interface
- DibiTranslator: enhanced arrays syntax
2009-05-24 23:32:42 +00:00
David Grudl
fb8054d21a - added placeholder ? 2009-05-11 18:43:30 +00:00
David Grudl
564a0e534e - renamed compact -> minified 2009-05-09 09:44:54 +00:00
David Grudl
bba85b5441 - Oracle driver: fixed bug 2009-04-26 20:05:09 +00:00
David Grudl
98d43e0815 - Oracle driver: implemented applyLimit() & getTables()
- DibiDataSource: removed key word AS
- DibiProfiler: fixed bug with unbuffered queries
- DibiTranslator: empty %and generates '1=1'
2009-04-26 15:35:39 +00:00
David Grudl
6589519419 - DibiTranslator: empty arrays generate NULL (it may cause an BC break!)
- Dibi: disables magic_quotes_runtime
2009-04-16 02:15:20 +00:00
David Grudl
496c224be5 typos 2009-03-19 12:18:16 +00:00
David Grudl
aa05a85dfb - BYTEA detection fixed 2009-03-17 07:26:09 +00:00
David Grudl
5946b7e1f6 - removed 'FIELD_' from dibi data types 2009-03-16 06:48:27 +00:00
David Grudl
a9afe1e397 - improved binary escaping 2009-03-16 05:47:20 +00:00
David Grudl
97969edace - smarter handling of substitutions :subst: outside of brackets [] 2009-03-08 23:27:31 +00:00
David Grudl
d0097d6c9c - updated Nette\Debug 2009-03-08 18:00:36 +00:00
David Grudl
7969289aaa - DibiDataSource: fixed empty ORDER BY
- DibiTranslator: removed empty %by handling 
- DibiTranslator: better NULL values handling
2009-03-08 17:36:11 +00:00
David Grudl
5c2245a9d7 - added DibiDataSource::release() 2009-03-02 02:22:36 +00:00
David Grudl
2f86997ca7 - affectedRows(), insertId(), rowCount() are aliases for getAffectedRows(), getInsertId(), getRowCount() 2009-02-26 20:02:14 +00:00
David Grudl
d2f1beba57 - added connection directive 'substitutes'
- better ArrayObject handling in DibiTranslator
2009-02-25 20:10:17 +00:00
David Grudl
14b731e002 - added DibiDataSource::fetch(), fetchAll(), fetchSingle(), fetchPairs() & fetchAssoc() 2009-02-23 03:55:47 +00:00
David Grudl
60559d9a9f - added mysql_info support 2009-02-22 20:03:09 +00:00
David Grudl
b958e37fdf - supported substitutions :subst: outside of brackets [] 2009-02-22 18:58:29 +00:00
David Grudl
a26744388d - substitution is performed by DibiTranslator
- fixed bug in DibiTranslator
2009-02-22 18:35:18 +00:00
David Grudl
0b0596dbeb - DibiTable removed from distribution (is available as separated archive) 2009-02-13 12:59:40 +00:00
David Grudl
419929f813 - DibiSqliteDriver: added registerFunction() and registerAggregateFunction() 2009-02-11 03:38:28 +00:00
David Grudl
8df67d0de2 - fixed bug in DibiDataSource
- added DibiDataSource::getTotalCount()
- PostgreSql compatibility
2009-02-08 21:15:54 +00:00
David Grudl
60662bdae1 - improved DibiDataSource 2009-02-05 23:13:29 +00:00
David Grudl
dd6ffc3d0e - DibiTranslator: modifiers %ex, %by, %n improved, %SQL added
- DibiDataSource: allows select columns, sorting and conditions
2009-02-05 21:10:50 +00:00
David Grudl
46d79fc305 - DibiConnection & DibiTranslator refactoring
- DibiException accepts SQL parameter
- undeprecated IDataSource
2009-02-05 21:08:00 +00:00
David Grudl
32ed383326 - query returns DibiResult or number of affected rows now 2009-02-05 02:13:15 +00:00
David Grudl
bf6dc1cbd1 - DibiFluent implements Countable, IteratorAggregate
- DibiDataSource is deprecated
- DibiTranslator - fixed DateTime class support
2009-02-05 01:26:08 +00:00
David Grudl
a5a1da19a7 - DibiPostgreDriver: schema is case-sensitive
- DibiTranslator: %n modifier can construct [table] AS t statements
2009-02-02 17:50:02 +00:00
David Grudl
5ecfaf7ab1 - all drivers accepts injected connection resource
- DibiFluent: fixed identifier substitution
2009-01-17 19:27:40 +00:00
David Grudl
47d1180aee 2009-01-13 02:55:02 +00:00
David Grudl
7c0c4db0f6 - fixed Oracle driver 2009-01-10 22:33:29 +00:00
David Grudl
c24f2b8705 - API cleanup: some method in DibiConnection marked as deprecated 2009-01-08 00:42:01 +00:00
David Grudl
84172591d5 - added %ex modifier for keys in arrays 2009-01-07 17:30:01 +00:00
David Grudl
ca87994189 - DibiResultIterator is back (from rev. 123) 2009-01-07 13:48:01 +00:00
David Grudl
c5f8a260c7 - improved %and & %or modifiers behavior
- added %sql modifier for arrays (sub-translating)
2009-01-07 13:38:29 +00:00
David Grudl
96acdb46dc - DibiSQLiteDriver: added ability to convert strings to requested character encoding
- added experimental Microsoft SQL Server 2005 Driver for PHP
2009-01-07 03:34:50 +00:00
David Grudl
348af48ecd - updated PHP 5.3 namespace separator 2009-01-05 20:55:08 +00:00
David Grudl
52a9e316b8 - PHP 5.2.x ArrayObject serialization bug workaround 2009-01-05 00:03:27 +00:00
David Grudl
e5a1877ed9 - year 2008 -> 2009 2008-12-31 00:13:40 +00:00
David Grudl
4b189b93d6 - class DateTime support
- DibiProfiler checks whether headers was sent
2008-12-28 19:27:16 +00:00
David Grudl
781274ba65 - added fetch() methods into DibiConnection
- public $useFirebug
2008-12-07 23:59:04 +00:00
David Grudl
9244a1b1b5 - supported UNIX timestamp in datetime column 2008-11-25 20:33:52 +00:00
David Grudl
4b7b7de87a - DibiFluent - added support for nested selects
- DibiFluent::__toString() returns NOT highlighted SQL
2008-11-22 14:31:38 +00:00
David Grudl
fb2621eb04 - implemented savepoints support 2008-11-17 16:17:16 +00:00
David Grudl
2f690b63f9 - implemented insertId() in MS SQL driver 2008-11-09 20:03:08 +00:00
David Grudl
9f8b19f1fb - implemented applyLimit() in PDO driver 2008-11-07 10:47:57 +00:00
David Grudl
d08a5d3856 - added DibiResult::setRowClass() 2008-10-30 15:40:17 +00:00
David Grudl
901dc76103 - released "dibi 1.0"
- fixed FirePHP protocol bug
- DibiTable -> DibiTableX
2008-10-30 13:07:47 +00:00
David Grudl
8d063fe0a1 - added DibiPostgreDriver::getIndexes()
- implemented DibiTables types autodetection
- all examples uses Nette::Debug
2008-10-28 19:31:32 +00:00
David Grudl
a2d0d66d0f - phpDoc simplified 2008-10-28 15:24:47 +00:00
David Grudl
7565ffb1d4 - added dibi-field-type autodetection
- added DibiColumnInfo::getVendorInfo()
2008-10-28 14:37:40 +00:00
David Grudl
318b3093a5 - added DibiPostgreDriver::getColumns() 2008-10-28 12:27:21 +00:00
David Grudl
7a6cdc8afa - improved examples 2008-10-28 02:06:55 +00:00
David Grudl
ab892255d3 improved reflection skills 2008-10-28 01:03:50 +00:00
David Grudl
e221a13dda - requires FirePHP 0.2
- improved DibiFluent
2008-10-25 15:36:37 +00:00
David Grudl
56628144b4 - DibiFluent autodetects modifiers for associative arrays (after where, orderBy, ...)
- json_encode in PHP < 5.2
2008-10-25 00:26:56 +00:00
David Grudl
32dd3969a3 - added DibiProfiler (experimental)
- removed dibi::addHandler() & dibi::startLogger()
2008-10-22 11:44:11 +00:00
David Grudl
1e33b67e37 - added DibiTable magic fetch
- fixed some bugs
2008-10-21 13:42:53 +00:00
David Grudl
3e04378375 - returns result-set rows as DibiRow objects!
- removed option 'resultObjects'
- SQlite driver removes quotes from result-set column names
- this revision may cause compatibility break
2008-10-10 17:39:33 +00:00
David Grudl
ae77148773 introduced new class constants 2008-10-10 01:35:33 +00:00
David Grudl
b0f155f767 - implemented basic meta/reflection support 2008-10-02 17:13:43 +00:00
David Grudl
fc69f8f47b - fixed float numbers decimal separator vs. setlocale 2008-10-02 09:01:38 +00:00
David Grudl
44f281de27 fixed %by 2008-10-01 16:23:44 +00:00
David Grudl
9eddba204f - BC change: DibiResult::fetchAll() returns always multidimensional arrays (for single columns use fetchPairs() instead)
- added DibiTable::insertOrUpdate()
- new modifier %by
2008-10-01 16:04:16 +00:00
David Grudl
9b84459f09 quoted identifiers security fix 2008-09-15 23:58:03 +00:00
David Grudl
8c4211d5be - added fluent SQL builders support in DibiConnection and DibiTable 2008-09-13 16:38:59 +00:00
David Grudl
96c69f5bed 2008-09-11 12:37:41 +00:00
David Grudl
398c7a3500 added new methods fetchSingle, fetchAll, fetchAssoc and fetchPairs to DibiFluent 2008-09-11 12:33:49 +00:00
David Grudl
e5af8a8c67 - Object renamed to DibiObject
- DibiTranslator: improved %and and %or handling
- DibiTable::findAll allows to add conditions
2008-09-05 05:35:15 +00:00
David Grudl
f935968aa7 refactoring in drivers 2008-09-04 18:18:58 +00:00
David Grudl
52d2ecf5b0 - DibiResult::fetchAssoc() respects 'resultObject' configuration option
- DibiResult::dump() forces arrays
- updated Nette::Object
2008-09-03 22:47:49 +00:00
David Grudl
d4f9e0378f - fixed undefined item in mysqli 2008-08-28 15:41:19 +00:00
David Grudl
360f8799cf - added configuration option 'resultClass'
- updated class Object
2008-08-28 02:02:21 +00:00
David Grudl
119d5a1995 - renamed configuration keys result:object, result:withtables and format:* to resultObject resp. resultWithTables (due compatibility with PHP 5.3 INI parser)
- updated class Object
2008-08-25 18:55:50 +00:00
David Grudl
c438b72972 - DibiTable::insert() returns NULL when $primaryAutoIncrement is FALSE 2008-07-24 16:34:00 +00:00
David Grudl
97f2ed392f - public magic methods 2008-07-22 22:37:14 +00:00
David Grudl
e48cd54a41 - DibiTable::$types enables auto-convert columns to specified type
- DibiTable::insert() returns NULL, when $primary is FALSE
2008-07-22 13:12:53 +00:00
David Grudl
85d190ec05 2008-07-17 04:59:45 +00:00
David Grudl
c0bd3761de modified SVN properties 2008-07-17 03:51:29 +00:00
David Grudl
7f0fa2e75e - added config alias 'hostname' for 'host' 2008-07-15 09:34:05 +00:00
David Grudl
69ead6da56 improved dibi, Texy and Nette exceptions compatibility 2008-07-12 23:11:57 +00:00
David Grudl
41e5b32a22 minor: renamed resultset -> resultSet 2008-06-30 15:01:25 +00:00
David Grudl
2a4f5ec456 improved DibiPdoDriver identifier escaping 2008-06-18 23:34:35 +00:00
David Grudl
d12895102f - new DibiTable constructor
- moved "tricky clone" from DibiConnection::query to IDibiDriver::query
2008-06-10 01:09:23 +00:00
David Grudl
4e41f1641e - added dibi::setSubstFallBack()
- added DibiFluent::fetch()
2008-06-08 12:44:44 +00:00
David Grudl
c23bf15a3d - DibiDriver::format splitted into escape() & unescape()
- added DibiConnection::unescape
- DibiPostgreDriver support escaping & unescaping BYTEA type
2008-05-25 18:44:43 +00:00
David Grudl
3728b16a21 - added DibiFluent
- bugfix in dibi::dump()
2008-05-21 11:20:46 +00:00
David Grudl
69876a70b7 - removed DibiResultIterator
- DibiConnection::nativeQuery & query() returns DibiResult or NULL
- added optional parameters $offset, $limit, $simplify to DibiResult::fetchAll()
2008-05-20 08:15:30 +00:00
David Grudl
4334eaa963 fixed bug in IDibiDriver 2008-05-19 19:32:06 +00:00
David Grudl
a8acce6d59 - removed DibiPostgreDriver workaround (there is no bug in pg_affected_rows)
- added IDibiConnection::isConnected()
2008-05-16 17:56:24 +00:00
David Grudl
b27b0193f1 fixed compatibility with Nette::Config 2008-05-13 13:02:26 +00:00
David Grudl
40e9f313c3 - DibiPostgreDriver - workaround for bug in pg_affected_rows
- added DibiResult::setObjects([TRUE | FALSE | class name])
2008-05-12 22:55:51 +00:00
David Grudl
7bb5684d71 code formatting: 4 spaces -> tabs 2008-05-12 00:30:59 +00:00
David Grudl
fd22c55639 DibiPdoDriver - new connection parameter "pdo" for PDO object passing 2008-05-01 21:56:20 +00:00
David Grudl
62f8c47410 compatibility with Nette::Config 2008-04-21 11:32:58 +00:00
David Grudl
11a314ca96 minor: updated phpDoc headers 2008-04-19 14:13:48 +00:00
David Grudl
c8febb8322 /*Nette::*/Object 2008-04-16 10:18:02 +00:00
David Grudl
24bf999cd9 * better datetime converting in DibiResult (see http://forum.dibiphp.com/viewtopic.php?pid=2331)
* added support for Nette_Debug
* renamed NObject -> Nette_Object (Nette::Object in PHP 5.3)
2008-04-03 12:40:04 +00:00
David Grudl
2632953541 - removed NException, ability of catching PHP error moved to DibiDriverException 2008-03-04 14:01:22 +00:00
David Grudl
1459c6c95d updated headers 2008-02-15 04:51:35 +00:00
David Grudl
2f9704bca2 - DibiPostgreDriver: added support for "schema"
- DibiTable::fetch($conditions) (experimental)
- DibiConnection no longer invokes autoloading
2008-02-14 02:02:27 +00:00
David Grudl
8da9e778a6 added new Nette exceptions 2008-02-01 02:12:36 +00:00
David Grudl
89dfa9f772 fixed bug in conditional SQL 2008-01-20 01:50:30 +00:00
David Grudl
18e02de80c - REWRITTEN DibiTranslar
- allows modifiers inside SQL -> modifiers become placeholders
- new modifier %ex - expand array
- new modifiers %or and %and
- changed interface IDibiVariable and implementation DibiVariable
2008-01-18 07:35:45 +00:00
David Grudl
f6b781f12d - added DibiDataSource as default implementation of IDataSource
- new modifiers %lmt %ofs
- removed old modifier %p (alias for %sql)
2008-01-18 02:57:43 +00:00
David Grudl
c41167d49f bug fixed 2008-01-15 05:40:08 +00:00
David Grudl
5c045e58dc * added DibiTable
* new connection options: "result:objects" & "result:withtables"
* renamed DibiDriverInterface -> IDibiDriver, DibiVariableInterface -> IDibiVariable
2008-01-15 03:43:03 +00:00
David Grudl
da0a239d6d * dibi::loadFile() extreme fast SQL dump loading 2008-01-12 01:20:23 +00:00
David Grudl
de4a882788 - DibiResult: meta type Dibi::FIELD_BOOL resolves 'f' and 'F' as FALSE 2008-01-02 05:25:21 +00:00
David Grudl
041f059408 removed NClass 2007-12-11 07:28:55 +00:00
David Grudl
2c8906e7c4 * new: DibiResult::fetchAssoc() supports "object" mode via @ descriptor
* fixed mysqli_set_charset in PHP < 5.1.5
2007-12-07 16:51:17 +00:00
David Grudl
dfacb48449 * fixed DibiPostgreDriver::insertId
* changed URL to http://dibiphp.com
2007-12-05 09:27:55 +00:00
David Grudl
7d964e054b * fixed identifier delimitation in DibiPostgreDriver (table.col -> table."col")
* better SQL syntax highlighting
* removed addslashes from DibiPostgreDriver
2007-12-01 17:24:49 +00:00
David Grudl
cbd37021f2 * new: qualifiy each column name with the table name using DibiResult::setWithTables
* removed DibiResult::setType(TRUE) with autodetection
* removed DibiResult::getFields() & getMetaData() in favour of new method getColumnsMeta()
* MySQLi and MySQL transaction implementation are the same
* better escaping in DibiPostgreDriver (new pg_escape_string and addslashes)
2007-11-30 10:12:45 +00:00
David Grudl
1aad1c8da9 * better syntax highlighting
* all drivers checks for extension in constructor
* DibiMySqlDriver - charset is set by mysql_set_charset
* DibiMySqliDriver - charset is set by mysqli_set_charset
2007-11-28 15:56:57 +00:00
David Grudl
1a9abfb326 NClass moved to separate file 2007-11-26 01:44:07 +00:00
David Grudl
a0febd3d50 * added support for affectedRows in DibiPdoDriver 2007-11-24 08:23:26 +00:00
David Grudl
c8fedf7692 optimizations 2007-11-23 23:50:57 +00:00
David Grudl
7c6947a019 * added dibi::date & dibi::datetime
* DibiConnection::insertId && affectedRows throws exception on failure
* added protected throwException() to drivers
* DibiPostgreDriver - can build connection string
* DibiSqliteDriver - support for parameters 'format:date' & 'format:datetime'
* fixed query errors in DibiSqliteDriver
* DibiConnection prevents serialization and multiple transactions
2007-11-23 23:27:14 +00:00
David Grudl
3f42b2cf55 - added DibiVariable (experimental)
- fixed bug in DibiPostgreDriver::insertId()
2007-11-22 10:35:23 +00:00
David Grudl
981a1adaad Changed symbols for fetchAssoc: # means index, = means record field 2007-11-18 09:16:54 +00:00
David Grudl
58ed8d34f4 DibiException is NException descendant 2007-11-18 02:29:11 +00:00
David Grudl
cbb315cbc7 * renamed DibiDatabaseException to DibiDriverException
* PDO driver convert PDOExceptions to DibiDriverException
2007-11-17 09:37:55 +00:00
David Grudl
c174e20135 2007-11-15 00:19:41 +00:00
David Grudl
6bfa40f594 added NException 2007-11-14 23:05:57 +00:00
David Grudl
0eeff53fe8 * DibiConnection::getConfig('name') returns name of connection
* seek() returns boolean again
* DibiDriver throws exception when is used after free()
2007-11-13 01:51:44 +00:00
David Grudl
40444c1341 * added dibi::fetch, dibi::fetchAll, dibi::fetchSingle
* some bugs fixed
2007-11-12 16:20:44 +00:00
David Grudl
d19eb5b815 fixed some bugs 2007-11-12 14:35:55 +00:00
David Grudl
46850aa588 * update DibiVariableInterface
* some bugs fixed
2007-11-12 07:33:23 +00:00
David Grudl
89c53395c1 2007-11-12 06:43:09 +00:00
David Grudl
22c27f678a BIG REFACTORING!
* DibiDriver -> DibiConnection
2007-11-12 06:41:59 +00:00
David Grudl
ea00d5d37d * seek() or rowCount() in unbuffered mode throws exceptions
* out of range seek() throws exception
* deprecated DibiDriver::errorInfo
* fixed seek(0) on first iteration
* added DibiDatabaseException::catchError() & restore() for converting errors to exceptions
2007-11-12 01:39:26 +00:00
David Grudl
5ee6a19f93 * added support for unbuffered queries (MySQL, MySQLi, SQLite)
* doc-comments changed to be compatible with phpDocumentor
* DibiDriver::config() renamed to alias()
2007-11-12 00:08:29 +00:00
David Grudl
fd1d2b86ff added NClass 2007-11-11 04:59:39 +00:00
David Grudl
9ff43d0ac3 added DibiDriver::disconnect() 2007-11-10 07:37:44 +00:00
David Grudl
8a6d664876 * renamed some files libs
* added doc comments to drivers
* DibiDriver::prepare() renamed to config()
* fixed connection error handling in Postgre driver
2007-11-09 02:28:27 +00:00
David Grudl
6492fe10b6 * added NObject 2007-11-08 21:35:30 +00:00
David Grudl
8b99c00f91 fetchAssoc() && fetchPairs() throws exceptions 2007-11-08 06:19:03 +00:00
David Grudl
25fa4293fc * new exceptions: BadMethodCallException, InvalidArgumentException
* DibiMySqlDriver, DibiMySqliDriver, DibiSqliteDriver, DibiOracleDriver: error checking instead of FALSE checking in doQuery
2007-11-08 03:32:37 +00:00
David Grudl
453cc9be13 2007-11-01 05:52:13 +00:00
David Grudl
917b0851da added icon dibi-powered.gif 2007-11-01 02:06:47 +00:00
David Grudl
6856ace01e Changed New BSD License to "dibi license" 2007-10-30 00:58:15 +00:00
David Grudl
330930fb34 fixed boolean value in Postgre driver 2007-10-28 18:04:23 +00:00
David Grudl
39c62c1cd5 * added configuration aliases (user -> username, pass -> password)
* dibi::$defaultDriver
2007-10-26 17:44:24 +00:00
David Grudl
879bbeba1f changed website to http://php7.org/dibi/ 2007-10-13 22:50:29 +00:00
David Grudl
eb5b3d9756 + untested Oracle driver 2007-10-02 08:20:36 +00:00
David Grudl
ccea418c34 DibiDriver::doQuery returns TRUE or DibiResult 2007-10-01 05:34:50 +00:00
David Grudl
d35a850311 * removed $throwExceptions (always throws)
* added DibiLogger, dibi::notify(), dibi::startLogger()
* miniprofiler dibi::$numOfQueries, $totalTime, $elapsedTime
* simplified DibiException, added DibiDatabaseException
* Dibi::nativeQuery splitted into DibiDriver::doQuery & nativeQuery()
* moved dibi::dumpResult -> DibiResult::dump()
* moved dibi::test() -> DibiDriver::test()
* DibiTranslator generates $mask
2007-09-29 07:53:25 +00:00
David Grudl
0d8478d1d3 - dibi::substitute -> dibi::getSubst
- dibi:removeSubst(TRUE) removes all substitutes
2007-09-27 07:56:43 +00:00
David Grudl
281cdb65e0 * added dibi::addHandler & dibi::invokeEvent
* logging moved from DibiDriver -> Dibi::afterQuery()
2007-08-29 08:17:45 +00:00
David Grudl
53874f22d4 * support for sequence name in dibi::insertId() & DibiPostgreDriver::insertId()
* implemented DibiPostgreDriver::insertId()
* implemented DibiPostgreDriver::delimite()
2007-08-28 23:17:34 +00:00
David Grudl
d5e6cedddb added: throw new DibiException(__METHOD__ . ' is not implemented') 2007-08-28 22:25:01 +00:00
David Grudl
6f4d2c545d * removed variables $insertId & $affectedRows 2007-08-28 22:13:53 +00:00
David Grudl
0ff0cd21df * fixed pg_affected_rows 2007-08-28 21:41:15 +00:00
David Grudl
5243122e6a * support for big int & big floats 2007-08-27 22:38:14 +00:00
David Grudl
7f995a558b * fixed odbc_num_rows and pg_affected_rows 2007-08-23 17:12:58 +00:00
David Grudl
05b8c0ad43 2007-08-23 00:57:28 +00:00
David Grudl
2c6608f817 IDibiVariable -> DibiVariableInterface 2007-08-20 22:17:52 +00:00
David Grudl
8b89eb3bd0 * vetsina method/trid oznacena jako final 2007-08-09 03:31:34 +00:00
David Grudl
609a3d64fb static public -> public static 2007-06-25 23:47:05 +00:00
David Grudl
5f4dbbbcfd * some variables renamed 2007-06-25 17:02:12 +00:00
David Grudl
a7ddc1547c updated phpDoc blocks 2007-06-24 23:49:57 +00:00
David Grudl
5ffbe076f3 Changed "Access to undeclared property" exception message 2007-06-24 15:07:10 +00:00
David Grudl
e2e5ba16f1 * Changed license to NEW BSD license 2007-06-19 21:12:28 +00:00
David Grudl
c7dbc7134c * fetchPairs improved 2007-06-11 00:25:48 +00:00
David Grudl
e33689a5a1 multi INSERT or REPLACE command 2007-05-30 00:01:10 +00:00
David Grudl
89a7c8ac73 2007-05-17 21:02:26 +00:00
David Grudl
7452065de0 * new MS SQL driver
* removed constant DIBI
2007-05-13 18:32:03 +00:00
David Grudl
f766827219 bugfix, enhanced error reporting, better examples 2007-05-11 22:25:32 +00:00
David Grudl
d03f60c43c 2007-04-25 08:19:03 +00:00
David Grudl
7c693a26fb 2007-04-25 06:55:10 +00:00
David Grudl
799dfcc65e 2007-04-25 06:48:26 +00:00
David Grudl
b7834a3373 2007-04-25 06:44:38 +00:00
David Grudl
af7c4de14a * removed static factory DibiDriver::connect()
* added support for lazy connections
2007-04-25 06:18:06 +00:00
David Grudl
166f716091 * quoteName -> delimite
* fixed mysql_connect bug
2007-04-16 03:01:55 +00:00
David Grudl
57fa5831b1 2007-04-11 18:32:14 +00:00
David Grudl
e834c0ccae modified phpDoc headers 2007-04-11 18:30:30 +00:00
David Grudl
89ee302927 - added parameter $name in dibi::getConnection()
- bug fixed error handling in SQlite driver
2007-04-06 07:34:48 +00:00
David Grudl
53178717ad * DibiPdoDriver bugs fixed 2007-03-27 23:38:09 +00:00
David Grudl
48ea525b04 * DibiDriver::query -> DibiDriver::nativeQuery
* Dibi::query moved to DibiDriver::query
* methods getFields(), detectTypes(), getMetaData() moved to base class DibiDriver
* added PDO driver (not tested)
2007-03-27 23:12:36 +00:00
David Grudl
f64a5d5251 * fixed affectedRows & insertId bug 2007-03-26 06:22:53 +00:00
David Grudl
4d2c90ba68 * support for default values (mysql.default_user etc...) 2007-02-14 18:27:06 +00:00
David Grudl
4b2e04220b * DibiResult::fetchAssoc() rewritten 2007-02-05 05:14:48 +00:00
David Grudl
0c86515076 * modified DibiException (getDbError, ...)
* fix dibi::dumpResult()
2007-02-02 03:51:43 +00:00
David Grudl
a2b1036a66 * SET CHARACTER SET -> SET NAMES
* %p bug
2007-01-30 21:50:04 +00:00
David Grudl
6536dfb7dd + rewritten support for logging & error handling
+ modifier %sn
* modifier %sql
* changed modifier behaviour: NULL is always 'NULL'
2007-01-29 05:08:52 +00:00
David Grudl
ac980fe8c9 + DibiResult::fetchAll()
* changed year in headers (2007)
2007-01-08 00:55:11 +00:00
David Grudl
3b8766d376 * throwing exception in DibiTranslator and DibiDriver
+ added dibi::$errorMode
2006-11-22 12:55:24 +00:00
David Grudl
f447a03c96 throwing exception on connect 2006-11-13 06:32:16 +00:00
David Grudl
11b294be44 renamed parser -> translator
added "Undefined property usage prevention"
2006-10-26 13:09:56 +00:00
David Grudl
d705f4089d applyLimit support 2006-09-23 07:55:11 +00:00
David Grudl
da608c2db2 substitutes moved from DibiDriver to Dibi
changed "comments" behavior in DibiParser
2006-09-23 06:34:44 +00:00
David Grudl
da70be27a8 2006-09-13 12:02:38 +00:00
David Grudl
6c4ede17d6 added PostgreSQL driver 2006-09-13 11:49:32 +00:00
David Grudl
36b88503f9 remove operator @ in connect() methods 2006-08-25 18:10:30 +00:00
David Grudl
2cc9fa22fb prefix & substitution support (0.6c) 2006-08-25 15:17:40 +00:00
149 changed files with 16755 additions and 3540 deletions

4
.gitattributes vendored Normal file
View File

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

2
.gitignore vendored Normal file
View File

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

47
.travis.yml Normal file
View File

@@ -0,0 +1,47 @@
language: php
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
matrix:
allow_failures:
- php: 7.1
- php: hhvm
script:
- 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
- for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done
before_script:
# Install Nette Tester & Code Checker
- travis_retry composer install --no-interaction
- travis_retry composer create-project nette/code-checker temp/code-checker ~2.5 --no-interaction
- if [ $TRAVIS_PHP_VERSION == "7.0" ]; then COVERAGE="-p phpdbg --coverage ./coverage.xml --coverage-src ./src"; fi
# Create 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:
directories:
- $HOME/.composer/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 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-5.6.14-Win32-VC11-x86.zip
- IF %PHP%==1 7z x php-5.6.14-Win32-VC11-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_56_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 copy SQLSRV\php_pdo_sqlsrv_56_ts.dll ext\php_pdo_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install PHP 7
- IF EXIST c:\php7 (SET PHP=0) ELSE (SET PHP=1)
- IF %PHP%==1 mkdir c:\php7
- IF %PHP%==1 cd c:\php7
- IF %PHP%==1 appveyor DownloadFile http://windows.php.net/downloads/releases/archives/php-7.0.3-Win32-VC14-x86.zip
- IF %PHP%==1 7z x php-7.0.3-Win32-VC14-x86.zip >nul
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 appveyor DownloadFile https://files.nette.org/misc/php-sqlsrv.zip
- IF %PHP%==1 7z x php-sqlsrv.zip >nul
- IF %PHP%==1 copy SQLSRV\php_sqlsrv_7_ts.dll ext\php_sqlsrv_ts.dll
- IF %PHP%==1 del /Q *.zip
# Install Nette Tester
- cd c:\projects\dibi
- appveyor DownloadFile https://getcomposer.org/composer.phar
- php composer.phar install --prefer-dist --no-interaction --no-progress
# Create databases.ini
- copy tests\databases.appveyor.ini tests\databases.ini
test_script:
- vendor\bin\tester tests -s -p c:\php5\php -c tests\php5-win.ini
- vendor\bin\tester tests -s -p c:\php7\php -c tests\php7-win.ini
on_failure:
# Print *.actual content
- type tests\dibi\output\*.actual

32
composer.json Normal file
View File

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

27
contributing.md Normal file
View File

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

View File

@@ -1,20 +0,0 @@
Copyright notice
----------------
dibi (C) David Grudl, 2005-2006 <dave@dgx.cz>
For more information, visit the homepage http://texy.info/dibi
or author's weblog: http://www.dgx.cz/trine/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

View File

@@ -1,35 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* For PHP 5.0.3 or newer
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version 0.6b $Revision: 11 $ $Date: 2006-06-08 04:02:05 +0200 (čt, 08 VI 2006) $
*/define('DIBI','Version 0.6b $Revision: 11 $');if(version_compare(PHP_VERSION,'5.0.3','<'))die('dibi needs PHP 5.0.3 or newer');
if(!defined('DIBI'))die();abstract class DibiDriver{protected$config;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);abstract static public function connect($config);protected function __construct($config){$this->config=$config;}public function getConfig(){return$this->config;}abstract public function query($sql);abstract public function affectedRows();abstract public function insertId();abstract public function begin();abstract public function commit();abstract public function rollback();abstract public function escape($value,$appendQuotes=FALSE);abstract public function quoteName($value);abstract public function getMetaData();}
if(!defined('DIBI'))die();if(!interface_exists('Countable',false)){interface Countable{function count();}}abstract class DibiResult implements IteratorAggregate,Countable{protected$convert;abstract public function seek($row);abstract public function rowCount();abstract public function getFields();abstract public function getMetaData($field);abstract protected function detectTypes();abstract protected function free();abstract protected function doFetch();final public function fetch(){$rec=$this->doFetch();if(!is_array($rec))return FALSE;if($t=$this->convert){foreach($rec as$key=>$value){if(isset($t[$key]))$rec[$key]=$this->convert($value,$t[$key]);}}return$rec;}final function fetchSingle(){$rec=$this->doFetch();if(!is_array($rec))return FALSE;if($t=$this->convert){$value=reset($rec);$key=key($rec);return isset($t[$key])?$this->convert($value,$t[$key]):$value;}return reset($rec);}final function fetchAll(){@$this->seek(0);$rec=$this->fetch();if(!$rec)return array();$assocBy=func_get_args();$arr=array();if(!$assocBy){$value=count($rec)==1?key($rec):NULL;do{$arr[]=$value===NULL?$rec:$rec[$value];}while($rec=$this->fetch());return$arr;}do{foreach($assocBy as$n=>$assoc){$val[$n]=$rec[$assoc];unset($rec[$assoc]);}foreach($assocBy as$n=>$assoc){if($n==0)$tmp=&$arr[$val[$n]];else$tmp=&$tmp[$assoc][$val[$n]];if($tmp===NULL)$tmp=$rec;}}while($rec=$this->fetch());return$arr;}final function fetchPairs($key,$value){@$this->seek(0);$rec=$this->fetch();if(!$rec)return array();$arr=array();do{$arr[$rec[$key]]=$rec[$value];}while($rec=$this->fetch());return$arr;}public function __destruct(){@$this->free();}public function setType($field,$type=NULL){if($field===TRUE)$this->detectTypes();elseif(is_array($field))$this->convert=$field;else$this->convert[$field]=$type;}public function getType($field){return isset($this->convert[$field])?$this->convert[$field]:NULL;}public function convert($value,$type){if($value===NULL||$value===FALSE)return$value;static$conv=array(dibi::FIELD_TEXT=>'string',dibi::FIELD_BINARY=>'string',dibi::FIELD_BOOL=>'bool',dibi::FIELD_INTEGER=>'int',dibi::FIELD_FLOAT=>'float',dibi::FIELD_COUNTER=>'int',);if(isset($conv[$type])){settype($value,$conv[$type]);return$value;}if($type==dibi::FIELD_DATE)return strtotime($value);if($type==dibi::FIELD_DATETIME)return strtotime($value);return$value;}public function getIterator($offset=NULL,$count=NULL){return new DibiResultIterator($this,$offset,$count);}public function count(){return$this->rowCount();}}class DibiResultIterator implements Iterator{private$result,$offset,$count,$record,$row;public function __construct(DibiResult$result,$offset=NULL,$count=NULL){$this->result=$result;$this->offset=(int)$offset;$this->count=$count===NULL?2147483647:(int)$count;}public function rewind(){$this->row=0;@$this->result->seek($this->offset);$this->record=$this->result->fetch();}public function key(){return$this->row;}public function current(){return$this->record;}public function next(){$this->record=$this->result->fetch();$this->row++;}public function valid(){return is_array($this->record)&&($this->row<$this->count);}}
if(!defined('DIBI'))die();class DibiParser{private$modifier,$hasError,$driver,$ifLevel,$ifLevelStart;public function parse($driver,$args){$this->driver=$driver;$this->hasError=FALSE;$command=null;$mod=&$this->modifier;$mod=FALSE;$this->ifLevel=$this->ifLevelStart=0;$comment=&$this->comment;$comment=FALSE;$sql=array();foreach($args as$arg){if('if'==$mod){$mod=FALSE;$this->ifLevel++;if(!$comment&&!$arg){$sql[]='/*';$this->ifLevelStart=$this->ifLevel;$comment=TRUE;}continue;}if(is_string($arg)&&(!$mod||'p'==$mod)){$mod=FALSE;$sql[]=$this->formatValue($arg,'p');continue;}if(!$mod&&is_array($arg)&&is_string(key($arg))){if(!$command)$command=strtoupper(substr(ltrim($args[0]),0,6));$mod=('INSERT'==$command||'REPLAC'==$command)?'v':'a';}$sql[]=$comment?'...':$this->formatValue($arg,$mod);$mod=FALSE;}if($comment)$sql[]='*/';$sql=implode(' ',$sql);if($this->hasError)return new DibiException('Errors during generating SQL',array('sql'=>$sql));return$sql;}private function formatValue($value,$modifier){if(is_array($value)){$vx=$kx=array();switch($modifier){case'a':foreach($value as$k=>$v){$pair=explode('%',$k,2);if(isset($pair[1])){$mod=$pair[1];if(isset($mod[0])&&'?'==$mod[0]){if(NULL===$v)continue;$mod=substr($mod,1);}}else$mod=FALSE;$vx[]=$this->driver->quoteName($pair[0]).'='.$this->formatValue($v,$mod);}return implode(', ',$vx);case'v':foreach($value as$k=>$v){$pair=explode('%',$k,2);if(isset($pair[1])){$mod=$pair[1];if(isset($mod[0])&&'?'==$mod[0]){if($v===NULL)continue;$mod=substr($mod,1);}}else$mod=FALSE;$kx[]=$this->driver->quoteName($pair[0]);$vx[]=$this->formatValue($v,$mod);}return'('.implode(', ',$kx).') VALUES ('.implode(', ',$vx).')';default:foreach($value as$v)$vx[]=$this->formatValue($v,$modifier);return implode(', ',$vx);}}if($modifier){if($value instanceof IDibiVariable)return$value->toSql($this->driver,$modifier);if(!is_scalar($value)&&!is_null($value)){$this->hasError=TRUE;return'**Unexpected '.gettype($value).'**';}switch($modifier){case"s":return$this->driver->escape($value,TRUE);case'b':return$value?$this->driver->formats['TRUE']:$this->driver->formats['FALSE'];case'i':case'u':return(string)(int)$value;case'f':return(string)(float)$value;case'd':return date($this->driver->formats['date'],is_string($value)?strtotime($value):$value);case't':return date($this->driver->formats['datetime'],is_string($value)?strtotime($value):$value);case'n':return$this->driver->quoteName($value);case'p':$value=(string)$value;$toSkip=strcspn($value,'`[\'"%');if(strlen($value)==$toSkip)return$value;return substr($value,0,$toSkip).preg_replace_callback('/
(?=`|\[|\'|"|%) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier]
(\')((?:\'\'|[^\'])*)\'| ## 3,4) string
(")((?:""|[^"])*)"| ## 5,6) "string"
%(else|end)| ## 7) conditional SQL
%([a-zA-Z]{1,2})$| ## 8) right modifier
(\'|") ## 9) lone-quote
)/xs',array($this,'callback'),substr($value,$toSkip));case'a':case'v':$this->hasError=TRUE;return"**Unexpected ".gettype($value)."**";case'if':$this->hasError=TRUE;return"**The %$modifier is not allowed here**";default:$this->hasError=TRUE;return"**Unknown modifier %$modifier**";}}if(is_string($value))return$this->driver->escape($value,TRUE);if(is_int($value)||is_float($value))return(string)$value;if(is_bool($value))return$value?$this->driver->formats['TRUE']:$this->driver->formats['FALSE'];if(is_null($value))return$this->driver->formats['NULL'];if($value instanceof IDibiVariable)return$value->toSql($this->driver);$this->hasError=TRUE;return'**Unexpected '.gettype($value).'**';}private function callback($matches){if($matches[1])return$this->driver->quoteName($matches[1]);if($matches[2])return$this->driver->quoteName($matches[2]);if($matches[3])return$this->comment?'...':$this->driver->escape(strtr($matches[4],array("''"=>"'")),TRUE);if($matches[5])return$this->comment?'...':$this->driver->escape(strtr($matches[6],array('""'=>'"')),TRUE);if($matches[7]){if(!$this->ifLevel){$this->hasError=TRUE;return"**Unexpected condition $matches[8]**";}if('end'==$matches[7]){$this->ifLevel--;if($this->ifLevelStart==$this->ifLevel+1){$this->ifLevelStart=0;$this->comment=FALSE;return'*/';}return'';}if($this->ifLevelStart==$this->ifLevel){$this->ifLevelStart=0;$this->comment=FALSE;return'*/';}elseif(!$this->comment){$this->ifLevelStart=$this->ifLevel;$this->comment=TRUE;return'/*';}}if($matches[8]){$this->modifier=$matches[8];return'';}if($matches[9]){$this->hasError=TRUE;return'**Alone quote**';}die('this should be never executed');}}
if(!defined('DIBI'))die();class DibiException extends Exception{private$info;public function __construct($message,$info=NULL){$this->info=$info;if(isset($info['message']))$message="$message: $info[message]";parent::__construct($message);}public function getSql(){return@$this->info['sql'];}}function is_error($var){return($var===FALSE)||($var instanceof Exception);} if(function_exists('date_default_timezone_set'))date_default_timezone_set('Europe/Prague');interface IDibiVariable{public function toSQL($driver,$modifier=NULL);}class dibi{const FIELD_TEXT='s',FIELD_BINARY='S',FIELD_BOOL='b',FIELD_INTEGER='i',FIELD_FLOAT='f',FIELD_DATE='d',FIELD_DATETIME='t',FIELD_UNKNOWN='?',FIELD_COUNTER='c';static private$registry=array();static private$conn;static private$parser;static public$sql;static public$error;static public$logFile;static public$debug=false;static public function connect($config,$name='def'){if(!self::$parser)self::$parser=new DibiParser();if(is_string($config))parse_str($config,$config);if(empty($config['driver']))return new DibiException('Driver is not specified.');$className="Dibi$config[driver]Driver"; if(!class_exists($className))return new DibiException("Unable to create instance of dibi driver class '$className'.");$conn=call_user_func(array($className,'connect'),$config);if(self::$logFile!=NULL){if(is_error($conn))$msg="Can't connect to DB '$config[driver]': ".$conn->getMessage();else$msg="Successfully connected to DB '$config[driver]'";$f=fopen(self::$logFile,'a');fwrite($f,"$msg\r\n\r\n");fclose($f);}if(is_error($conn)){if(self::$debug)echo'[dibi error] '.$conn->getMessage();return$conn;}self::$conn=self::$registry[$name]=$conn;return TRUE;}static public function isConnected(){return(bool)self::$conn;}static public function getConnection($name=NULL){return NULL===$name?self::$conn:@self::$registry[$name];}static public function activate($name){if(!isset(self::$registry[$name]))return FALSE;self::$conn=self::$registry[$name];return TRUE;}static public function query($args){if(!self::$conn)return new DibiException('Dibi is not connected to DB');if(!is_array($args))$args=func_get_args();self::$sql=self::$parser->parse(self::$conn,$args);if(is_error(self::$sql))return self::$sql;$timer=-microtime(true);$res=self::$conn->query(self::$sql);$timer+=microtime(true);if(is_error($res)){if(self::$debug){echo'[dibi error] '.$res->getMessage();self::dump(self::$sql);}self::$error=$res;}else{self::$error=FALSE;}if(self::$logFile!=NULL){if(is_error($res))$msg=$res->getMessage();elseif($res instanceof DibiResult)$msg='object('.get_class($res).') rows: '.$res->rowCount();else$msg='OK';$f=fopen(self::$logFile,'a');fwrite($f,self::$sql.";\r\n-- Result: $msg"."\r\n-- Takes: ".sprintf('%0.3f',$timer*1000).' ms'."\r\n\r\n");fclose($f);}return$res;}static public function test($args){if(!self::$conn)return FALSE;if(!is_array($args))$args=func_get_args();$sql=self::$parser->parse(self::$conn,$args);$dump=TRUE;if($dump){if(is_error($sql))self::dump($sql->getSql());else self::dump($sql);}return$sql;}static public function insertId(){if(!self::$conn)return FALSE;return self::$conn->insertId();}static public function affectedRows(){if(!self::$conn)return FALSE;return self::$conn->affectedRows();}static private function dumpHighlight($matches){if(!empty($matches[1]))return'<em style="color:gray">'.$matches[1].'</em>';if(!empty($matches[2]))return'<strong style="color:red">'.$matches[2].'</strong>';if(!empty($matches[3]))return'<strong style="color:blue">'.$matches[3].'</strong>';if(!empty($matches[4]))return'<strong style="color:green">'.$matches[4].'</strong>';}static public function dump($sql){static$keywords2='ALL|DISTINCT|AS|ON|INTO|AND|OR|AS';static$keywords1='SELECT|UPDATE|INSERT|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN';$sql=preg_replace("#\\b(?:$keywords1)\\b#","\n\$0",$sql);$sql=trim($sql);$sql=wordwrap($sql,100);$sql=htmlSpecialChars($sql);$sql=strtr($sql,array("\n"=>'<br />'));$sql=preg_replace_callback("#(/\*.+?\*/)|(\*\*.+?\*\*)|\\b($keywords1)\\b|\\b($keywords2)\\b#",array('dibi','dumpHighlight'),$sql);echo'<pre class="dibi">',$sql,'</pre>';}static public function dumpResult(DibiResult$res){echo'<table class="dump"><tr>';echo'<th>Row</th>';$fieldCount=$res->fieldCount();for($i=0;$i<$fieldCount;$i++){$info=$res->fieldMeta($i);echo'<th>'.htmlSpecialChars($info['name']).'</th>';}echo'</tr>';foreach($res as$row=>$fields){echo'<tr><th>',$row,'</th>';foreach($fields as$field){if(is_object($field))$field=$field->__toString();echo'<td>',htmlSpecialChars($field),'</td>';}echo'</tr>';}echo'</table>';}}
if(!defined('DIBI'))die();class DibiMySqlDriver extends DibiDriver{private$conn,$insertId=FALSE,$affectedRows=FALSE;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('mysql'))return new DibiException("PHP extension 'mysql' is not loaded");if(empty($config['host']))$config['host']='localhost';if(@$config['protocol']==='unix')$host=':'.$config['host'];else$host=$config['host'].(empty($config['port'])?'':$config['port']);if(function_exists('ini_set'))$save=ini_set('track_errors',TRUE);$php_errormsg='';if(empty($config['persistent']))$conn=@mysql_connect($host,@$config['username'],@$config['password']);else$conn=@mysql_pconnect($host,@$config['username'],@$config['password']);if(function_exists('ini_set'))ini_set('track_errors',$save);if(!is_resource($conn))return new DibiException("Connecting error",array('message'=>mysql_error()?mysql_error():$php_errormsg,'code'=>mysql_errno(),));if(!empty($config['charset'])){$succ=@mysql_query('SET CHARACTER SET '.$config['charset'],$conn);}if(!empty($config['database'])){if(!@mysql_select_db($config['database'],$conn))return new DibiException("Connecting error",array('message'=>mysql_error($conn),'code'=>mysql_errno($conn),));}$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$this->insertId=$this->affectedRows=FALSE;$res=@mysql_query($sql,$this->conn);if(is_resource($res))return new DibiMySqlResult($res);if($res===FALSE)return new DibiException("Query error",array('message'=>mysql_error($this->conn),'code'=>mysql_errno($this->conn),'sql'=>$sql,));$this->affectedRows=mysql_affected_rows($this->conn);if($this->affectedRows<0)$this->affectedRows=FALSE;$this->insertId=mysql_insert_id($this->conn);if($this->insertId<1)$this->insertId=FALSE;return TRUE;}public function affectedRows(){return$this->affectedRows;}public function insertId(){return$this->insertId;}public function begin(){return mysql_query('BEGIN',$this->conn);}public function commit(){return mysql_query('COMMIT',$this->conn);}public function rollback(){return mysql_query('ROLLBACK',$this->conn);}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".mysql_real_escape_string($value,$this->conn)."'":mysql_real_escape_string($value,$this->conn);}public function quoteName($value){return'`'.strtr($value,array('.'=>'`.`')).'`';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiMySqlResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return mysql_num_rows($this->resource);}protected function doFetch(){return mysql_fetch_assoc($this->resource);}public function seek($row){return mysql_data_seek($this->resource,$row);}protected function free(){mysql_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){static$types=array('ENUM'=>dibi::FIELD_TEXT,'SET'=>dibi::FIELD_TEXT,'CHAR'=>dibi::FIELD_TEXT,'VARCHAR'=>dibi::FIELD_TEXT,'STRING'=>dibi::FIELD_TEXT,'TINYTEXT'=>dibi::FIELD_TEXT,'TEXT'=>dibi::FIELD_TEXT,'MEDIUMTEXT'=>dibi::FIELD_TEXT,'LONGTEXT'=>dibi::FIELD_TEXT,'BINARY'=>dibi::FIELD_BINARY,'VARBINARY'=>dibi::FIELD_BINARY,'TINYBLOB'=>dibi::FIELD_BINARY,'BLOB'=>dibi::FIELD_BINARY,'MEDIUMBLOB'=>dibi::FIELD_BINARY,'LONGBLOB'=>dibi::FIELD_BINARY,'DATE'=>dibi::FIELD_DATE,'DATETIME'=>dibi::FIELD_DATETIME,'TIMESTAMP'=>dibi::FIELD_DATETIME,'TIME'=>dibi::FIELD_DATETIME,'BIT'=>dibi::FIELD_BOOL,'YEAR'=>dibi::FIELD_INTEGER,'TINYINT'=>dibi::FIELD_INTEGER,'SMALLINT'=>dibi::FIELD_INTEGER,'MEDIUMINT'=>dibi::FIELD_INTEGER,'INT'=>dibi::FIELD_INTEGER,'INTEGER'=>dibi::FIELD_INTEGER,'BIGINT'=>dibi::FIELD_INTEGER,'FLOAT'=>dibi::FIELD_FLOAT,'DOUBLE'=>dibi::FIELD_FLOAT,'REAL'=>dibi::FIELD_FLOAT,'DECIMAL'=>dibi::FIELD_FLOAT,'NUMERIC'=>dibi::FIELD_FLOAT,);$count=mysql_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$info['native']=$native=strtoupper(mysql_field_type($this->resource,$index));$info['flags']=explode(' ',mysql_field_flags($this->resource,$index));$info['length']=mysql_field_len($this->resource,$index);$info['table']=mysql_field_table($this->resource,$index);if(in_array('auto_increment',$info['flags']))$info['type']=dibi::FIELD_COUNTER;else{$info['type']=isset($types[$native])?$types[$native]:dibi::FIELD_UNKNOWN;}$name=mysql_field_name($this->resource,$index);$this->meta[$name]=$info;$this->convert[$name]=$info['type'];}}}
if(!defined('DIBI'))die();class DibiMySqliDriver extends DibiDriver{private$conn,$insertId=FALSE,$affectedRows=FALSE;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('mysqli'))return new DibiException("PHP extension 'mysqli' is not loaded");if(empty($config['host']))$config['host']='localhost';$conn=@mysqli_connect($config['host'],@$config['username'],@$config['password'],@$config['database'],@$config['port']);if(!$conn)return new DibiException("Connecting error",array('message'=>mysqli_connect_error(),'code'=>mysqli_connect_errno(),));if(!empty($config['charset']))mysqli_query($conn,'SET CHARACTER SET '.$config['charset']);$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$this->insertId=$this->affectedRows=FALSE;$res=@mysqli_query($this->conn,$sql);if(is_object($res))return new DibiMySqliResult($res);if($res===FALSE)return new DibiException("Query error",$this->errorInfo($sql));$this->affectedRows=mysqli_affected_rows($this->conn);if($this->affectedRows<0)$this->affectedRows=FALSE;$this->insertId=mysqli_insert_id($this->conn);if($this->insertId<1)$this->insertId=FALSE;return TRUE;}public function affectedRows(){return$this->affectedRows;}public function insertId(){return$this->insertId;}public function begin(){return mysqli_autocommit($this->conn,FALSE);}public function commit(){$ok=mysqli_commit($this->conn);mysqli_autocommit($this->conn,TRUE);return$ok;}public function rollback(){$ok=mysqli_rollback($this->conn);mysqli_autocommit($this->conn,TRUE);return$ok;}private function errorInfo($sql=NULL){return array('message'=>mysqli_error($this->conn),'code'=>mysqli_errno($this->conn),'sql'=>$sql,);}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".mysqli_real_escape_string($this->conn,$value)."'":mysqli_real_escape_string($this->conn,$value);}public function quoteName($value){return'`'.strtr($value,array('.'=>'`.`')).'`';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiMySqliResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return mysqli_num_rows($this->resource);}protected function doFetch(){return mysqli_fetch_assoc($this->resource);}public function seek($row){return mysqli_data_seek($this->resource,$row);}protected function free(){mysqli_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){static$types=array(MYSQLI_TYPE_FLOAT=>dibi::FIELD_FLOAT,MYSQLI_TYPE_DOUBLE=>dibi::FIELD_FLOAT,MYSQLI_TYPE_DECIMAL=>dibi::FIELD_FLOAT,MYSQLI_TYPE_TINY=>dibi::FIELD_INTEGER,MYSQLI_TYPE_SHORT=>dibi::FIELD_INTEGER,MYSQLI_TYPE_LONG=>dibi::FIELD_INTEGER,MYSQLI_TYPE_LONGLONG=>dibi::FIELD_INTEGER,MYSQLI_TYPE_INT24=>dibi::FIELD_INTEGER,MYSQLI_TYPE_YEAR=>dibi::FIELD_INTEGER,MYSQLI_TYPE_GEOMETRY=>dibi::FIELD_INTEGER,MYSQLI_TYPE_DATE=>dibi::FIELD_DATE,MYSQLI_TYPE_NEWDATE=>dibi::FIELD_DATE,MYSQLI_TYPE_TIMESTAMP=>dibi::FIELD_DATETIME,MYSQLI_TYPE_TIME=>dibi::FIELD_DATETIME,MYSQLI_TYPE_DATETIME=>dibi::FIELD_DATETIME,MYSQLI_TYPE_ENUM=>dibi::FIELD_TEXT,MYSQLI_TYPE_SET=>dibi::FIELD_TEXT,MYSQLI_TYPE_STRING=>dibi::FIELD_TEXT,MYSQLI_TYPE_VAR_STRING=>dibi::FIELD_TEXT,MYSQLI_TYPE_TINY_BLOB=>dibi::FIELD_BINARY,MYSQLI_TYPE_MEDIUM_BLOB=>dibi::FIELD_BINARY,MYSQLI_TYPE_LONG_BLOB=>dibi::FIELD_BINARY,MYSQLI_TYPE_BLOB=>dibi::FIELD_BINARY,);$count=mysqli_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$info=(array)mysqli_fetch_field_direct($this->resource,$index);$native=$info['native']=$info['type'];if($info['flags']&MYSQLI_AUTO_INCREMENT_FLAG)$info['type']=dibi::FIELD_COUNTER;else{$info['type']=isset($types[$native])?$types[$native]:dibi::FIELD_UNKNOWN;}$this->meta[$info['name']]=$info;$this->convert[$info['name']]=$info['type'];}}}
if(!defined('DIBI'))die();class DibiOdbcDriver extends DibiDriver{private$conn,$affectedRows=FALSE;public$formats=array('NULL'=>"NULL",'TRUE'=>"-1",'FALSE'=>"0",'date'=>"#m/d/Y#",'datetime'=>"#m/d/Y H:i:s#",);public static function connect($config){if(!extension_loaded('odbc'))return new DibiException("PHP extension 'odbc' is not loaded");if(@$config['persistent'])$conn=@odbc_pconnect($config['database'],$config['username'],$config['password']);else$conn=@odbc_connect($config['database'],$config['username'],$config['password']);if(!is_resource($conn))return new DibiException("Connecting error",array('message'=>odbc_errormsg(),'code'=>odbc_error(),));$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$this->affectedRows=FALSE;$res=@odbc_exec($this->conn,$sql);if(is_resource($res))return new DibiOdbcResult($res);if($res===FALSE)return new DibiException("Query error",$this->errorInfo($sql));$this->affectedRows=odbc_num_rows($this->conn);if($this->affectedRows<0)$this->affectedRows=FALSE;return TRUE;}public function affectedRows(){return$this->affectedRows;}public function insertId(){return FALSE;}public function begin(){return odbc_autocommit($this->conn,FALSE);}public function commit(){$ok=odbc_commit($this->conn);odbc_autocommit($this->conn,TRUE);return$ok;}public function rollback(){$ok=odbc_rollback($this->conn);odbc_autocommit($this->conn,TRUE);return$ok;}private function errorInfo($sql=NULL){return array('message'=>odbc_errormsg($this->conn),'code'=>odbc_error($this->conn),'sql'=>$sql,);}public function escape($value,$appendQuotes=FALSE){$value=str_replace("'","''",$value);return$appendQuotes?"'".$value."'":$value;}public function quoteName($value){return'['.strtr($value,array('.'=>'].[')).']';}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiOdbcResult extends DibiResult{private$resource,$meta,$row=0;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return odbc_num_rows($this->resource);}protected function doFetch(){return odbc_fetch_array($this->resource,$this->row++);}public function seek($row){$this->row=$row;}protected function free(){odbc_free_result($this->resource);}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){if($this->meta!==NULL)return$this->meta;static$types=array('CHAR'=>dibi::FIELD_TEXT,'COUNTER'=>dibi::FIELD_COUNTER,'VARCHAR'=>dibi::FIELD_TEXT,'LONGCHAR'=>dibi::FIELD_TEXT,'INTEGER'=>dibi::FIELD_INTEGER,'DATETIME'=>dibi::FIELD_DATETIME,'CURRENCY'=>dibi::FIELD_FLOAT,'BIT'=>dibi::FIELD_BOOL,'LONGBINARY'=>dibi::FIELD_BINARY,'SMALLINT'=>dibi::FIELD_INTEGER,'BYTE'=>dibi::FIELD_INTEGER,'BIGINT'=>dibi::FIELD_INTEGER,'INT'=>dibi::FIELD_INTEGER,'TINYINT'=>dibi::FIELD_INTEGER,'REAL'=>dibi::FIELD_FLOAT,'DOUBLE'=>dibi::FIELD_FLOAT,'DECIMAL'=>dibi::FIELD_FLOAT,'NUMERIC'=>dibi::FIELD_FLOAT,'MONEY'=>dibi::FIELD_FLOAT,'SMALLMONEY'=>dibi::FIELD_FLOAT,'FLOAT'=>dibi::FIELD_FLOAT,'YESNO'=>dibi::FIELD_BOOL,);$count=odbc_num_fields($this->resource);$this->meta=$this->convert=array();for($index=1;$index<=$count;$index++){$native=strtoupper(odbc_field_type($this->resource,$index));$name=odbc_field_name($this->resource,$index);$this->meta[$name]=array('type'=>isset($types[$native])?$types[$native]:dibi::FIELD_UNKNOWN,'native'=>$native,'length'=>odbc_field_len($this->resource,$index),'scale'=>odbc_field_scale($this->resource,$index),'precision'=>odbc_field_precision($this->resource,$index),);$this->convert[$name]=$this->meta[$name]['type'];}}}
if(!defined('DIBI'))die();class DibiSqliteDriver extends DibiDriver{private$conn,$insertId=FALSE,$affectedRows=FALSE;public$formats=array('NULL'=>"NULL",'TRUE'=>"1",'FALSE'=>"0",'date'=>"'Y-m-d'",'datetime'=>"'Y-m-d H:i:s'",);public static function connect($config){if(!extension_loaded('sqlite'))return new DibiException("PHP extension 'sqlite' is not loaded");if(empty($config['database']))return new DibiException("Database must be specified");$errorMsg='';if(empty($config['persistent']))$conn=@sqlite_open($config['database'],@$config['mode'],$errorMsg);else$conn=@sqlite_popen($config['database'],@$config['mode'],$errorMsg);if(!$conn)return new DibiException("Connecting error",array('message'=>$errorMsg,));$obj=new self($config);$obj->conn=$conn;return$obj;}public function query($sql){$this->insertId=$this->affectedRows=FALSE;$errorMsg='';$res=@sqlite_query($this->conn,$sql,SQLITE_ASSOC,$errorMsg);if($res===FALSE)return new DibiException("Query error",array('message'=>$errorMsg,'sql'=>$sql,));if(is_resource($res))return new DibiSqliteResult($res);$this->affectedRows=sqlite_changes($this->conn);if($this->affectedRows<0)$this->affectedRows=FALSE;$this->insertId=sqlite_last_insert_rowid($this->conn);if($this->insertId<1)$this->insertId=FALSE;return TRUE;}public function affectedRows(){return$this->affectedRows;}public function insertId(){return$this->insertId;}public function begin(){return sqlite_query($this->conn,'BEGIN');}public function commit(){return sqlite_query($this->conn,'COMMIT');}public function rollback(){return sqlite_query($this->conn,'ROLLBACK');}public function escape($value,$appendQuotes=FALSE){return$appendQuotes?"'".sqlite_escape_string($value)."'":sqlite_escape_string($value);}public function quoteName($value){return$value;}public function getMetaData(){trigger_error('Meta is not implemented yet.',E_USER_WARNING);}}class DibiSqliteResult extends DibiResult{private$resource,$meta;public function __construct($resource){$this->resource=$resource;}public function rowCount(){return sqlite_num_rows($this->resource);}protected function doFetch(){return sqlite_fetch_array($this->resource,SQLITE_ASSOC);}public function seek($row){return sqlite_seek($this->resource,$row);}protected function free(){}public function getFields(){if($this->meta===NULL)$this->createMeta();return array_keys($this->meta);}protected function detectTypes(){if($this->meta===NULL)$this->createMeta();}public function getMetaData($field){if($this->meta===NULL)$this->createMeta();return isset($this->meta[$field])?$this->meta[$field]:FALSE;}private function createMeta(){$count=sqlite_num_fields($this->resource);$this->meta=$this->convert=array();for($index=0;$index<$count;$index++){$name=sqlite_field_name($this->resource,$index);$this->meta[$name]=array('type'=>dibi::FIELD_UNKNOWN);$this->convert[$name]=dibi::FIELD_UNKNOWN;}}}?>

View File

@@ -1,7 +0,0 @@
limit/offset support for select?
complete phpdoc
PostgreSql driver
table prefix suppor

View File

@@ -1,433 +1,3 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* For PHP 5.0.3 or newer
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version 0.6b $Revision$ $Date$
*/
define('DIBI', 'Version 0.6b $Revision$');
if (version_compare(PHP_VERSION , '5.0.3', '<'))
die('dibi needs PHP 5.0.3 or newer');
// libraries
require_once dirname(__FILE__).'/libs/driver.php';
require_once dirname(__FILE__).'/libs/resultset.php';
require_once dirname(__FILE__).'/libs/parser.php';
require_once dirname(__FILE__).'/libs/exception.php';
// required since PHP 5.1.0
if (function_exists('date_default_timezone_set'))
date_default_timezone_set('Europe/Prague'); // or 'GMT'
/**
* Interface for user variable, used for generating SQL
*/
interface IDibiVariable
{
/**
* Format for SQL
*
* @param object destination DibiDriver
* @param string optional modifier
* @return string SQL code
*/
public function toSQL($driver, $modifier = NULL);
}
/**
* Interface for database drivers
*
* This class is static container class for creating DB objects and
* store debug & connections info.
*
*/
class dibi
{
/**
* Column type in relation to PHP native type
*/
const
FIELD_TEXT = 's', // as 'string'
FIELD_BINARY = 'S',
FIELD_BOOL = 'b',
FIELD_INTEGER = 'i',
FIELD_FLOAT = 'f',
FIELD_DATE = 'd',
FIELD_DATETIME = 't',
FIELD_UNKNOWN = '?',
// special
FIELD_COUNTER = 'c'; // counter or autoincrement, is integer
/**
* Connection registry storage for DibiDriver objects
* @var array
*/
static private $registry = array();
/**
* Current connection
* @var object DibiDriver
*/
static private $conn;
/**
* Arguments -> SQL parser
* @var object DibiParser
*/
static private $parser;
/**
* Last SQL command @see dibi::query()
* @var string
*/
static public $sql;
static public $error;
/**
* File for logging SQL queryies - strongly recommended to use with NSafeStream
* @var string|NULL
*/
static public $logFile;
static public $logMode = 'w';
/**
* Enable/disable debug mode
* @var bool
*/
static public $debug = false;
/**
* Creates a new DibiDriver object and connects it to specified database
*
* @param array|string connection parameters
* @param string connection name
* @return bool|object TRUE on success, FALSE or Exception on failure
*/
static public function connect($config, $name = 'def')
{
// init parser
if (!self::$parser) self::$parser = new DibiParser();
// DSN string
if (is_string($config))
parse_str($config, $config);
// config['driver'] is required
if (empty($config['driver']))
return new DibiException('Driver is not specified.');
// include dibi driver
$className = "Dibi$config[driver]Driver";
require_once dirname(__FILE__) . "/drivers/$config[driver].php";
if (!class_exists($className))
return new DibiException("Unable to create instance of dibi driver class '$className'.");
// create connection object
/** like $conn = $className::connect($config); */
$conn = call_user_func(array($className, 'connect'), $config);
// optionally log to file
// todo: log other exceptions!
if (self::$logFile != NULL && self::$logMode) {
if (is_error($conn))
$msg = "Can't connect to DB '$config[driver]': ".$conn->getMessage();
else
$msg = "Successfully connected to DB '$config[driver]'";
$f = fopen(self::$logFile, self::$logMode);
fwrite($f, "$msg\r\n\r\n");
fclose($f);
}
if (is_error($conn)) {
// optionally debug on display
if (self::$debug) echo '[dibi error] ' . $conn->getMessage();
return $conn; // reraise the exception
}
// store connection in list
self::$conn = self::$registry[$name] = $conn;
return TRUE;
}
/**
* Returns TRUE when connection was established
*
* @return bool
*/
static public function isConnected()
{
return (bool) self::$conn;
}
/**
* Retrieve active connection
*
* @param string connection registy name or NULL for active connection
* @return object DibiDriver object.
*/
static public function getConnection($name = NULL)
{
return NULL === $name
? self::$conn
: @self::$registry[$name];
}
/**
* Change active connection
*
* @param string connection registy name
* @return void
*/
static public function activate($name)
{
if (!isset(self::$registry[$name]))
return FALSE;
// change active connection
self::$conn = self::$registry[$name];
return TRUE;
}
/**
* Generates and executes SQL query
*
* @param array|mixed one or more arguments
* @return int|DibiResult|Exception
*/
static public function query($args)
{
if (!self::$conn) return new DibiException('Dibi is not connected to DB'); // is connected?
// receive arguments
if (!is_array($args))
$args = func_get_args();
// and generate SQL
self::$sql = self::$parser->parse(self::$conn, $args);
if (is_error(self::$sql)) return self::$sql; // reraise the exception
// execute SQL
$timer = -microtime(true);
$res = self::$conn->query(self::$sql);
$timer += microtime(true);
if (is_error($res)) {
// optionally debug on display
if (self::$debug) {
echo '[dibi error] ' . $res->getMessage();
self::dump(self::$sql);
}
// todo: log all errors!
self::$error = $res;
} else {
self::$error = FALSE;
}
// optionally log to file
if (self::$logFile != NULL)
{
if (is_error($res))
$msg = $res->getMessage();
elseif ($res instanceof DibiResult)
$msg = 'object('.get_class($res).') rows: '.$res->rowCount();
else
$msg = 'OK';
$f = fopen(self::$logFile, 'a');
fwrite($f,
self::$sql
. ";\r\n-- Result: $msg"
. "\r\n-- Takes: " . sprintf('%0.3f', $timer * 1000) . ' ms'
. "\r\n\r\n"
);
fclose($f);
}
return $res;
}
/**
* Generates and returns SQL query
*
* @param array|mixed one or more arguments
* @return string
*/
static public function test($args)
{
if (!self::$conn) return FALSE; // is connected?
// receive arguments
if (!is_array($args))
$args = func_get_args();
// and generate SQL
$sql = self::$parser->parse(self::$conn, $args);
$dump = TRUE; // !!!
if ($dump) {
if (is_error($sql))
self::dump($sql->getSql());
else
self::dump($sql);
}
return $sql;
}
/**
* Monostate for DibiDriver::insertId()
*
* @return int
*/
static public function insertId()
{
if (!self::$conn) return FALSE; // is connected?
return self::$conn->insertId();
}
/**
* Monostate for DibiDriver::affectedRows()
*
* @return int
*/
static public function affectedRows()
{
if (!self::$conn) return FALSE; // is connected?
return self::$conn->affectedRows();
}
static private function dumpHighlight($matches)
{
if (!empty($matches[1])) // comment
return '<em style="color:gray">'.$matches[1].'</em>';
if (!empty($matches[2])) // error
return '<strong style="color:red">'.$matches[2].'</strong>';
if (!empty($matches[3])) // most important keywords
return '<strong style="color:blue">'.$matches[3].'</strong>';
if (!empty($matches[4])) // other keywords
return '<strong style="color:green">'.$matches[4].'</strong>';
}
/**
* Prints out a syntax highlighted version of the SQL command
*
* @param string SQL command
* @return void
*/
static public function dump($sql) {
static $keywords2 = 'ALL|DISTINCT|AS|ON|INTO|AND|OR|AS';
static $keywords1 = 'SELECT|UPDATE|INSERT|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN';
// insert new lines
$sql = preg_replace("#\\b(?:$keywords1)\\b#", "\n\$0", $sql);
$sql = trim($sql);
// reduce spaces
// $sql = preg_replace('# +#', ' ', $sql);
$sql = wordwrap($sql, 100);
$sql = htmlSpecialChars($sql);
$sql = strtr($sql, array("\n" => '<br />'));
// syntax highlight
$sql = preg_replace_callback("#(/\*.+?\*/)|(\*\*.+?\*\*)|\\b($keywords1)\\b|\\b($keywords2)\\b#", array('dibi', 'dumpHighlight'), $sql);
echo '<pre class="dibi">', $sql, '</pre>';
}
/**
* Displays complete result-set as HTML table
*
* @param object DibiResult
* @return void
*/
static public function dumpResult(DibiResult $res)
{
echo '<table class="dump"><tr>';
echo '<th>Row</th>';
$fieldCount = $res->fieldCount();
for ($i = 0; $i < $fieldCount; $i++) {
$info = $res->fieldMeta($i);
echo '<th>'.htmlSpecialChars($info['name']).'</th>';
}
echo '</tr>';
foreach ($res as $row => $fields) {
echo '<tr><th>', $row, '</th>';
foreach ($fields as $field) {
if (is_object($field)) $field = $field->__toString();
echo '<td>', htmlSpecialChars($field), '</td>';
}
echo '</tr>';
}
echo '</table>';
}
} // class dibi
?>
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php';

View File

@@ -1,341 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* The dibi driver for MySQL database
*
*/
class DibiMySqlDriver extends DibiDriver {
private
$conn,
$insertId = FALSE,
$affectedRows = FALSE;
public
$formats = array(
'NULL' => "NULL",
'TRUE' => "1",
'FALSE' => "0",
'date' => "'Y-m-d'",
'datetime' => "'Y-m-d H:i:s'",
);
/**
* Driver factory
*/
public static function connect($config)
{
if (!extension_loaded('mysql'))
return new DibiException("PHP extension 'mysql' is not loaded");
if (empty($config['host'])) $config['host'] = 'localhost';
if (@$config['protocol'] === 'unix') // host can be socket
$host = ':' . $config['host'];
else
$host = $config['host'] . (empty($config['port']) ? '' : $config['port']);
// some errors aren't handled. Must use $php_errormsg
if (function_exists('ini_set'))
$save = ini_set('track_errors', TRUE);
$php_errormsg = '';
if (empty($config['persistent']))
$conn = @mysql_connect($host, @$config['username'], @$config['password']);
else
$conn = @mysql_pconnect($host, @$config['username'], @$config['password']);
if (function_exists('ini_set'))
ini_set('track_errors', $save);
if (!is_resource($conn))
return new DibiException("Connecting error", array(
'message' => mysql_error() ? mysql_error() : $php_errormsg,
'code' => mysql_errno(),
));
if (!empty($config['charset'])) {
$succ = @mysql_query('SET CHARACTER SET '.$config['charset'], $conn);
// don't handle this error...
}
if (!empty($config['database'])) {
if (!@mysql_select_db($config['database'], $conn))
return new DibiException("Connecting error", array(
'message' => mysql_error($conn),
'code' => mysql_errno($conn),
));
}
$obj = new self($config);
$obj->conn = $conn;
return $obj;
}
public function query($sql)
{
$this->insertId = $this->affectedRows = FALSE;
$res = @mysql_query($sql, $this->conn);
if (is_resource($res))
return new DibiMySqlResult($res);
if ($res === FALSE)
return new DibiException("Query error", array(
'message' => mysql_error($this->conn),
'code' => mysql_errno($this->conn),
'sql' => $sql,
));
$this->affectedRows = mysql_affected_rows($this->conn);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
$this->insertId = mysql_insert_id($this->conn);
if ($this->insertId < 1) $this->insertId = FALSE;
return TRUE;
}
public function affectedRows()
{
return $this->affectedRows;
}
public function insertId()
{
return $this->insertId;
}
public function begin()
{
return mysql_query('BEGIN', $this->conn);
}
public function commit()
{
return mysql_query('COMMIT', $this->conn);
}
public function rollback()
{
return mysql_query('ROLLBACK', $this->conn);
}
public function escape($value, $appendQuotes = FALSE)
{
return $appendQuotes
? "'" . mysql_real_escape_string($value, $this->conn) . "'"
: mysql_real_escape_string($value, $this->conn);
}
public function quoteName($value)
{
return '`' . strtr($value, array('.' => '`.`')) . '`';
}
public function getMetaData()
{
trigger_error('Meta is not implemented yet.', E_USER_WARNING);
}
/*
// is this really needed?
public function getResource()
{
return $this->conn;
}
// experimental
public function applyLimit(&$sql, $offset, $limit)
{
if ($limit > 0) {
$sql .= " LIMIT " . (int) $limit . ($offset > 0 ? " OFFSET " . (int) $offset : "");
} elseif ($offset > 0) {
$sql .= " LIMIT " . $offset . ", 18446744073709551615";
}
}
*/
} // DibiMySqlDriver
class DibiMySqlResult extends DibiResult
{
private
$resource,
$meta;
public function __construct($resource)
{
$this->resource = $resource;
}
public function rowCount()
{
return mysql_num_rows($this->resource);
}
protected function doFetch()
{
return mysql_fetch_assoc($this->resource);
}
public function seek($row)
{
return mysql_data_seek($this->resource, $row);
}
protected function free()
{
mysql_free_result($this->resource);
}
public function getFields()
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return array_keys($this->meta);
}
protected function detectTypes()
{
if ($this->meta === NULL)
$this->createMeta();
}
/** this is experimental */
public function getMetaData($field)
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return isset($this->meta[$field]) ? $this->meta[$field] : FALSE;
}
/** this is experimental */
private function createMeta()
{
static $types = array(
'ENUM' => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER
'SET' => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER
'CHAR' => dibi::FIELD_TEXT,
'VARCHAR' => dibi::FIELD_TEXT,
'STRING' => dibi::FIELD_TEXT,
'TINYTEXT' => dibi::FIELD_TEXT,
'TEXT' => dibi::FIELD_TEXT,
'MEDIUMTEXT'=> dibi::FIELD_TEXT,
'LONGTEXT' => dibi::FIELD_TEXT,
'BINARY' => dibi::FIELD_BINARY,
'VARBINARY' => dibi::FIELD_BINARY,
'TINYBLOB' => dibi::FIELD_BINARY,
'BLOB' => dibi::FIELD_BINARY,
'MEDIUMBLOB'=> dibi::FIELD_BINARY,
'LONGBLOB' => dibi::FIELD_BINARY,
'DATE' => dibi::FIELD_DATE,
'DATETIME' => dibi::FIELD_DATETIME,
'TIMESTAMP' => dibi::FIELD_DATETIME,
'TIME' => dibi::FIELD_DATETIME,
'BIT' => dibi::FIELD_BOOL,
'YEAR' => dibi::FIELD_INTEGER,
'TINYINT' => dibi::FIELD_INTEGER,
'SMALLINT' => dibi::FIELD_INTEGER,
'MEDIUMINT' => dibi::FIELD_INTEGER,
'INT' => dibi::FIELD_INTEGER,
'INTEGER' => dibi::FIELD_INTEGER,
'BIGINT' => dibi::FIELD_INTEGER,
'FLOAT' => dibi::FIELD_FLOAT,
'DOUBLE' => dibi::FIELD_FLOAT,
'REAL' => dibi::FIELD_FLOAT,
'DECIMAL' => dibi::FIELD_FLOAT,
'NUMERIC' => dibi::FIELD_FLOAT,
);
$count = mysql_num_fields($this->resource);
$this->meta = $this->convert = array();
for ($index = 0; $index < $count; $index++) {
$info['native'] = $native = strtoupper(mysql_field_type($this->resource, $index));
$info['flags'] = explode(' ', mysql_field_flags($this->resource, $index));
$info['length'] = mysql_field_len($this->resource, $index);
$info['table'] = mysql_field_table($this->resource, $index);
if (in_array('auto_increment', $info['flags'])) // or 'primary_key' ?
$info['type'] = dibi::FIELD_COUNTER;
else {
$info['type'] = isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN;
// if ($info['type'] == dibi::FIELD_TEXT && $info['length'] > 255)
// $info['type'] = dibi::FIELD_LONG_TEXT;
}
$name = mysql_field_name($this->resource, $index);
$this->meta[$name] = $info;
$this->convert[$name] = $info['type'];
}
}
} // class DibiMySqlResult
?>

View File

@@ -1,289 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* The dibi driver for MySQLi database
*
*/
class DibiMySqliDriver extends DibiDriver {
private
$conn,
$insertId = FALSE,
$affectedRows = FALSE;
public
$formats = array(
'NULL' => "NULL",
'TRUE' => "1",
'FALSE' => "0",
'date' => "'Y-m-d'",
'datetime' => "'Y-m-d H:i:s'",
);
public static function connect($config)
{
if (!extension_loaded('mysqli'))
return new DibiException("PHP extension 'mysqli' is not loaded");
if (empty($config['host'])) $config['host'] = 'localhost';
$conn = @mysqli_connect($config['host'], @$config['username'], @$config['password'], @$config['database'], @$config['port']);
if (!$conn)
return new DibiException("Connecting error", array(
'message' => mysqli_connect_error(),
'code' => mysqli_connect_errno(),
));
if (!empty($config['charset']))
mysqli_query($conn, 'SET CHARACTER SET '.$config['charset']);
$obj = new self($config);
$obj->conn = $conn;
return $obj;
}
public function query($sql)
{
$this->insertId = $this->affectedRows = FALSE;
$res = @mysqli_query($this->conn, $sql);
if (is_object($res))
return new DibiMySqliResult($res);
if ($res === FALSE)
return new DibiException("Query error", $this->errorInfo($sql));
$this->affectedRows = mysqli_affected_rows($this->conn);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
$this->insertId = mysqli_insert_id($this->conn);
if ($this->insertId < 1) $this->insertId = FALSE;
return TRUE;
}
public function affectedRows()
{
return $this->affectedRows;
}
public function insertId()
{
return $this->insertId;
}
public function begin()
{
return mysqli_autocommit($this->conn, FALSE);
}
public function commit()
{
$ok = mysqli_commit($this->conn);
mysqli_autocommit($this->conn, TRUE);
return $ok;
}
public function rollback()
{
$ok = mysqli_rollback($this->conn);
mysqli_autocommit($this->conn, TRUE);
return $ok;
}
private function errorInfo($sql = NULL)
{
return array(
'message' => mysqli_error($this->conn),
'code' => mysqli_errno($this->conn),
'sql' => $sql,
);
}
public function escape($value, $appendQuotes = FALSE)
{
return $appendQuotes
? "'" . mysqli_real_escape_string($this->conn, $value) . "'"
: mysqli_real_escape_string($this->conn, $value);
}
public function quoteName($value)
{
return '`' . strtr($value, array('.' => '`.`')) . '`';
}
public function getMetaData()
{
trigger_error('Meta is not implemented yet.', E_USER_WARNING);
}
} // class DibiMySqliDriver
class DibiMySqliResult extends DibiResult
{
private
$resource,
$meta;
public function __construct($resource)
{
$this->resource = $resource;
}
public function rowCount()
{
return mysqli_num_rows($this->resource);
}
protected function doFetch()
{
return mysqli_fetch_assoc($this->resource);
}
public function seek($row)
{
return mysqli_data_seek($this->resource, $row);
}
protected function free()
{
mysqli_free_result($this->resource);
}
public function getFields()
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return array_keys($this->meta);
}
protected function detectTypes()
{
if ($this->meta === NULL)
$this->createMeta();
}
/** this is experimental */
public function getMetaData($field)
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return isset($this->meta[$field]) ? $this->meta[$field] : FALSE;
}
/** this is experimental */
private function createMeta()
{
static $types = array(
MYSQLI_TYPE_FLOAT => dibi::FIELD_FLOAT,
MYSQLI_TYPE_DOUBLE => dibi::FIELD_FLOAT,
MYSQLI_TYPE_DECIMAL => dibi::FIELD_FLOAT,
// MYSQLI_TYPE_NEWDECIMAL=> dibi::FIELD_FLOAT,
// MYSQLI_TYPE_BIT => dibi::FIELD_INTEGER,
MYSQLI_TYPE_TINY => dibi::FIELD_INTEGER,
MYSQLI_TYPE_SHORT => dibi::FIELD_INTEGER,
MYSQLI_TYPE_LONG => dibi::FIELD_INTEGER,
MYSQLI_TYPE_LONGLONG => dibi::FIELD_INTEGER,
MYSQLI_TYPE_INT24 => dibi::FIELD_INTEGER,
MYSQLI_TYPE_YEAR => dibi::FIELD_INTEGER,
MYSQLI_TYPE_GEOMETRY => dibi::FIELD_INTEGER,
MYSQLI_TYPE_DATE => dibi::FIELD_DATE,
MYSQLI_TYPE_NEWDATE => dibi::FIELD_DATE,
MYSQLI_TYPE_TIMESTAMP => dibi::FIELD_DATETIME,
MYSQLI_TYPE_TIME => dibi::FIELD_DATETIME,
MYSQLI_TYPE_DATETIME => dibi::FIELD_DATETIME,
MYSQLI_TYPE_ENUM => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER
MYSQLI_TYPE_SET => dibi::FIELD_TEXT, // eventually dibi::FIELD_INTEGER
MYSQLI_TYPE_STRING => dibi::FIELD_TEXT,
MYSQLI_TYPE_VAR_STRING=> dibi::FIELD_TEXT,
MYSQLI_TYPE_TINY_BLOB => dibi::FIELD_BINARY,
MYSQLI_TYPE_MEDIUM_BLOB=> dibi::FIELD_BINARY,
MYSQLI_TYPE_LONG_BLOB => dibi::FIELD_BINARY,
MYSQLI_TYPE_BLOB => dibi::FIELD_BINARY,
);
$count = mysqli_num_fields($this->resource);
$this->meta = $this->convert = array();
for ($index = 0; $index < $count; $index++) {
$info = (array) mysqli_fetch_field_direct($this->resource, $index);
$native = $info['native'] = $info['type'];
if ($info['flags'] & MYSQLI_AUTO_INCREMENT_FLAG) // or 'primary_key' ?
$info['type'] = dibi::FIELD_COUNTER;
else {
$info['type'] = isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN;
// if ($info['type'] == dibi::FIELD_TEXT && $info['length'] > 255)
// $info['type'] = dibi::FIELD_LONG_TEXT;
}
$this->meta[$info['name']] = $info;
$this->convert[$info['name']] = $info['type'];
}
}
} // class DibiMySqliResult
?>

View File

@@ -1,278 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* The dibi driver interacting with databases via ODBC connections
*
*/
class DibiOdbcDriver extends DibiDriver {
private
$conn,
$affectedRows = FALSE;
public
$formats = array(
'NULL' => "NULL",
'TRUE' => "-1",
'FALSE' => "0",
'date' => "#m/d/Y#",
'datetime' => "#m/d/Y H:i:s#",
);
public static function connect($config)
{
if (!extension_loaded('odbc'))
return new DibiException("PHP extension 'odbc' is not loaded");
if (@$config['persistent'])
$conn = @odbc_pconnect($config['database'], $config['username'], $config['password']);
else
$conn = @odbc_connect($config['database'], $config['username'], $config['password']);
if (!is_resource($conn))
return new DibiException("Connecting error", array(
'message' => odbc_errormsg(),
'code' => odbc_error(),
));
$obj = new self($config);
$obj->conn = $conn;
return $obj;
}
public function query($sql)
{
$this->affectedRows = FALSE;
$res = @odbc_exec($this->conn, $sql);
if (is_resource($res))
return new DibiOdbcResult($res);
if ($res === FALSE)
return new DibiException("Query error", $this->errorInfo($sql));
$this->affectedRows = odbc_num_rows($this->conn);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
return TRUE;
}
public function affectedRows()
{
return $this->affectedRows;
}
public function insertId()
{
return FALSE;
}
public function begin()
{
return odbc_autocommit($this->conn, FALSE);
}
public function commit()
{
$ok = odbc_commit($this->conn);
odbc_autocommit($this->conn, TRUE);
return $ok;
}
public function rollback()
{
$ok = odbc_rollback($this->conn);
odbc_autocommit($this->conn, TRUE);
return $ok;
}
private function errorInfo($sql = NULL)
{
return array(
'message' => odbc_errormsg($this->conn),
'code' => odbc_error($this->conn),
'sql' => $sql,
);
}
public function escape($value, $appendQuotes = FALSE)
{
$value = str_replace("'", "''", $value);
return $appendQuotes
? "'" . $value . "'"
: $value;
}
public function quoteName($value)
{
return '[' . strtr($value, array('.' => '].[')) . ']';
}
public function getMetaData()
{
trigger_error('Meta is not implemented yet.', E_USER_WARNING);
}
} // class DibiOdbcDriver
class DibiOdbcResult extends DibiResult
{
private
$resource,
$meta,
$row = 0;
public function __construct($resource)
{
$this->resource = $resource;
}
public function rowCount()
{
// will return -1 with many drivers :-(
return odbc_num_rows($this->resource);
}
protected function doFetch()
{
return odbc_fetch_array($this->resource, $this->row++);
}
public function seek($row)
{
$this->row = $row;
}
protected function free()
{
odbc_free_result($this->resource);
}
public function getFields()
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return array_keys($this->meta);
}
protected function detectTypes()
{
if ($this->meta === NULL)
$this->createMeta();
}
/** this is experimental */
public function getMetaData($field)
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return isset($this->meta[$field]) ? $this->meta[$field] : FALSE;
}
/** this is experimental */
private function createMeta()
{
// cache
if ($this->meta !== NULL)
return $this->meta;
static $types = array(
'CHAR' => dibi::FIELD_TEXT,
'COUNTER' => dibi::FIELD_COUNTER,
'VARCHAR' => dibi::FIELD_TEXT,
'LONGCHAR' => dibi::FIELD_TEXT,
'INTEGER' => dibi::FIELD_INTEGER,
'DATETIME' => dibi::FIELD_DATETIME,
'CURRENCY' => dibi::FIELD_FLOAT,
'BIT' => dibi::FIELD_BOOL,
'LONGBINARY'=> dibi::FIELD_BINARY,
'SMALLINT' => dibi::FIELD_INTEGER,
'BYTE' => dibi::FIELD_INTEGER,
'BIGINT' => dibi::FIELD_INTEGER,
'INT' => dibi::FIELD_INTEGER,
'TINYINT' => dibi::FIELD_INTEGER,
'REAL' => dibi::FIELD_FLOAT,
'DOUBLE' => dibi::FIELD_FLOAT,
'DECIMAL' => dibi::FIELD_FLOAT,
'NUMERIC' => dibi::FIELD_FLOAT,
'MONEY' => dibi::FIELD_FLOAT,
'SMALLMONEY'=> dibi::FIELD_FLOAT,
'FLOAT' => dibi::FIELD_FLOAT,
'YESNO' => dibi::FIELD_BOOL,
// and many others?
);
$count = odbc_num_fields($this->resource);
$this->meta = $this->convert = array();
for ($index = 1; $index <= $count; $index++) {
$native = strtoupper(odbc_field_type($this->resource, $index));
$name = odbc_field_name($this->resource, $index);
$this->meta[$name] = array(
'type' => isset($types[$native]) ? $types[$native] : dibi::FIELD_UNKNOWN,
'native' => $native,
'length' => odbc_field_len($this->resource, $index),
'scale' => odbc_field_scale($this->resource, $index),
'precision' => odbc_field_precision($this->resource, $index),
);
$this->convert[$name] = $this->meta[$name]['type'];
}
}
} // class DibiOdbcResult
?>

View File

@@ -1,241 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* The dibi driver for SQlite database
*
*/
class DibiSqliteDriver extends DibiDriver {
private
$conn,
$insertId = FALSE,
$affectedRows = FALSE;
public
$formats = array(
'NULL' => "NULL",
'TRUE' => "1",
'FALSE' => "0",
'date' => "'Y-m-d'",
'datetime' => "'Y-m-d H:i:s'",
);
public static function connect($config)
{
if (!extension_loaded('sqlite'))
return new DibiException("PHP extension 'sqlite' is not loaded");
if (empty($config['database']))
return new DibiException("Database must be specified");
$errorMsg = '';
if (empty($config['persistent']))
$conn = @sqlite_open($config['database'], @$config['mode'], $errorMsg);
else
$conn = @sqlite_popen($config['database'], @$config['mode'], $errorMsg);
if (!$conn)
return new DibiException("Connecting error", array(
'message' => $errorMsg,
));
$obj = new self($config);
$obj->conn = $conn;
return $obj;
}
public function query($sql)
{
$this->insertId = $this->affectedRows = FALSE;
$errorMsg = '';
$res = @sqlite_query($this->conn, $sql, SQLITE_ASSOC, $errorMsg);
if ($res === FALSE)
return new DibiException("Query error", array(
'message' => $errorMsg,
'sql' => $sql,
));
if (is_resource($res))
return new DibiSqliteResult($res);
$this->affectedRows = sqlite_changes($this->conn);
if ($this->affectedRows < 0) $this->affectedRows = FALSE;
$this->insertId = sqlite_last_insert_rowid($this->conn);
if ($this->insertId < 1) $this->insertId = FALSE;
return TRUE;
}
public function affectedRows()
{
return $this->affectedRows;
}
public function insertId()
{
return $this->insertId;
}
public function begin()
{
return sqlite_query($this->conn, 'BEGIN');
}
public function commit()
{
return sqlite_query($this->conn, 'COMMIT');
}
public function rollback()
{
return sqlite_query($this->conn, 'ROLLBACK');
}
public function escape($value, $appendQuotes = FALSE)
{
return $appendQuotes
? "'" . sqlite_escape_string($value) . "'"
: sqlite_escape_string($value);
}
public function quoteName($value)
{
return $value;
}
public function getMetaData()
{
trigger_error('Meta is not implemented yet.', E_USER_WARNING);
}
} // class DibiSqliteDriver
class DibiSqliteResult extends DibiResult
{
private
$resource,
$meta;
public function __construct($resource)
{
$this->resource = $resource;
}
public function rowCount()
{
return sqlite_num_rows($this->resource);
}
protected function doFetch()
{
return sqlite_fetch_array($this->resource, SQLITE_ASSOC);
}
public function seek($row)
{
return sqlite_seek($this->resource, $row);
}
protected function free()
{
}
public function getFields()
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return array_keys($this->meta);
}
protected function detectTypes()
{
if ($this->meta === NULL)
$this->createMeta();
}
/** this is experimental */
public function getMetaData($field)
{
// cache
if ($this->meta === NULL)
$this->createMeta();
return isset($this->meta[$field]) ? $this->meta[$field] : FALSE;
}
/** this is experimental */
private function createMeta()
{
$count = sqlite_num_fields($this->resource);
$this->meta = $this->convert = array();
for ($index = 0; $index < $count; $index++) {
$name = sqlite_field_name($this->resource, $index);
$this->meta[$name] = array('type' => dibi::FIELD_UNKNOWN);
$this->convert[$name] = dibi::FIELD_UNKNOWN;
}
}
} // class DibiSqliteResult
?>

View File

@@ -1,164 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* dibi Common Driver
*
*/
abstract class DibiDriver
{
/**
* Current connection configuration
* @var array
*/
protected
$config;
/**
* Describes how convert some datatypes to SQL command
* @var array
*/
public $formats = array(
'NULL' => "NULL", // NULL
'TRUE' => "1", // boolean true
'FALSE' => "0", // boolean false
'date' => "'Y-m-d'", // format used by date()
'datetime' => "'Y-m-d H:i:s'", // format used by date()
);
/**
* DibiDriver factory: creates object and connects to a database
*
* @param array connect configuration
* @return bool|object DibiDriver object on success, FALSE or Exception on failure
*/
abstract static public function connect($config);
/**
* Driver initialization
*
* @param array connect configuration
*/
protected function __construct($config)
{
$this->config = $config;
}
/**
* Get the configuration descriptor used by connect() to connect to database.
* @see connect()
* @return array
*/
public function getConfig()
{
return $this->config;
}
/**
* Executes the SQL query
*
* @param string SQL statement.
* @return object|bool Result set object or TRUE on success, Exception on failure
*/
abstract public function query($sql);
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query
*
* @return int number of rows or FALSE on error
*/
abstract public function affectedRows();
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query
* @return int|bool int on success or FALSE on failure
*/
abstract public function insertId();
/**
* Begins a transaction (if supported).
*/
abstract public function begin();
/**
* Commits statements in a transaction.
*/
abstract public function commit();
/**
* Rollback changes in a transaction.
*/
abstract public function rollback();
/**
* Escapes the string
* @param string unescaped string
* @param bool quote string?
* @return string escaped and optionally quoted string
*/
abstract public function escape($value, $appendQuotes = FALSE);
/**
* Quotes SQL identifier (table's or column's name, etc.)
* @param string identifier
* @return string quoted identifier
*/
abstract public function quoteName($value);
/**
* Gets a information of the current database.
*
* @return DibiMetaData
*/
abstract public function getMetaData();
} // class DibiDriver
?>

View File

@@ -1,70 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* dibi exception class
*
*/
class DibiException extends Exception
{
private
$info;
public function __construct($message, $info=NULL) {
$this->info = $info;
if (isset($info['message']))
$message = "$message: $info[message]";
/*
if (isset($info['sql']))
$message .= "\n[SQL] $info[sql]";
*/
parent::__construct($message);
}
public function getSql()
{
return @$this->info['sql'];
}
} // class DibiException
/**
* Checks result state
*/
function is_error($var)
{
return ($var === FALSE) || ($var instanceof Exception);
}
?>

View File

@@ -1,354 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
/**
* dibi parser
*
*/
class DibiParser
{
private
$modifier,
$hasError,
$driver,
$ifLevel,
$ifLevelStart;
/**
* Generates SQL
*
* @param array
* @return string
*/
public function parse($driver, $args)
{
$this->driver = $driver;
$this->hasError = FALSE;
$command = null;
$mod = & $this->modifier; // shortcut
$mod = FALSE;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
$comment = & $this->comment;
$comment = FALSE;
// iterate
$sql = array();
foreach ($args as $arg)
{
// %if was opened
if ('if' == $mod) {
$mod = FALSE;
$this->ifLevel++;
if (!$comment && !$arg) {
// open comment
$sql[] = '/*';
$this->ifLevelStart = $this->ifLevel;
$comment = TRUE;
}
continue;
}
// simple string means SQL
if (is_string($arg) && (!$mod || 'p'==$mod)) {
$mod = FALSE;
// will generate new mod
$sql[] = $this->formatValue($arg, 'p');
continue;
}
// associative array without modifier - autoselect between SET or VALUES
if (!$mod && is_array($arg) && is_string(key($arg))) {
if (!$command)
$command = strtoupper(substr(ltrim($args[0]), 0, 6));
$mod = ('INSERT' == $command || 'REPLAC' == $command) ? 'v' : 'a';
}
// default processing
$sql[] = $comment
? '...'
: $this->formatValue($arg, $mod);
$mod = FALSE;
} // foreach
if ($comment) $sql[] = '*/';
$sql = implode(' ', $sql);
if ($this->hasError)
return new DibiException('Errors during generating SQL', array('sql' => $sql));
return $sql;
}
private function formatValue($value, $modifier)
{
// array processing (with or without modifier)
if (is_array($value)) {
$vx = $kx = array();
switch ($modifier) {
case 'a': // SET (assoc)
foreach ($value as $k => $v) {
// split into identifier & modifier
$pair = explode('%', $k, 2);
if (isset($pair[1])) {
$mod = $pair[1];
// %? skips NULLS
if (isset($mod[0]) && '?' == $mod[0]) {
if (NULL === $v) continue;
$mod = substr($mod, 1);
}
} else $mod = FALSE;
// generate array
$vx[] = $this->driver->quoteName($pair[0]) . '=' . $this->formatValue($v, $mod);
}
return implode(', ', $vx);
case 'v': // VALUES
foreach ($value as $k => $v) {
// split into identifier & modifier
$pair = explode('%', $k, 2);
if (isset($pair[1])) {
$mod = $pair[1];
// %m? skips NULLS
if (isset($mod[0]) && '?' == $mod[0]) {
if ($v === NULL) continue;
$mod = substr($mod, 1);
}
} else $mod = FALSE;
// generate arrays
$kx[] = $this->driver->quoteName($pair[0]);
$vx[] = $this->formatValue($v, $mod);
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
default: // LIST
foreach ($value as $v)
$vx[] = $this->formatValue($v, $modifier);
return implode(', ', $vx);
}
}
// with modifier procession
if ($modifier) {
if ($value instanceof IDibiVariable)
return $value->toSql($this->driver, $modifier);
if (!is_scalar($value) && !is_null($value)) { // array is already processed
$this->hasError = TRUE;
return '**Unexpected '.gettype($value).'**';
}
switch ($modifier) {
case "s": // string
return $this->driver->escape($value, TRUE);
case 'b': // boolean
return $value
? $this->driver->formats['TRUE']
: $this->driver->formats['FALSE'];
case 'i': // signed int
case 'u': // unsigned int
return (string) (int) $value;
case 'f': // float
return (string) (float) $value; // something like -9E-005 is accepted by SQL
case 'd': // date
return date($this->driver->formats['date'], is_string($value)
? strtotime($value)
: $value);
case 't': // datetime
return date($this->driver->formats['datetime'], is_string($value)
? strtotime($value)
: $value);
case 'n': // identifier name
return $this->driver->quoteName($value);
case 'p': // preserve as SQL
$value = (string) $value;
// speed-up - is regexp required?
$toSkip = strcspn($value, '`[\'"%');
if (strlen($value) == $toSkip) // needn't be translated
return $value;
// note: only this can change $this->modifier
return substr($value, 0, $toSkip)
. preg_replace_callback('/
(?=`|\[|\'|"|%) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier]
(\')((?:\'\'|[^\'])*)\'| ## 3,4) string
(")((?:""|[^"])*)"| ## 5,6) "string"
%(else|end)| ## 7) conditional SQL
%([a-zA-Z]{1,2})$| ## 8) right modifier
(\'|") ## 9) lone-quote
)/xs',
array($this, 'callback'),
substr($value, $toSkip)
);
case 'a':
case 'v':
$this->hasError = TRUE;
return "**Unexpected ".gettype($value)."**";
case 'if':
$this->hasError = TRUE;
return "**The %$modifier is not allowed here**";
default:
$this->hasError = TRUE;
return "**Unknown modifier %$modifier**";
}
}
// without modifier procession
if (is_string($value))
return $this->driver->escape($value, TRUE);
if (is_int($value) || is_float($value))
return (string) $value; // something like -9E-005 is accepted by SQL
if (is_bool($value))
return $value ? $this->driver->formats['TRUE'] : $this->driver->formats['FALSE'];
if (is_null($value))
return $this->driver->formats['NULL'];
if ($value instanceof IDibiVariable)
return $value->toSql($this->driver);
$this->hasError = TRUE;
return '**Unexpected '.gettype($value).'**';
}
/**
* PREG callback for @see self::translate()
* @param array
* @return string
*/
private function callback($matches)
{
// [1] => `ident`
// [2] => [ident]
// [3] => '
// [4] => string
// [5] => "
// [6] => string
// [7] => %else | %end
// [8] => right modifier
// [9] => lone-quote
if ($matches[1]) // SQL identifiers: `ident`
return $this->driver->quoteName($matches[1]);
if ($matches[2]) // SQL identifiers: [ident]
return $this->driver->quoteName($matches[2]);
if ($matches[3]) // SQL strings: '....'
return $this->comment
? '...'
: $this->driver->escape( strtr($matches[4], array("''" => "'")), TRUE);
if ($matches[5]) // SQL strings: "..."
return $this->comment
? '...'
: $this->driver->escape( strtr($matches[6], array('""' => '"')), TRUE);
if ($matches[7]) { // %end | %else
if (!$this->ifLevel) {
$this->hasError = TRUE;
return "**Unexpected condition $matches[8]**";
}
if ('end' == $matches[7]) {
$this->ifLevel--;
if ($this->ifLevelStart == $this->ifLevel + 1) {
// close comment
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
}
return '';
}
// else
if ($this->ifLevelStart == $this->ifLevel) {
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
} elseif (!$this->comment) {
$this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE;
return '/*';
}
}
if ($matches[8]) { // modifier
$this->modifier = $matches[8];
return '';
}
if ($matches[9]) { // string quote
$this->hasError = TRUE;
return '**Alone quote**';
}
die('this should be never executed');
}
} // class DibiParser
?>

View File

@@ -1,379 +0,0 @@
<?php
/**
* dibi - Database Abstraction Layer according to dgx
* --------------------------------------------------
*
* This source file is subject to the GNU GPL license.
*
* @author David Grudl aka -dgx- <dave@dgx.cz>
* @link http://texy.info/dibi/
* @copyright Copyright (c) 2005-2006 David Grudl
* @license GNU GENERAL PUBLIC LICENSE v2
* @package dibi
* @category Database
* @version $Revision$ $Date$
*/
// security - include dibi.php, not this file
if (!defined('DIBI')) die();
// PHP < 5.1 compatibility
if (!interface_exists('Countable', false)) {
interface Countable
{
function count();
}
}
/**
* dibi result-set abstract class
*
* <code>
* $result = dibi::query('SELECT * FROM [table]');
* $value = $result->fetchSingle();
* $all = $result->fetchAll();
* $assoc = $result->fetchAll('id');
* $assoc = $result->fetchAll('active', 'id');
* unset($result);
* </code>
*/
abstract class DibiResult implements IteratorAggregate, Countable
{
/**
* Describes columns types
* @var array
*/
protected $convert;
/**
* 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
*/
abstract public function seek($row);
/**
* Returns the number of rows in a result set
* @return int
*/
abstract public function rowCount();
/**
* Gets an array of field names
* @return array
*/
abstract public function getFields();
/**
* Gets an array of meta informations about column
* @param string column name
* @return array
*/
abstract public function getMetaData($field);
/**
* Acquires ....
* @return void
*/
abstract protected function detectTypes();
/**
* Frees the resources allocated for this result set
* @return void
*/
abstract protected function free();
/**
* Fetches the row at current position and moves the internal cursor to the next position
* internal usage only
* @return array|FALSE array() on success, FALSE if no next record
*/
abstract protected function doFetch();
/**
* Fetches the row at current position, process optional type conversion
* and moves the internal cursor to the next position
* @return array|FALSE array() on success, FALSE if no next record
*/
final public function fetch()
{
$rec = $this->doFetch();
if (!is_array($rec))
return FALSE;
// types-converting?
if ($t = $this->convert) { // little speed-up
foreach ($rec as $key => $value) {
if (isset($t[$key]))
$rec[$key] = $this->convert($value, $t[$key]);
}
}
return $rec;
}
/**
* Like fetch(), but returns only first field
* @return mixed value on success, FALSE if no next record
*/
final function fetchSingle()
{
$rec = $this->doFetch();
if (!is_array($rec))
return FALSE;
// types-converting?
if ($t = $this->convert) { // little speed-up
$value = reset($rec);
$key = key($rec);
return isset($t[$key])
? $this->convert($value, $t[$key])
: $value;
}
return reset($rec);
}
/**
* Fetches all records from table. Records , but returns only first field
* @param string associative colum [, param, ... ]
* @return array
*/
final function fetchAll()
{
@$this->seek(0);
$rec = $this->fetch();
if (!$rec)
return array(); // empty resultset
$assocBy = func_get_args();
$arr = array();
if (!$assocBy) { // no associative array
$value = count($rec) == 1 ? key($rec) : NULL;
do {
$arr[] = $value === NULL ? $rec : $rec[$value];
} while ($rec = $this->fetch());
return $arr;
}
do { // make associative arrays
foreach ($assocBy as $n => $assoc) {
$val[$n] = $rec[$assoc];
unset($rec[$assoc]);
}
foreach ($assocBy as $n => $assoc) {
if ($n == 0)
$tmp = &$arr[ $val[$n] ];
else
$tmp = &$tmp[$assoc][ $val[$n] ];
if ($tmp === NULL)
$tmp = $rec;
}
} while ($rec = $this->fetch());
return $arr;
}
/**
* Fetches all records from table like $key => $value pairs
* @return array
*/
final function fetchPairs($key, $value)
{
@$this->seek(0);
$rec = $this->fetch();
if (!$rec)
return array(); // empty resultset
$arr = array();
do {
$arr[ $rec[$key] ] = $rec[$value];
} while ($rec = $this->fetch());
return $arr;
}
/**
* Automatically frees the resources allocated for this result set
* @return void
*/
public function __destruct()
{
@$this->free();
}
public function setType($field, $type = NULL)
{
if ($field === TRUE)
$this->detectTypes();
elseif (is_array($field))
$this->convert = $field;
else
$this->convert[$field] = $type;
}
/** is this needed? */
public function getType($field)
{
return isset($this->convert[$field]) ? $this->convert[$field] : NULL;
}
public function convert($value, $type)
{
if ($value === NULL || $value === FALSE)
return $value;
static $conv = array(
dibi::FIELD_TEXT => 'string',
dibi::FIELD_BINARY => 'string',
dibi::FIELD_BOOL => 'bool',
dibi::FIELD_INTEGER => 'int',
dibi::FIELD_FLOAT => 'float',
dibi::FIELD_COUNTER => 'int',
);
if (isset($conv[$type])) {
settype($value, $conv[$type]);
return $value;
}
if ($type == dibi::FIELD_DATE)
return strtotime($value); // !!! not good
if ($type == dibi::FIELD_DATETIME)
return strtotime($value); // !!! not good
return $value;
}
/** these are the required IteratorAggregate functions */
public function getIterator($offset = NULL, $count = NULL)
{
return new DibiResultIterator($this, $offset, $count);
}
/** end required IteratorAggregate functions */
/** these are the required Countable functions */
public function count()
{
return $this->rowCount();
}
/** end required Countable functions */
} // class DibiResult
/**
* Basic Result set iterator.
*
* This can be returned by DibiResult::getIterator() method or directly using foreach:
* <code>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $fields) {
* print_r($fields);
* }
* unset($result);
* </code>
*
* Optionally you can specify offset and limit:
* <code>
* foreach ($result->getIterator(2, 3) as $fields) {
* print_r($fields);
* }
* </code>
*/
class DibiResultIterator implements Iterator
{
private
$result,
$offset,
$count,
$record,
$row;
public function __construct(DibiResult $result, $offset = NULL, $count = NULL)
{
$this->result = $result;
$this->offset = (int) $offset;
$this->count = $count === NULL ? 2147483647 /*PHP_INT_MAX till 5.0.5 */ : (int) $count;
}
/** these are the required Iterator functions */
public function rewind()
{
$this->row = 0;
@$this->result->seek($this->offset);
$this->record = $this->result->fetch();
}
public function key()
{
return $this->row;
}
public function current()
{
return $this->record;
}
public function next()
{
$this->record = $this->result->fetch();
$this->row++;
}
public function valid()
{
return is_array($this->record) && ($this->row < $this->count);
}
/** end required Iterator functions */
} // class DibiResultIterator
?>

4
examples/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
_test.bat
ref
output
log

View File

@@ -1,50 +0,0 @@
<?php
require_once '../dibi/dibi.php';
// using DSN
$state = dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=utf8');
// connects to mysql
$state = dibi::connect(array(
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'database' => 'test',
'charset' => 'utf8',
));
/* connects to ODBC
dibi::connect(array(
'driver' => 'odbc',
'username' => 'root',
'password' => '***',
'database' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq=C:\\Database.mdb',
));
*/
// check status
if (!dibi::isConnected()) {
echo 'dibi::isConnected(): Not connected';
echo "<br>\n";
}
// or checked status this way
if (is_error($state)) {
// $state can be FALSE or Exception
if ($state instanceof Exception)
echo $state;
else
echo 'FALSE';
echo "<br>\n";
}
?>

View File

@@ -0,0 +1,161 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Connecting to Databases | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
// connects to SQlite using dibi class
echo '<p>Connecting to Sqlite: ';
try {
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to SQlite using Dibi\Connection object
echo '<p>Connecting to Sqlite: ';
try {
$connection = new Dibi\Connection([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to MySQL using DSN
echo '<p>Connecting to MySQL: ';
try {
dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=cp1250');
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to MySQLi using array
echo '<p>Connecting to MySQLi: ';
try {
dibi::connect([
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
'database' => 'dibi',
'options' => [
MYSQLI_OPT_CONNECT_TIMEOUT => 30,
],
'flags' => MYSQLI_CLIENT_COMPRESS,
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to ODBC
echo '<p>Connecting to ODBC: ';
try {
dibi::connect([
'driver' => 'odbc',
'username' => 'root',
'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.__DIR__.'/data/sample.mdb',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to PostgreSql
echo '<p>Connecting to PostgreSql: ';
try {
dibi::connect([
'driver' => 'postgre',
'string' => 'host=localhost port=5432 dbname=mary',
'persistent' => TRUE,
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to PDO
echo '<p>Connecting to Sqlite via PDO: ';
try {
dibi::connect([
'driver' => 'pdo',
'dsn' => 'sqlite::memory:',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to MS SQL
echo '<p>Connecting to MS SQL: ';
try {
dibi::connect([
'driver' => 'mssql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to SQLSRV
echo '<p>Connecting to Microsoft SQL Server: ';
try {
dibi::connect([
'driver' => 'sqlsrv',
'host' => '(local)',
'username' => 'Administrator',
'password' => 'xxx',
'database' => 'main',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to Oracle
echo '<p>Connecting to Oracle: ';
try {
dibi::connect([
'driver' => 'oracle',
'username' => 'root',
'password' => 'xxx',
'database' => 'db',
]);
echo 'OK';
} catch (Dibi\Exception $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";

BIN
examples/data/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

BIN
examples/data/sample.mdb Normal file

Binary file not shown.

BIN
examples/data/sample.s3db Normal file

Binary file not shown.

64
examples/data/style.css Normal file
View File

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

View File

@@ -0,0 +1,49 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Database Reflection | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// retrieve database reflection
$database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->getName()}'</h2>\n";
echo "<ul>\n";
foreach ($database->getTables() as $table) {
echo '<li>', ($table->isView() ? 'view' : 'table') . " {$table->getName()}</li>\n";
}
echo "</ul>\n";
// table reflection
$table = $database->getTable('products');
echo "<h2>Table '{$table->getName()}'</h2>\n";
echo "Columns\n";
echo "<ul>\n";
foreach ($table->getColumns() as $column) {
echo "<li>{$column->getName()} <i>{$column->getNativeType()}</i> <code>{$column->getDefault()}</code></li>\n";
}
echo "</ul>\n";
echo 'Indexes';
echo "<ul>\n";
foreach ($table->getIndexes() as $index) {
echo "<li>{$index->getName()} " . ($index->isPrimary() ? 'primary ' : '') . ($index->isUnique() ? 'unique' : '') . ' (';
foreach ($index->getColumns() as $column) {
echo $column->getName(), ', ';
}
echo ")</li>\n";
}
echo "</ul>\n";

View File

@@ -1,76 +0,0 @@
<?php
require_once '../dibi/dibi.php';
/**
* Pseudotype for UNIX timestamp representation
*/
class TDateTime implements IDibiVariable
{
/**
* Unix timestamp
* @var int
*/
protected $time;
public function __construct($time = NULL)
{
if ($time === NULL)
$this->time = time(); // current time
elseif (is_string($time))
$this->time = strtotime($time); // try convert to timestamp
else
$this->time = (int) $time;
}
/**
* Format for SQL
*
* @param object destination DibiDriver
* @param string optional modifier
* @return string
*/
public function toSQL($driver, $modifier = NULL)
{
return date(
$driver->formats['datetime'], // format according to driver's spec.
$this->time
);
}
}
// connects to mysqli
dibi::connect(array(
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'charset' => 'utf8',
));
// generate and dump SQL
dibi::test("
INSERT INTO [test]", array(
'A' => 12,
'B' => NULL,
'C' => new TDateTime(31542), // using out class
'D' => 'any string',
));
?>

View File

@@ -0,0 +1,32 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Dumping SQL and Result Set | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
$res = dibi::query('
SELECT * FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo '<h2>dibi::dump()</h2>';
// dump last query (dibi::$sql)
dibi::dump();
// dump result table
echo '<h2>Dibi\Result::dump()</h2>';
$res->dump();

View File

@@ -1,53 +0,0 @@
<pre>
<?php
require_once '../dibi/dibi.php';
dibi::$debug = true;
// mysql
dibi::connect(array(
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'database' => 'test',
'charset' => 'utf8',
));
if (!dibi::isConnected())
die('Not connected');
$res = dibi::query('SELECT * FROM table');
// fetch a single value
$value = $res->fetchSingle();
// fetch complete result set
$all = $res->fetchAll();
// fetch complete result set like association array
$assoc = $res->fetchAll('id');
$assoc = $res->fetchAll('id', 'id2');
// fetch complete result set like pairs key => value
$pairs = $res->fetchPairs('id', 'name');
// fetch row by row
foreach ($res as $row => $fields) {
print_r($fields);
}
// fetch row by row with defined offset and limit
foreach ($res->getIterator(2, 3) as $row => $fields) {
print_r($fields);
}
?>

View File

@@ -0,0 +1,92 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
Tracy\Debugger::enable();
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
/*
TABLE products
product_id | title
-----------+----------
1 | Chair
2 | Table
3 | Computer
*/
// fetch a single row
echo "<h2>fetch()</h2>\n";
$row = dibi::fetch('SELECT title FROM products');
Tracy\Dumper::dump($row); // Chair
// fetch a single value
echo "<h2>fetchSingle()</h2>\n";
$value = dibi::fetchSingle('SELECT title FROM products');
Tracy\Dumper::dump($value); // Chair
// fetch complete result set
echo "<h2>fetchAll()</h2>\n";
$all = dibi::fetchAll('SELECT * FROM products');
Tracy\Dumper::dump($all);
// fetch complete result set like association array
echo "<h2>fetchAssoc('title')</h2>\n";
$res = dibi::query('SELECT * FROM products');
$assoc = $res->fetchAssoc('title'); // key
Tracy\Dumper::dump($assoc);
// fetch complete result set like pairs key => value
echo "<h2>fetchPairs('product_id', 'title')</h2>\n";
$res = dibi::query('SELECT * FROM products');
$pairs = $res->fetchPairs('product_id', 'title');
Tracy\Dumper::dump($pairs);
// fetch row by row
echo "<h2>using foreach</h2>\n";
$res = dibi::query('SELECT * FROM products');
foreach ($res as $n => $row) {
Tracy\Dumper::dump($row);
}
// more complex association array
$res = dibi::query('
SELECT *
FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo "<h2>fetchAssoc('name|title')</h2>\n";
$assoc = $res->fetchAssoc('name|title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('name[]title')</h2>\n";
$res = dibi::query('SELECT * FROM products INNER JOIN orders USING (product_id) INNER JOIN customers USING (customer_id)');
$assoc = $res->fetchAssoc('name[]title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('name->title')</h2>\n";
$res = dibi::query('SELECT * FROM products INNER JOIN orders USING (product_id) INNER JOIN customers USING (customer_id)');
$assoc = $res->fetchAssoc('name->title'); // key
Tracy\Dumper::dump($assoc);

View File

@@ -0,0 +1,18 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Importing SQL Dump from File | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
$count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz');
echo 'Number of SQL commands:', $count;

View File

@@ -1,14 +0,0 @@
Successfully connected to DB 'mysql'
SELECT * FROM `nucleus_item` WHERE `inumber` = 38;
-- Result: Query error: Table 'test.nucleus_item' doesn't exist
-- Takes: 1.357 ms
SELECT * FROM `nucleus_item` WHERE `inumber` < 38;
-- Result: Query error: Table 'test.nucleus_item' doesn't exist
-- Takes: 2.013 ms
SELECT * FROM `*nucleus_item` WHERE `inumber` < 38;
-- Result: Query error: Can't find file: '.\test\*nucleus_item.frm' (errno: 22)
-- Takes: 75.413 ms

View File

@@ -1,32 +0,0 @@
<pre>
<?php
require_once '../dibi/dibi.php';
// enable log to this file
dibi::$logFile = 'log.sql';
// mysql
dibi::connect(array(
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'database' => 'test',
'charset' => 'utf8',
));
$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] = %i', 38);
$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] < %i', 38);
$res = dibi::query('SELECT * FROM [*nucleus_item] WHERE [inumber] < %i', 38);
?>

View File

@@ -1,35 +0,0 @@
<pre>
<?php
require_once '../dibi/dibi.php';
// mysql
dibi::connect(array(
'driver' => 'mysql',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'database' => 'test',
'charset' => 'utf8',
));
$res = dibi::query('SELECT * FROM [nucleus_item] WHERE [inumber] <> %i', 38);
if (is_error($res))
die('SQL error');
// auto-convert this field to integer
$res->setType('inumber', Dibi::FIELD_INTEGER);
$record = $res->fetch();
var_dump($record);
// auto-detect all types
$res->setType(TRUE);
$record = $res->fetch();
var_dump($record);
?>

View File

@@ -0,0 +1,61 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Query Language & Conditions | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// some variables
$cond1 = TRUE;
$cond2 = FALSE;
$foo = -1;
$bar = 2;
// conditional variable
$name = $cond1 ? 'K%' : NULL;
// if & end
dibi::test('
SELECT *
FROM customers
%if', isset($name), 'WHERE name LIKE ?', $name, '%end'
);
// -> SELECT * FROM customers WHERE name LIKE 'K%'
// if & else & (optional) end
dibi::test('
SELECT *
FROM people
WHERE id > 0
%if', ($foo > 0), 'AND foo=?', $foo, '
%else %if', ($bar > 0), 'AND bar=?', $bar, '
');
// -> SELECT * FROM people WHERE id > 0 AND bar=2
// nested condition
dibi::test('
SELECT *
FROM customers
WHERE
%if', isset($name), 'name LIKE ?', $name, '
%if', $cond2, 'AND admin=1 %end
%else 1 LIMIT 10 %end'
);
// -> SELECT * FROM customers WHERE LIMIT 10
// IF()
dibi::test('UPDATE products SET', [
'price' => ['IF(price_fixed, price, ?)', 123],
]);
// -> SELECT * FROM customers WHERE LIMIT 10

View File

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

View File

@@ -0,0 +1,48 @@
<!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`');
}
Tracy\Debugger::enable();
date_default_timezone_set('Europe/Prague');
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// using manual hints
$res = dibi::query('SELECT * FROM [customers]');
$res->setType('customer_id', Type::INTEGER)
->setType('added', Type::DATETIME)
->setFormat(Type::DATETIME, 'Y-m-d H:i:s');
Tracy\Dumper::dump($res->fetch());
// outputs:
// Dibi\Row(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)
// using auto-detection (works well with MySQL or other strictly typed databases)
$res = dibi::query('SELECT * FROM [customers]');
Tracy\Dumper::dump($res->fetch());
// outputs:
// Dibi\Row(3) {
// customer_id => 1
// name => "Dave Lister" (11)
// added => "2007-03-11 17:20:03" (19)

View File

@@ -1,67 +0,0 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<pre>
<?php
require_once '../dibi/dibi.php';
// mysql
dibi::connect(array(
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'charset' => 'utf8',
));
$arr1 = array(1, 2, 3);
$arr2 = array('one', 'two', 'three');
$arr3 = array(
'col1' => 'one',
'col2' => 'two',
'col3' => 'three',
);
$arr4 = array(
'a' => 12,
'b' => NULL,
'c%?' => NULL,
'd%t' => time(), // modifier 'T' means datetime
'e' => 'any string',
);
$arr5 = array('RAND()', '[col1] > [col2]');
dibi::test("
SELECT *
FROM [db.table]
WHERE ([test.a] LIKE %d", '1995-03-01', "
OR [b1] IN (", $arr1, ")
OR [b2] IN (%s", $arr1, ")
OR [b3] IN (", $arr2, ")
OR [b4] IN (%n", $arr3, ")
OR [b4] IN (%p", $arr5, ")
AND [c] = 'embedded '' string'
OR [d]=%i", 10.3, "
OR [true]=", true, "
OR [false]=", false, "
OR [null]=", NULL, "
LIMIT 10");
// dibi detects INSERT or REPLACE command
dibi::test("INSERT INTO [test]", $arr4);
// dibi detects UPDATE command
$n = 123;
dibi::test("UPDATE [test] SET", $arr4, " WHERE [id]=%i", $n);
// array with modifier %a - means strings
dibi::test("UPDATE [test] SET%a", $arr4, " WHERE [id]=%i", $n);
?>

View File

@@ -1,51 +0,0 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<pre>
<?php
require_once '../dibi/dibi.php';
// mysql
dibi::connect(array(
'driver' => 'mysqli',
'host' => 'localhost',
'username' => 'root',
'password' => 'xxx', // change to real password!
'charset' => 'utf8',
));
$cond1 = rand(0,2) < 1;
$cond2 = rand(0,2) < 1;
$user = $cond1 ? 'Davidek' : NULL;
dibi::test('
SELECT *
FROM [table]
%if', isset($user), 'WHERE [user]=%s', $user, '%end'
);
// last end is optional
dibi::test('
SELECT *
FROM %if', $cond1, '[one_table] %else [second_table]'
);
// nested condition
dibi::test('
SELECT *
FROM [test]
WHERE
%if', isset($user), '[user]=%s', $user, '
%if', $cond2, 'AND [admin]=1 %end
%else LIMIT 10 %end'
);
?>

View File

@@ -0,0 +1,33 @@
<!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') {
die('Install dependencies using `composer install --dev`');
}
// enable Tracy
Tracy\Debugger::enable();
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
],
]);
// add panel to debug bar
$panel = new Dibi\Bridges\Tracy\Panel;
$panel->register($connection);
// throws error because SQL is bad
dibi::query('SELECT FROM customers WHERE customer_id < ?', 38);

38
examples/tracy.php Normal file
View File

@@ -0,0 +1,38 @@
<!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') {
die('Install dependencies using `composer install --dev`');
}
// enable Tracy
Tracy\Debugger::enable();
$connection = dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
],
]);
// add panel to debug bar
$panel = new Dibi\Bridges\Tracy\Panel;
$panel->register($connection);
// query will be logged
dibi::query('SELECT 123');
// result set will be dumped
Tracy\Debugger::barDump(dibi::fetchAll('SELECT * FROM customers WHERE customer_id < ?', 38), '[customers]');

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using DateTime | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
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]', [
'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

@@ -0,0 +1,31 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
Tracy\Debugger::enable();
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// using the "prototype" to add custom method to class Dibi\Result
Dibi\Result::extensionMethod('fetchShuffle', function (Dibi\Result $obj) {
$all = $obj->fetchAll();
shuffle($all);
return $all;
});
// fetch complete result set shuffled
$res = dibi::query('SELECT * FROM [customers]');
$all = $res->fetchShuffle();
Tracy\Dumper::dump($all);

View File

@@ -0,0 +1,77 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Fluent Syntax | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
date_default_timezone_set('Europe/Prague');
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
$id = 10;
$record = [
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
];
// SELECT ...
dibi::select('product_id')->as('id')
->select('title')
->from('products')
->innerJoin('orders')->using('(product_id)')
->innerJoin('customers USING (customer_id)')
->orderBy('title')
->test();
// -> SELECT [product_id] AS [id] , [title] FROM [products] INNER JOIN [orders]
// USING (product_id) INNER JOIN customers USING (customer_id) ORDER BY [title]
// SELECT ...
echo dibi::select('title')->as('id')
->from('products')
->fetchSingle();
// -> Chair (as result of query: SELECT [title] AS [id] FROM [products])
// INSERT ...
dibi::insert('products', $record)
->setFlag('IGNORE')
->test();
// -> INSERT IGNORE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1)
// UPDATE ...
dibi::update('products', $record)
->where('product_id = ?', $id)
->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
// DELETE ...
dibi::delete('products')
->where('product_id = ?', $id)
->test();
// -> DELETE FROM [products] WHERE product_id = 10
// custom commands
dibi::command()
->update('products')
->where('product_id = ?', $id)
->set($record)
->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
dibi::command()
->truncate('products')
->test();
// -> TRUNCATE [products]

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Limit & Offset | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// no limit
dibi::test('SELECT * FROM [products]');
// -> SELECT * FROM [products]
// with limit = 2
dibi::test('SELECT * FROM [products] %lmt', 2);
// -> SELECT * FROM [products] LIMIT 2
// with limit = 2, offset = 1
dibi::test('SELECT * FROM [products] %lmt %ofs', 2, 1);
// -> SELECT * FROM [products] LIMIT 2 OFFSET 1

37
examples/using-logger.php Normal file
View File

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

View File

@@ -0,0 +1,43 @@
<?php ob_start() // needed by FirePHP ?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Profiler | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
],
]);
// execute some queries...
for ($i = 0; $i < 20; $i++) {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', $i);
}
// display output
?>
<p>Last query: <strong><?php echo dibi::$sql; ?></strong></p>
<p>Number of queries: <strong><?php echo dibi::$numOfQueries; ?></strong></p>
<p>Elapsed time for last query: <strong><?php echo sprintf('%0.3f', dibi::$elapsedTime * 1000); ?> ms</strong></p>
<p>Total elapsed time: <strong><?php echo sprintf('%0.3f', dibi::$totalTime * 1000); ?> ms</strong></p>
<br>
<p>Dibi can log to your Firebug Console. You first need to install the Firefox, Firebug and FirePHP extensions. You can install them from here:</p>
<ul>
<li>Firebug: https://addons.mozilla.org/en-US/firefox/addon/1843
<li>FirePHP: http://www.firephp.org/
</ul>

View File

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

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Transactions | dibi</h1>
<?php
require __DIR__ . '/../src/loader.php';
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
echo "<h2>Before</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows
dibi::begin();
dibi::query('INSERT INTO [products]', [
'title' => 'Test product',
]);
echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
dibi::rollback(); // or dibi::commit();
echo "<h2>After rollback</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows again

55
license.md Normal file
View File

@@ -0,0 +1,55 @@
Licenses
========
Good news! You may use Dibi under the terms of either the New BSD License
or the GNU General Public License (GPL) version 2 or 3.
The BSD License is recommended for most projects. It is easy to understand and it
places almost no restrictions on what you can do with the framework. If the GPL
fits better to your project, you can use the framework under this license.
You don't have to notify anyone which license you are using. You can freely
use Dibi in commercial projects as long as the copyright header
remains intact.
New BSD License
---------------
Copyright (c) 2004, 2014 David Grudl (https://davidgrudl.com)
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of "Dibi" nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are
disclaimed. In no event shall the copyright owner or contributors be liable for
any direct, indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused and on
any theory of liability, whether in contract, strict liability, or tort
(including negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
GNU General Public License
--------------------------
GPL licenses are very very long, so instead of including them here we offer
you URLs with full text:
- [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html)
- [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html)

View File

@@ -1,271 +0,0 @@
----------------------------------------------------------------------------------
Tento text je neofici<63>ln<6C>m p<>ekladem GNU General Public License (GNU GPL). Nebyl
vyd<EFBFBD>n nadac<61> Free Software Foundation a nevyjad<61>uje pr<70>vn<76> podstatu podm<64>nek pro
<EFBFBD><EFBFBD><EFBFBD>en<EFBFBD> softwaru pou<6F><75>vaj<61>c<EFBFBD>ho GNU GPL - tomuto <20><>elu slou<6F><75> v<>hradn<64> p<>vodn<64>
anglick<EFBFBD> verze GNU GPL. P<>esto douf<75>me, <20>e tento p<>eklad pom<6F><6D>e <20>esk<73>m <20>ten<65><6E><EFBFBD>m
l<EFBFBD>pe porozum<75>t licenci GNU GPL.
----------------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
<20>esk<73> p<>eklad verze 2, <20>erven 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Kop<6F>rov<6F>n<EFBFBD> a distribuce doslovn<76>ch kopi<70> tohoto licen<65>n<EFBFBD>ho dokumentu jsou
dovoleny komukoliv, jeho zm<7A>ny jsou v<>ak zak<61>z<EFBFBD>ny.
Preambule
Softwarov<6F> licence jsou v<>t<EFBFBD>inou navr<76>eny tak, <20>e v<>m odeb<65>raj<61> pr<70>vo svobodn<64>ho
sd<EFBFBD>len<EFBFBD> a <20>prav program<61>. Smyslem GNU General Public License je naproti tomu
zaru<EFBFBD>it svobodu ke sd<73>len<65> a <20>prav<61>m svobodn<64>ho softwaru - pro zaji<6A>t<EFBFBD>n<EFBFBD>
svobodn<EFBFBD>ho p<><70>stupu k tomuto softwaru pro v<>echny jeho u<>ivatele. Tato General
Public License se vztahuje na v<>t<EFBFBD>inu softwaru nadace Free Software Foundation a
na jak<61>koli jin<69> program, jeho<68> autor se p<>iklon<6F> k jej<65>mu pou<6F><75>v<EFBFBD>n<EFBFBD>. (N<>kter<65>
dal<EFBFBD><EFBFBD> software od Free Software Foundation je nam<61>sto toho pokryt GNU Lesser
General Public License.) M<><4D>ete ji rovn<76><6E> pou<6F><75>t pro sv<73> programy.
Pokud mluv<75>me o svobodn<64>m softwaru, m<>me na mysli svobodu, nikoliv cenu. Na<4E>e
General Public License je navr<76>ena pro zaji<6A>t<EFBFBD>n<EFBFBD> toho, <20>e m<><6D>ete svobodn<64> <20><><EFBFBD>it
kopie svobodn<64>ho softwaru (a <20><>tovat si poplatek za tuto slu<6C>bu, pokud chcete),
<EFBFBD>e obdr<64><72>te zdrojov<6F> k<>d anebo jej m<><6D>ete z<>skat, pokud ho chcete, <20>e m<><6D>ete
tento software modifikovat nebo jeho <20><>sti pou<6F><75>t v nov<6F>ch svobodn<64>ch programech;
a <20>e v<>te, <20>e tyto v<>ci sm<73>te d<>lat.
Abychom mohli va<76>e pr<70>va chr<68>nit, mus<75>me vytvo<76>it omezen<65>, kter<65> zak<61><6B><EFBFBD> komukoli
v<EFBFBD>m tato pr<70>va odep<65>rat nebo v<>s <20><>dat, abyste se t<>chto pr<70>v vzdal. Tato
omezen<EFBFBD> se prom<6F>taj<61> do jist<73>ch povinnost<73>, kter<65>m mus<75>te dost<73>t, pokud <20><><EFBFBD><EFBFBD>te
kopie doty<74>n<EFBFBD>ho softwaru anebo ho modifikujete.
Nap<61><70>klad, <20><><EFBFBD><EFBFBD>te-li kopie takov<6F>ho programu, a<> ji<6A> zdarma nebo za poplatek,
mus<EFBFBD>te poskytnout p<><70>jemc<6D>m v<>echna pr<70>va, kter<65> m<>te s<>m. Mus<75>te zaru<72>it, <20>e
p<EFBFBD><EFBFBD>jemci rovn<76><6E> dostanou anebo mohou z<>skat zdrojov<6F> k<>d. A mus<75>te jim uk<75>zat
tyto podm<64>nky, aby znali sv<73> pr<70>va.
Va<56>e pr<70>va chr<68>n<EFBFBD>me ve dvou kroc<6F>ch: (1) autorizac<61> softwaru a (2) nab<61>dkou t<>to
licence, kter<65> v<>m d<>v<EFBFBD> pr<70>voplatn<74> svolen<65> ke kop<6F>rov<6F>n<EFBFBD>, <20><><EFBFBD>en<65> a modifikaci
softwaru.
Kv<4B>li ochran<61> ka<6B>d<EFBFBD>ho autora i n<>s samotn<74>ch chceme zajistit, aby ka<6B>d<EFBFBD> ch<63>pal
skute<EFBFBD>nost, <20>e pro svobodn<64> software neplat<61> <20><>dn<64> z<>ruky. Je-li software n<>k<EFBFBD>m
jin<EFBFBD>m modifikov<6F>n a posl<73>n d<>le, chceme, aby p<><70>jemci v<>d<EFBFBD>li, <20>e to, co maj<61>,
nen<EFBFBD> origin<69>l, tak<61>e jak<61>koliv probl<62>my vnesen<65> jin<69>mi se neodraz<61> na reputaci
p<EFBFBD>vodn<EFBFBD>ch autor<6F>.
Kone<6E>n<EFBFBD>, ka<6B>d<EFBFBD> svobodn<64> program je neust<73>le ohro<72>en softwarov<6F>mi patenty.
P<EFBFBD>ejeme si zamezit nebezpe<70><65>, <20>e redistributo<74>i svobodn<64>ho programu obdr<64><72>
samostatn<EFBFBD> patentov<6F> osv<73>d<EFBFBD>en<65> a t<>m u<>in<69> program v<>zan<61>m. Abychom tomu
zamezili, deklarovali jsme, <20>e ka<6B>d<EFBFBD> patent mus<75> b<>t bu<62> vyd<79>n s t<>m, <20>e
umo<EFBFBD><EFBFBD>uje ka<6B>d<EFBFBD>mu svobodn<64> u<>it<69>, anebo nesm<73> b<>t vyd<79>n v<>bec.
P<>esn<73> ustanoven<65> a podm<64>nky pro kop<6F>rov<6F>n<EFBFBD>, <20><><EFBFBD>en<65> a modifikaci jsou uvedeny
d<EFBFBD>le.
USTANOVEN<45> A PODM<44>NKY PRO KOP<4F>ROV<4F>N<EFBFBD>, DISTRIBUCI A MODIFIKACI
0. Tato licence se vztahuje na kter<65>koliv program <20>i jin<69> d<>lo, kter<65> obsahuje
zm<EFBFBD>nku, um<75>st<73>nou v n<>m dr<64>itelem autorsk<73>ch pr<70>v, o tom, <20>e d<>lo m<><6D>e b<>t
<EFBFBD><EFBFBD><EFBFBD>eno podle ustanoven<65> GNU General Public License. V dal<61><6C>m textu znamen<65> "program"
ka<EFBFBD>d<EFBFBD> takov<6F> program nebo d<>lo a "d<>lo zalo<6C>en<65> na programu" znamen<65> bu<62> program
samotn<EFBFBD> anebo ka<6B>d<EFBFBD> jin<69> d<>lo z n<>j odvozen<65>, kter<65> podl<64>h<EFBFBD> autorsk<73>mu z<>konu:
t<EFBFBD>m se m<>n<EFBFBD> d<>lo obsahuj<75>c<EFBFBD> program nebo jeho <20><>st, bu<62> doslovn<76> anebo s
modifikacemi, pop<6F><70>pad<61> v p<>ekladu do jin<69>ho jazyka. (Nad<61>le je p<>eklad
zahrnov<EFBFBD>n bez omezen<65> pod pojem "modifikace".) Ka<4B>d<EFBFBD> u<>ivatel licence je
ozna<EFBFBD>ov<EFBFBD>n jako "vy".
Jin<EFBFBD> <20>innosti ne<6E> kop<6F>rov<6F>n<EFBFBD>, <20><><EFBFBD>en<65> a modifikace nejsou pokryty touto licenc<6E>;
sahaj<EFBFBD> mimo jej<65> r<>mec. Akt spu<70>t<EFBFBD>n<EFBFBD> programu nen<65> omezen a v<>stup z programu je
pokryt pouze tehdy, jestli<6C>e obsah v<>stupu tvo<76><6F> d<>lo zalo<6C>en<65> na programu (nez<65>visle
na tom, zda bylo vytvo<76>eno <20>innost<73> programu). Posouzen<65> platnosti p<>edchoz<6F>
v<EFBFBD>ty z<>vis<69> na tom, co program d<>l<EFBFBD>.
1. Sm<53>te kop<6F>rovat a <20><><EFBFBD>it doslovn<76> kopie zdrojov<6F>ho k<>du programu tak, jak jste
jej obdr<64>el a na libovoln<6C>m m<>diu, za p<>edpokladu, <20>e na ka<6B>d<EFBFBD> kopii viditeln<6C> a
n<EFBFBD>le<EFBFBD>it<EFBFBD> zve<76>ejn<6A>te zm<7A>nku o autorsk<73>ch pr<70>vech a absenci z<>ruky; ponech<63>te
nedot<EFBFBD>en<EFBFBD> v<>echny zm<7A>nky vztahuj<75>c<EFBFBD> se k t<>to licenci a k absenci z<>ruky; a d<>te
ka<EFBFBD>d<EFBFBD>mu p<><70>jemci spolu s programem kopii t<>to licence.
Za fyzick<63> akt p<>enesen<65> kopie m<><6D>ete <20><>dat poplatek a podle vlastn<74>ho uv<75><76>en<65>
m<EFBFBD><EFBFBD>ete nab<61>dnout za poplatek z<>ru<72>n<EFBFBD> ochranu.
2. M<><4D>ete modifikovat va<76>i kopii <20>i kopie programu anebo kter<65>koliv jeho <20><>sti,
a tak vytvo<76>it d<>lo zalo<6C>en<65> na programu a kop<6F>rovat a roz<6F>i<EFBFBD>ovat takov<6F>
modifikace <20>i d<>lo podle podm<64>nek paragrafu 1 v<><76>e, za p<>edpokladu, <20>e spln<6C>te
v<EFBFBD>echny tyto podm<64>nky:
a) Modifikovan<61> soubory mus<75>te opat<61>it z<>etelnou zm<7A>nkou uv<75>d<EFBFBD>j<EFBFBD>c<EFBFBD>, <20>e jste
soubory zm<7A>nil a datum ka<6B>d<EFBFBD> zm<7A>ny.
b) Mus<75>te umo<6D>nit, aby jak<61>koliv v<>mi publikovan<61> <20>i roz<6F>i<EFBFBD>ovan<61> d<>lo, kter<65>
obsahuje zcela nebo z<><7A>sti program nebo jakoukoli jeho <20><>st, pop<6F><70>pad<61> je z
programu nebo jeho <20><>sti odvozeno, mohlo b<>t jako celek bezplatn<74> poskytnuto
ka<6B>d<EFBFBD> t<>et<65> osob<6F> v souladu s ustanoven<65>mi t<>to licence.
c) Pokud modifikovan<61> program pracuje norm<72>ln<6C> tak, <20>e <20>te interaktivn<76> povely,
mus<75>te zajistit, <20>e p<>i nejb<6A><62>n<EFBFBD>j<EFBFBD><6A>m zp<7A>sobu jeho spu<70>t<EFBFBD>n<EFBFBD> vytiskne nebo zobraz<61>
hl<68><6C>en<65> zahrnuj<75>c<EFBFBD> p<><70>slu<6C>nou zm<7A>nku o autorsk<73>m pr<70>vu a uvede, <20>e neexistuje
<20><>dn<64> z<>ruka (nebo p<><70>padn<64>, <20>e z<>ruku poskytujete vy), a <20>e u<>ivatel<65> mohou za
t<>chto podm<64>nek program redistribuovat, a mus<75> u<>ivateli sd<73>lit, jak<61>m zp<7A>sobem
m<><6D>e nahl<68>dnout do kopie t<>to licence. (V<>jimka: v p<><70>pad<61>, <20>e s<>m program je
interaktivn<76>, av<61>ak <20><>dn<64> takov<6F> hl<68><6C>en<65> nevypisuje, nepo<70>aduje se, aby va<76>e
d<>lo zalo<6C>en<65> na programu takov<6F> hl<68><6C>en<65> vypisovalo.)
Tyto po<70>adavky se vztahuj<75> k modifikovan<61>mu d<>lu jako celku. Pokud lze
identifikovat <20><>sti takov<6F>ho d<>la, kter<65> z<>ejm<6A> nejsou odvozeny z programu a
mohou b<>t samy o sob<6F> rozumn<6D> pova<76>ov<6F>ny za nez<65>visl<73> a samostatn<74> d<>la, pak se
tato licence a jej<65> ustanoven<65> nevztahuj<75> na tyto <20><>sti, jsou-li <20><><EFBFBD>eny jako
nez<EFBFBD>visl<EFBFBD> d<>la. Av<41>ak jakmile tyt<79><74> <20><>sti roz<6F>i<EFBFBD>ujete jako <20><>st celku, j<>m<EFBFBD> je
d<EFBFBD>lo zalo<6C>en<65> na programu, mus<75> b<>t roz<6F>i<EFBFBD>ov<6F>n<EFBFBD> tohoto celku pod<6F><64>zeno
ustanoven<EFBFBD>m t<>to licence tak, <20>e povolen<65> poskytnut<75> dal<61><6C>m u<>ivatel<65>m se
roz<EFBFBD><EFBFBD><EFBFBD><EFBFBD> na cel<65> d<>lo, tedy na v<>echny jeho <20><>sti bez ohledu na to, kdo kterou
<EFBFBD><EFBFBD>st napsal.
Smyslem tohoto paragrafu tedy nen<65> z<>sk<73>n<EFBFBD> pr<70>v na d<>lo zcela napsan<61> v<>mi ani
pop<EFBFBD>r<EFBFBD>n<EFBFBD> va<76>ich pr<70>v v<><76>i n<>mu; skute<74>n<EFBFBD>m smyslem je v<>kon pr<70>va na <20><>zen<65>
distribuce odvozen<65>ch nebo kolektivn<76>ch d<>l zalo<6C>en<65>ch na programu.
Pouh<EFBFBD> spojen<65> jin<69>ho d<>la, je<6A> nen<65> na programu zalo<6C>eno, s programem (anebo
d<EFBFBD>lem zalo<6C>en<65>m na programu) na pam<61><6D>ov<6F>m nebo distribu<62>n<EFBFBD>m m<>diu neuvazuje toto
jin<EFBFBD> d<>lo do p<>sobnosti t<>to licence.
3. M<><4D>ete kop<6F>rovat a roz<6F>i<EFBFBD>ovat program (nebo d<>lo na n<>m zalo<6C>en<65>, viz
paragraf 2) v objektov<6F> anebo spustiteln<6C> podob<6F> podle ustanoven<65> paragraf<61> 1 a
2 v<><76>e, pokud spln<6C>te n<>kterou z n<>sleduj<75>c<EFBFBD>ch n<>le<6C>itost<73>:
a) Doprovod<6F>te jej zdrojov<6F>m k<>dem ve strojov<6F> <20>iteln<6C> form<72>. Zdrojov<6F> k<>d mus<75>
b<>t roz<6F>i<EFBFBD>ov<6F>n podle ustanoven<65> paragraf<61> 1 a 2 v<><76>e, a to na m<>diu b<><62>n<EFBFBD>
pou<6F><75>van<61>m pro v<>m<EFBFBD>nu softwaru; nebo
b) Doprovod<6F>te jej p<>semnou nab<61>dkou s platnost<73> nejm<6A>n<EFBFBD> t<>i roky, podle n<><6E>
poskytnete jak<61>koli t<>et<65> stran<61>, za poplatek nep<65>evy<76>uj<75>c<EFBFBD> va<76>e v<>daje
vynalo<6C>en<65> na fyzickou v<>robou zdrojov<6F> distribuce, kompletn<74> strojov<6F> <20>itelnou
kopii odpov<6F>daj<61>c<EFBFBD>ho zdrojov<6F>ho k<>du, jen<65> mus<75> b<>t <20><><EFBFBD>en podle ustanoven<65>
paragraf<61> 1 a 2 v<><76>e na m<>diu b<><62>n<EFBFBD> pou<6F><75>van<61>m pro v<>m<EFBFBD>nu softwaru; nebo
c) Doprovod<6F>te jej informacemi, kter<65> jste dostal ohledn<64> nab<61>dky na poskytnut<75>
zdrojov<6F>ho k<>du. (Tato alternativa je povolena jen pro nekomer<65>n<EFBFBD> <20><><EFBFBD>en<65> a jenom
tehdy, pokud jste obdr<64>el program v objektov<6F>m nebo spustiteln<6C>m tvaru spolu s
takovou nab<61>dkou, v souladu s polo<6C>kou b v<><76>e.)
Zdrojov<EFBFBD> k<>d k d<>lu je nejvhodn<64>j<EFBFBD><6A> formou d<>la z hlediska jeho p<><70>padn<64>ch
modifikac<EFBFBD>. Pro d<>lo ve spustiteln<6C>m tvaru znamen<65> <20>pln<6C> zdrojov<6F> k<>d ve<76>ker<65>
zdrojov<EFBFBD> k<>d pro v<>echny moduly, kter<65> obsahuje, plus jak<61>koli dal<61><6C> soubory pro
definici rozhran<61>, plus d<>vkov<6F> soubory pot<6F>ebn<62> pro kompilaci a instalaci
spustiteln<EFBFBD>ho programu. Zvl<76><6C>tn<74> v<>jimkou jsou v<>ak ty softwarov<6F> komponenty,
kter<EFBFBD> jsou norm<72>ln<6C> <20><><EFBFBD>eny (bu<62> ve zdrojov<6F> nebo bin<69>rn<72> form<72>) s hlavn<76>mi
sou<EFBFBD><EFBFBD>stmi opera<72>n<EFBFBD>ho syst<73>mu, na n<>m<EFBFBD> spustiteln<6C> program b<><62><EFBFBD> (tj. s
p<EFBFBD>eklada<EFBFBD>em, j<>drem apod.). Tyto komponenty nemus<75> b<>t <20><><EFBFBD>eny se zdrojov<6F>m k<>dem,
pokud ov<6F>em komponenta sama nedoprov<6F>z<EFBFBD> spustitelnou podobu d<>la.
Je-li <20><><EFBFBD>en<65> objektov<6F>ho nebo spustiteln<6C>ho k<>du <20>in<69>no nab<61>dkou p<><70>stupu ke
kop<EFBFBD>rov<EFBFBD>n<EFBFBD> z ur<75>it<69>ho m<>sta, potom se za distribuci zdrojov<6F>ho k<>du po<70><6F>t<EFBFBD> i
nab<EFBFBD>dnut<EFBFBD> ekvivalentn<74>ho p<><70>stupu ke kop<6F>rov<6F>n<EFBFBD> zdrojov<6F>ho k<>du ze stejn<6A>ho
m<EFBFBD>sta, by<62> p<>itom nejsou t<>et<65> strany nuceny ke zkop<6F>rov<6F>n<EFBFBD> zdrojov<6F>ho k<>du
spolu s objektov<6F>m.
4. Nesm<73>te kop<6F>rovat, modifikovat, poskytovat sublicence anebo <20><><EFBFBD>it program
jin<EFBFBD>m zp<7A>sobem ne<6E> v<>slovn<76> uveden<65>m v t<>to licenci. Jak<61>koli jin<69> pokus o
kop<EFBFBD>rov<EFBFBD>n<EFBFBD>, modifikov<6F>n<EFBFBD>, poskytnut<75> sublicence anebo <20><><EFBFBD>en<65> programu je
neplatn<EFBFBD> a automaticky ukon<6F><6E> va<76>e pr<70>va dan<61> touto licenc<6E>. Strany, kter<65> od
v<EFBFBD>s obdr<64>ely kopie anebo pr<70>va v souladu s touto licenc<6E>, v<>ak nemaj<61> sv<73>
licence ukon<6F>eny, dokud se jim pln<6C> pod<6F>izuj<75>.
5. Nen<65> va<76><61> povinost<73> tuto licenci p<>ijmout, proto<74>e jste ji nepodepsal. Nic
jin<EFBFBD>ho v<>m v<>ak ned<65>v<EFBFBD> mo<6D>nost kop<6F>rovat nebo <20><><EFBFBD>it program nebo odvozen<65> d<>la.
V p<><70>pad<61>, <20>e tuto licenci nep<65>ijmete, jsou tyto <20>innosti z<>konem zak<61>z<EFBFBD>ny. T<>m
p<EFBFBD>dem modifikac<61> anebo <20><><EFBFBD>en<65>m programu (anebo ka<6B>d<EFBFBD>ho d<>la zalo<6C>en<65>ho na
programu) vyjad<61>ujete sv<73> pod<6F><64>zen<65> se licenci a v<>em jej<65>m ustanoven<65>m a
podm<EFBFBD>nk<EFBFBD>m pro kop<6F>rovan<61>, modifikov<6F>n<EFBFBD> a <20><><EFBFBD>en<65> programu a d<>l na n<>m zalo<6C>en<65>ch.
6. Poka<6B>d<EFBFBD>, kdy<64> redistribuujete program (nebo d<>lo zalo<6C>en<65> na programu),
z<EFBFBD>sk<EFBFBD>v<EFBFBD> p<><70>jemce od p<>vodn<64>ho dr<64>itele licence pr<70>vo kop<6F>rovat, modifikovat a
<EFBFBD><EFBFBD><EFBFBD>it program v souladu s t<>mito ustanoven<65>mi a podm<64>nkami. Nesm<73>te kl<6B>st <20><>dn<64>
dal<EFBFBD><EFBFBD> p<>ek<65><6B>ky v<>konu zde zaru<72>en<65>ch p<><70>jemcov<6F>ch pr<70>v. Nejste odpov<6F>dn<64> za
vym<EFBFBD>h<EFBFBD>n<EFBFBD> dodr<64>ov<6F>n<EFBFBD> t<>to licence t<>et<65>mi stranami.
7. Jsou-li v<>m z rozhodnut<75> soudu, obvin<69>n<EFBFBD>m z poru<72>en<65> patentu nebo z
jak<EFBFBD>hokoli jin<69>ho d<>vodu (nejen v souvislosti s patenty) ulo<6C>eny takov<6F> podm<64>nky
(a<> ji<6A> p<><70>kazem soudu, smlouvou nebo jinak), kter<65> se vylu<6C>uj<75> s podm<64>nkami
t<EFBFBD>to licence, nejste t<>m osvobozen od podm<64>nek t<>to licence. Pokud nem<65><6D>ete
<EFBFBD><EFBFBD><EFBFBD>it program tak, abyste vyhov<6F>l z<>rove<76> sv<73>m z<>vazk<7A>m vypl<70>vaj<61>c<EFBFBD>m z t<>to
licence a jin<69>m platn<74>m z<>vazk<7A>m, nesm<73>te jej v d<>sledku toho <20><><EFBFBD>it v<>bec. Pokud
by nap<61><70>klad patentov<6F> osv<73>d<EFBFBD>en<65> nepovolovalo bezplatnou redistribuci programu
v<EFBFBD>emi, kdo va<76><61>m p<>i<EFBFBD>in<69>n<EFBFBD>m z<>skaj<61> p<><70>mo nebo nep<65><70>mo jeho kopie, pak by jedin<69>
mo<EFBFBD>n<EFBFBD> zp<7A>sob jak vyhov<6F>t z<>rove<76> patentov<6F>mu osv<73>d<EFBFBD>en<65> i t<>to licenci spo<70><6F>val v
ukon<EFBFBD>en<EFBFBD> distribuce programu.
Pokud by se za n<>jak<61>ch specifick<63>ch okolnost<73> jevila n<>kter<65> <20><>st tohoto
paragrafu jako neplatn<74> nebo nevynutiteln<6C>, pova<76>uje se za sm<73>rodatnou rovnov<6F>ha
vyj<EFBFBD>d<EFBFBD>en<EFBFBD> t<>mto paragrafem a paragraf jako celek se pova<76>uje za sm<73>rodatn<74> za
jin<EFBFBD>ch okolnost<73>.
Smyslem tohoto paragrafu nen<65> nav<61>d<EFBFBD>t v<>s k poru<72>ov<6F>n<EFBFBD> patent<6E> <20>i jin<69>ch
ustanoven<EFBFBD> vlastnick<63>ho pr<70>va, anebo tato ustanoven<65> zpochyb<79>ovat; jedin<69>m jeho
smyslem je ochrana integrity syst<73>mu <20><><EFBFBD>en<65> svobodn<64>ho softwaru, kter<65> je
podlo<EFBFBD>en ve<76>ejn<6A>mi licen<65>n<EFBFBD>mi p<>edpisy. Mnoz<6F> lid<69> poskytli sv<73> p<><70>sp<73>vky do
<EFBFBD>irok<EFBFBD>ho okruhu softwaru <20><><EFBFBD>en<65>ho t<>mto syst<73>mem, spolehnuv<75>e se na jeho
d<EFBFBD>sledn<EFBFBD> uplat<61>ov<6F>n<EFBFBD>; z<>le<6C><65> na autorovi/d<>rci, aby rozhodl, zda si p<>eje <20><><EFBFBD>it
software pomoc<6F> n<>jak<61>ho jin<69>ho syst<73>mu a <20><>dn<64> u<>ivatel licence nem<65><6D>e takov<6F>
rozhodnut<EFBFBD> zpochyb<79>ovat.
Smyslem tohoto paragrafu je zevrubn<62> osv<73>tlit to, co je pova<76>ov<6F>no za d<>sledek
plynouc<EFBFBD> ze zbytku t<>to licence.
8. Pokud je <20><><EFBFBD>en<65> <20>i pou<6F>it<69> programu v n<>kter<65>ch zem<65>ch omezeno bu<62> patenty
anebo autorsky chr<68>n<EFBFBD>n<EFBFBD>mi rozhran<61>mi, m<><6D>e dr<64>itel p<>vodn<64>ch autorsk<73>ch pr<70>v,
kter<EFBFBD> sv<73><76>uje program do p<>sobnosti t<>to licence, p<>idat v<>slovn<76> omezen<65> pro
geografick<EFBFBD> <20><><EFBFBD>en<65>, vylu<6C>uj<75>c<EFBFBD> takov<6F> zem<65>, tak<61>e <20><><EFBFBD>en<65> je povoleno jen v t<>ch
zem<EFBFBD>ch nebo mezi t<>mi zem<65>mi, kter<65> nejsou t<>mto zp<7A>sobem vylou<6F>eny. Tato
licence zahrnuje v tomto p<><70>pad<61> takov<6F> omezen<65> p<>esn<73> tak, jako by bylo zaps<70>no
v textu t<>to licence.
9. Free Software Foundation m<><6D>e <20>as od <20>asu vyd<79>vat upraven<65> nebo nov<6F> verze
General Public License. Takov<6F> nov<6F> verze se budou sv<73>m duchem podobat sou<6F>asn<73>
verzi, v jednotlivostech se v<>ak mohou li<6C>it s ohledem na nov<6F> probl<62>my <20>i z<>jmy.
Ka<EFBFBD>d<EFBFBD> verzi je p<>id<69>leno rozli<6C>uj<75>c<EFBFBD> <20><>slo verze. Pokud program specifikuje
<EFBFBD><EFBFBD>slo verze, kter<65> se na n<>j vztahuje, a "v<>echny n<>sleduj<75>c<EFBFBD> verze", m<><6D>ete se
podle uv<75><76>en<65> <20><>dit ustanoven<65>mi a podm<64>nkami bu<62>to on<6F> konkr<6B>tn<74> verze anebo
kter<EFBFBD>koliv n<>sleduj<75>c<EFBFBD> verze, kterou vydala Free Software Foundation. Jestli<6C>e
program nespecifikuje <20><>slo verze t<>to licence, m<><6D>ete si vybrat libovolnou
verzi, kterou kdy Free Software Foundation vydala.
10. Pokud si p<>ejete zahrnout <20><>sti programu do jin<69>ch svobodn<64>ch program<61>,
jejich<EFBFBD> distribu<62>n<EFBFBD> podm<64>nky jsou odli<6C>n<EFBFBD>, za<7A>lete autorovi <20><>dost o povolen<65>. V
p<EFBFBD><EFBFBD>pad<EFBFBD> softwaru, k n<>mu<6D> vlastn<74> autorsk<73> pr<70>va Free Software Foundation,
napi<EFBFBD>te Free Software Foundation; n<>kdy <20>in<69>me v<>jimky ze zde uveden<65>ch
ustanoven<EFBFBD>. Na<4E>e rozhodnut<75> bude vedeno dv<64>ma c<>li: zachov<6F>n<EFBFBD>m svobodn<64> povahy
v<EFBFBD>ech odvozenin na<6E>eho svobodn<64>ho softwaru a podporou sd<73>len<65> a op<6F>tovn<76>ho
vyu<EFBFBD>it<EFBFBD> softwaru obecn<63>.
Z<>RUKA SE NEPOSKYTUJE
11. VZHLEDEM K BEZPLATN<54>MU POSKYTNUT<55> LICENCE K PROGRAMU SE NA PROGRAM
NEVZTAHUJE <20><>DN<44> Z<>RUKA, A TO V M<><4D>E POVOLEN<45> PLATN<54>M Z<>KONEM. POKUD NEN<45>
P<EFBFBD>SEMN<EFBFBD> STANOVENO JINAK, POSKYTUJ<55> DR<44>ITEL<45> AUTORSK<53>CH PR<50>V POP<4F><50>PAD<41> JIN<49>
STRANY PROGRAM "TAK, JAK JE", BEZ Z<>RUKY JAK<41>HOKOLI DRUHU, A<> V<>SLOVN<56> NEBO
VYPL<EFBFBD>VAJ<EFBFBD>C<EFBFBD>, V<>ETN<54>, ALE NIKOLI JEN, Z<>RUK PRODEJNOSTI A VHODNOSTI PRO UR<55>IT<49>
<EFBFBD><EFBFBD>EL. POKUD JDE O KVALITU A V<>KONNOST PROGRAMU, LE<4C><45> VE<56>KER<45> RIZIKO NA V<>S.
POKUD BY SE U PROGRAMU PROJEVILY Z<>VADY, PADAJ<41> N<>KLADY ZA V<>ECHNU POT<4F>EBNOU
<EFBFBD>DR<EFBFBD>BU, OPRAVU <20>I N<>PRAVU NA V<><56> VRUB.
12. V <20><>DN<44>M P<><50>PAD<41>, S V<>JIMKOU TOHO, KDY<44> TO VY<56>ADUJE PLATN<54> Z<>KON, ANEBO KDY<44>
TO BYLO P<>SEMN<4D> ODSOUHLASENO, V<>M NEBUDE <20><>DN<44> Z DR<44>ITEL<45> AUTORSK<53>CH PR<50>V ANI
<EFBFBD><EFBFBD>DN<EFBFBD> JIN<49> STRANA, KTER<45> SM<53> MODIFIKOVAT <20>I <20><><EFBFBD>IT PROGRAM V SOULADU S
P<EFBFBD>EDCHOZ<EFBFBD>MI USTANOVEN<45>MI, ODPOV<4F>DNI ZA <20>KODY, V<>ETN<54> V<>ECH OBECN<43>CH, SPECI<43>LN<4C>CH,
NAHODIL<EFBFBD>CH NEBO N<>SLEDN<44>CH <20>KOD VYPL<50>VAJ<41>C<EFBFBD>CH Z U<><55>V<EFBFBD>N<EFBFBD> ANEBO NESCHOPNOSTI
U<EFBFBD><EFBFBD>VAT PROGRAMU (V<>ETN<54>, ALE NIKOLI JEN, ZTR<54>TY NEBO ZKRESLEN<45> DAT, NEBO
TRVAL<EFBFBD>CH <20>KOD ZP<5A>SOBEN<45>CH V<>M NEBO T<>ET<45>M STRAN<41>M, NEBO SELH<4C>N<EFBFBD> FUNKCE PROGRAMU
V SOU<4F>INNOSTI S JIN<49>MI PROGRAMY), A TO I V P<><50>PAD<41>, <20>E TAKOV<4F> DR<44>ITEL AUTORSK<53>CH
PR<EFBFBD>V NEBO JIN<49> STRANA BYLI UPOZORN<52>NI NA MO<4D>NOST TAKOV<4F>CH <20>KOD.
KONEC USTANOVEN<45> A PODM<44>NEK
----------------------------------------------------------------------------------
(c) 2004, Str<74>nky o svobodn<64>m software (info@gnu.cz)
Tento p<>eklad je z velk<6C> <20><>sti zalo<6C>en na p<>ekladu od Ladislava Lhotky.
V p<><70>pad<61> dopl<70>uj<75>c<EFBFBD>ch informac<61> nebo oprav kontaktujte maintainera: kysela@gnu.cz
Posledn<EFBFBD> <20>prava: 22. 12. 2004
----------------------------------------------------------------------------------

View File

@@ -1,279 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS

133
readme.md Normal file
View File

@@ -0,0 +1,133 @@
[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)
Database access functions in PHP are not standardised. This library
hides the differences between them, and above all, it gives you a very handy interface.
The best way to install Dibi is to use a [Composer](https://getcomposer.org/download):
php composer.phar require dibi/dibi
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.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](https://dibiphp.com).
Connect to database:
```php
// connect to database (static way)
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);
```
SELECT, INSERT, UPDATE
```php
dibi::query('SELECT * FROM users WHERE id = ?', $id);
$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', [
'title' => array('SHA1(?)', 'tajneheslo'),
]);
// UPDATE users SET 'title' = SHA1('tajneheslo')
```
Getting results
```php
$result = dibi::query('SELECT * FROM users');
$value = $result->fetchSingle(); // single value
$all = $result->fetchAll(); // all rows
$assoc = $result->fetchAssoc('id'); // all rows as associative array
$pairs = $result->fetchPairs('customerID', 'name'); // all rows as key => value pairs
// iterating
foreach ($result as $n => $row) {
print_r($row);
}
```
Modifiers for arrays:
```php
dibi::query('SELECT * FROM users WHERE %and', [
array('number > ?', 10),
array('number < ?', 100),
]);
// SELECT * FROM users WHERE (number > 10) AND (number < 100)
```
<table>
<tr><td> %and </td><td> </td><td> `[key]=val AND [key2]="val2" AND ...` </td></tr>
<tr><td> %or </td><td> </td><td> `[key]=val OR [key2]="val2" OR ...` </td></tr>
<tr><td> %a </td><td> assoc </td><td> `[key]=val, [key2]="val2", ...` </td></tr>
<tr><td> %l %in </td><td> list </td><td> `(val, "val2", ...)` </td></tr>
<tr><td> %v </td><td> values </td><td> `([key], [key2], ...) VALUES (val, "val2", ...)` </td></tr>
<tr><td> %m </td><td> multivalues </td><td> `([key], [key2], ...) VALUES (val, "val2", ...), (val, "val2", ...), ...` </td></tr>
<tr><td> %by </td><td> ordering </td><td> `[key] ASC, [key2] DESC ...` </td></tr>
<tr><td> %n </td><td> identifiers </td><td> `[key], [key2] AS alias, ...` </td></tr>
<tr><td> other </td><td> - </td><td> `val, val2, ...` </td></tr>
</table>
Modifiers for LIKE
```php
dibi::query("SELECT * FROM table WHERE name LIKE %like~", $query);
```
<table>
<tr><td> %like~ </td><td> begins with </td></tr>
<tr><td> %~like </td><td> ends with </td></tr>
<tr><td> %~like~ </td><td> contains </td></tr>
</table>
DateTime:
```php
dibi::query('UPDATE users SET', [
'time' => new DateTime,
]);
// UPDATE users SET ('2008-01-01 01:08:10')
```
Testing:
```php
echo dibi::$sql; // last SQL query
echo dibi::$elapsedTime;
echo dibi::$numOfQueries;
echo dibi::$totalTime;
```

View File

@@ -0,0 +1,52 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Nette;
/**
* Dibi extension for Nette Framework 2.1. Creates 'connection' service.
*/
class DibiExtension21 extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$container = $this->getContainerBuilder();
$config = $this->getConfig();
$useProfiler = isset($config['profiler'])
? $config['profiler']
: $container->parameters['debugMode'];
unset($config['profiler']);
if (isset($config['flags'])) {
$flags = 0;
foreach ((array) $config['flags'] as $flag) {
$flags |= constant($flag);
}
$config['flags'] = $flags;
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('Dibi\Connection', [$config])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : TRUE);
if ($useProfiler) {
$panel = $container->addDefinition($this->prefix('panel'))
->setClass('Dibi\Bridges\Nette\Panel')
->addSetup('Nette\Diagnostics\Debugger::getBar()->addPanel(?)', ['@self'])
->addSetup('Nette\Diagnostics\Debugger::getBlueScreen()->addPanel(?)', ['Dibi\Bridges\Nette\Panel::renderException']);
$connection->addSetup('$service->onEvent[] = ?', [[$panel, 'logEvent']]);
}
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
use Dibi;
use Nette;
/**
* Dibi extension for Nette Framework 2.2. Creates 'connection' & 'panel' services.
*/
class DibiExtension22 extends Nette\DI\CompilerExtension
{
public function loadConfiguration()
{
$container = $this->getContainerBuilder();
$config = $this->getConfig();
$useProfiler = isset($config['profiler'])
? $config['profiler']
: class_exists('Tracy\Debugger') && $container->parameters['debugMode'];
unset($config['profiler']);
if (isset($config['flags'])) {
$flags = 0;
foreach ((array) $config['flags'] as $flag) {
$flags |= constant($flag);
}
$config['flags'] = $flags;
}
$connection = $container->addDefinition($this->prefix('connection'))
->setClass('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', [
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

@@ -0,0 +1,153 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* 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.
*/
class Panel implements Nette\Diagnostics\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
{
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
/**
* After event notification.
* @return void
*/
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getTab()
{
$totalTime = 0;
foreach ($this->events as $event) {
$totalTime += $event->time;
}
return '<span title="dibi"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAEYSURBVBgZBcHPio5hGAfg6/2+R980k6wmJgsJ5U/ZOAqbSc2GnXOwUg7BESgLUeIQ1GSjLFnMwsKGGg1qxJRmPM97/1zXFAAAAEADdlfZzr26miup2svnelq7d2aYgt3rebl585wN6+K3I1/9fJe7O/uIePP2SypJkiRJ0vMhr55FLCA3zgIAOK9uQ4MS361ZOSX+OrTvkgINSjS/HIvhjxNNFGgQsbSmabohKDNoUGLohsls6BaiQIMSs2FYmnXdUsygQYmumy3Nhi6igwalDEOJEjPKP7CA2aFNK8Bkyy3fdNCg7r9/fW3jgpVJbDmy5+PB2IYp4MXFelQ7izPrhkPHB+P5/PjhD5gCgCenx+VR/dODEwD+A3T7nqbxwf1HAAAAAElFTkSuQmCC" />'
. count($this->events) . ' queries'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
. '</span>';
}
/**
* Returns HTML code for custom panel. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getPanel()
{
$totalTime = $s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) {
try {
$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 = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
}
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
if ($explain) {
static $counter;
$counter++;
$s .= "<br /><a href='#nette-debug-DibiProfiler-row-$counter' class='nette-toggler nette-toggle-collapsed' rel='#nette-debug-DibiProfiler-row-$counter'>explain</a>";
}
$s .= '</td><td class="nette-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
if ($event->source) {
$helpers = 'Nette\Diagnostics\Helpers';
if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
}
$s .= call_user_func([$helpers, 'editorLink'], $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
}
return empty($this->events) ? '' :
'<style> #nette-debug td.nette-DibiProfiler-sql { background: white !important }
#nette-debug .nette-DibiProfiler-source { color: #999 !important }
#nette-debug nette-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . count($this->events) . ($totalTime === NULL ? '' : sprintf(', time: %0.3f ms', $totalTime * 1000)) . '</h1>
<div class="nette-inner nette-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
</table>
</div>';
}
}

View File

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

View File

@@ -0,0 +1,149 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Tracy;
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Tracy;
/**
* Dibi panel for Tracy.
*/
class Panel implements Tracy\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(Dibi\Connection $connection)
{
Tracy\Debugger::getBar()->addPanel($this);
Tracy\Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
/**
* After event notification.
* @return void
*/
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public static function renderException($e)
{
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (Tracy\IBarPanel)
* @return mixed
*/
public function getTab()
{
$totalTime = 0;
$count = count($this->events);
foreach ($this->events as $event) {
$totalTime += $event->time;
}
return '<span title="dibi"><svg viewBox="0 0 2048 2048" style="vertical-align: bottom; width:1.23em; height:1.55em"><path fill="' . ($count ? '#b079d6' : '#aaa') . '" d="M1024 896q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0 768q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-384q237 0 443-43t325-127v170q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-170q119 84 325 127t443 43zm0-1152q208 0 385 34.5t280 93.5 103 128v128q0 69-103 128t-280 93.5-385 34.5-385-34.5-280-93.5-103-128v-128q0-69 103-128t280-93.5 385-34.5z"/></svg><span class="tracy-label">'
. $count . ' queries'
. ($totalTime ? sprintf(' / %0.1f ms', $totalTime * 1000) : '')
. '</span></span>';
}
/**
* Returns HTML code for custom panel. (Tracy\IBarPanel)
* @return mixed
*/
public function getPanel()
{
$totalTime = $s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === Event::SELECT) {
try {
$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 = Helpers::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (Dibi\Exception $e) {
}
list($event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime) = $backup;
}
$s .= '<tr><td>' . sprintf('%0.3f', $event->time * 1000);
if ($explain) {
static $counter;
$counter++;
$s .= "<br /><a href='#tracy-debug-DibiProfiler-row-$counter' class='tracy-toggle tracy-collapsed' rel='#tracy-debug-DibiProfiler-row-$counter'>explain</a>";
}
$s .= '</td><td class="tracy-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='tracy-debug-DibiProfiler-row-$counter' class='tracy-collapsed'>{$explain}</div>";
}
if ($event->source) {
$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>";
}
return empty($this->events) ? '' :
'<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>
<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 . '
</table>
</div>';
}
}

666
src/Dibi/Connection.php Normal file
View File

@@ -0,0 +1,666 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use Traversable;
/**
* dibi connection.
*
* @property-read int $affectedRows
* @property-read int $insertId
*/
class Connection
{
use Strict;
/** @var array of function (Event $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var Driver */
private $driver;
/** @var Translator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var HashMap Substitutes for identifiers */
private $substitutes;
/**
* Connection options: (see driver-specific options too)
* - lazy (bool) => if TRUE, connection will be established only when required
* - result (array) => result set options
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
* - profiler (array or bool)
* - run (bool) => enable profiler?
* - file => file to log
* - substitutes (array) => map of driver specific substitutes (under development)
* @param mixed connection parameters
* @param string connection name
* @throws Exception
*/
public function __construct($config, $name = NULL)
{
if (is_string($config)) {
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
$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.');
}
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;
}
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;
// profiler
$profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = ['run' => (bool) $profilerCfg];
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $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('Dibi\Bridges\Nette\Panel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
}
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
}
}
if (empty($config['lazy'])) {
$this->connect();
}
}
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
// disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
$this->connected && $this->driver->getResource() && $this->disconnect();
}
/**
* Connects to a database.
* @return void
*/
final public function connect()
{
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
$event && $this->onEvent($event->done());
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Disconnects from a database.
* @return void
*/
final public function disconnect()
{
$this->driver->disconnect();
$this->connected = FALSE;
}
/**
* Returns TRUE when connection was established.
* @return bool
*/
final public function isConnected()
{
return $this->connected;
}
/**
* Returns configuration variable. If no $key is passed, returns the entire array.
* @see self::__construct
* @param string
* @param mixed default value to use if key not found
* @return mixed
*/
final public function getConfig($key = NULL, $default = NULL)
{
if ($key === NULL) {
return $this->config;
} elseif (isset($this->config[$key])) {
return $this->config[$key];
} else {
return $default;
}
}
/** @deprecated */
public static function alias(& $config, $key, $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 Driver
*/
final public function getDriver()
{
$this->connected || $this->connect();
return $this->driver;
}
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
* @return Result|int result set object (if any)
* @throws Exception
*/
final public function query($args)
{
$args = func_get_args();
return $this->nativeQuery($this->translateArgs($args));
}
/**
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws Exception
*/
final public function translate($args)
{
$args = func_get_args();
return $this->translateArgs($args);
}
/**
* Generates and prints SQL query.
* @param array|mixed one or more arguments
* @return bool
*/
final public function test($args)
{
$args = func_get_args();
try {
Helpers::dump($this->translateArgs($args));
return TRUE;
} catch (Exception $e) {
if ($e->getSql()) {
Helpers::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
return FALSE;
}
}
/**
* Generates (translates) and returns SQL query as DataSource.
* @param array|mixed one or more arguments
* @return DataSource
* @throws Exception
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DataSource($this->translateArgs($args), $this);
}
/**
* Generates SQL query.
* @param array
* @return string
*/
private function translateArgs($args)
{
$this->connected || $this->connect();
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 Result|int result set object (if any)
* @throws Exception
*/
final public function nativeQuery($sql)
{
$this->connected || $this->connect();
\dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL;
try {
$res = $this->driver->query($sql);
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
if ($res) {
$res = $this->createResultSet($res);
} else {
$res = $this->driver->getAffectedRows();
}
$event && $this->onEvent($event->done($res));
return $res;
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows
* @throws Exception
*/
public function getAffectedRows()
{
$this->connected || $this->connect();
$rows = $this->driver->getAffectedRows();
if (!is_int($rows) || $rows < 0) {
throw new Exception('Cannot retrieve number of affected rows.');
}
return $rows;
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Exception
*/
public function affectedRows()
{
return $this->getAffectedRows();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string optional sequence name
* @return int
* @throws Exception
*/
public function getInsertId($sequence = NULL)
{
$this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence);
if ($id < 1) {
throw new Exception('Cannot retrieve last generated ID.');
}
return (int) $id;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Exception
*/
public function insertId($sequence = NULL)
{
return $this->getInsertId($sequence);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
*/
public function begin($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL;
try {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
*/
public function commit($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL;
try {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
*/
public function rollback($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL;
try {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Result set factory.
* @param ResultDriver
* @return Result
*/
public function createResultSet(ResultDriver $resultDriver)
{
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return Fluent
*/
public function command()
{
return new Fluent($this);
}
/**
* @param string column name
* @return Fluent
*/
public function select($args)
{
$args = func_get_args();
return $this->command()->__call('select', $args);
}
/**
* @param string table
* @param array
* @return Fluent
*/
public function update($table, $args)
{
if (!(is_array($args) || $args instanceof Traversable)) {
throw new \InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->update('%n', $table)->set($args);
}
/**
* @param string table
* @param array
* @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.');
}
return $this->command()->insert()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
}
/**
* @param string table
* @return Fluent
*/
public function delete($table)
{
return $this->command()->delete()->from('%n', $table);
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap.
* @return HashMap
*/
public function getSubstitutes()
{
return $this->substitutes;
}
/**
* Provides substitution.
* @return string
*/
public function substitute($value)
{
return strpos($value, ':') === FALSE
? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
}
/********************* shortcuts ****************d*g**/
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return Row
* @throws Exception
*/
public function fetch($args)
{
$args = func_get_args();
return $this->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return Row[]
* @throws Exception
*/
public function fetchAll($args)
{
$args = func_get_args();
return $this->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws Exception
*/
public function fetchSingle($args)
{
$args = func_get_args();
return $this->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws Exception
*/
public function fetchPairs($args)
{
$args = func_get_args();
return $this->query($args)->fetchPairs();
}
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/
/**
* Import SQL dump from file.
* @param string filename
* @return int count of sql commands
*/
public function loadFile($file)
{
return Helpers::loadFromFile($this, $file);
}
/**
* Gets a information about the current database.
* @return Reflection\Database
*/
public function getDatabaseInfo()
{
$this->connected || $this->connect();
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
}
/**
* Prevents unserialization.
*/
public function __wakeup()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
/**
* Prevents serialization.
*/
public function __sleep()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
protected function onEvent($arg)
{
foreach ($this->onEvent ?: [] as $handler) {
call_user_func($handler, $arg);
}
}
}

309
src/Dibi/DataSource.php Normal file
View File

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

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

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

@@ -0,0 +1,814 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Firebird/InterBase database.
*
* Driver options:
* - database => the path to database file (server:/path/database.fdb)
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var resource Resultset resource */
private $transaction;
/** @var bool */
private $inTransaction = FALSE;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
Dibi\Helpers::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$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,
];
if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
}
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@ibase_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception
*/
public function query($sql)
{
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if ($res === FALSE) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
} else {
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql);
}
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return ibase_affected_rows($this->connection);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string generator name
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return ibase_gen_id($sequence, 0, $this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->getResource());
$this->inTransaction = TRUE;
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return $this->inTransaction;
}
/**
* Returns the connection resource.
* @return resource
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ********************/
/**
* Encodes data for use in a SQL statement.
* @param string
* @return string
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier($value)
{
return '"' . str_replace('"', '""', $value). '"';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
throw new Dibi\NotImplementedException;
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit > 0 || $offset > 0) {
// http://www.firebirdsql.org/refdocs/langrefupd20-select.html
$sql = 'SELECT ' . ($limit > 0 ? 'FIRST ' . (int) $limit : '') . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
}
}
/********************* result set ********************/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Firebird/Interbase do not support returning number of rows in result set.');
}
/**
* 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)
{
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (ibase_errcode()) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
} else {
throw new Dibi\DriverException($msg, ibase_errcode());
}
}
return $result;
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Firebird/Interbase do not support seek in result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
ibase_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns the result set resource.
* @return resource
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = [
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
];
}
return $columns;
}
/********************* Dibi\Reflector ********************/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->query("
SELECT TRIM(RDB\$RELATION_NAME),
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'TRUE',
];
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME,
CASE f.RDB\$FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'INT64'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS FIELD_TYPE,
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
r.RDB\$DEFAULT_VALUE AS DEFAULT_VALUE,
CASE r.RDB\$NULL_FLAG
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
END AS NULLABLE
FROM RDB\$RELATION_FIELDS r
LEFT JOIN RDB\$FIELDS f ON r.RDB\$FIELD_SOURCE = f.RDB\$FIELD_NAME
WHERE r.RDB\$RELATION_NAME = '$table'
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = [
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
'size' => $row['FIELD_LENGTH'],
'nullable' => $row['NULLABLE'] === 'TRUE',
'default' => $row['DEFAULT_VALUE'],
'autoincrement' => FALSE,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table (the constraints are included).
* @param string
* @return array
*/
public function getIndexes($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG,
i.RDB\$FOREIGN_KEY AS FOREIGN_KEY,
TRIM(r.RDB\$CONSTRAINT_TYPE) AS CONSTRAINT_TYPE,
s.RDB\$FIELD_POSITION AS FIELD_POSITION
FROM RDB\$INDEX_SEGMENTS s
LEFT JOIN RDB\$INDICES i ON i.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
$indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
$indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$key]['table'] = $table;
$indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
}
return $indexes;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$table = strtoupper($table);
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
FROM RDB\$INDEX_SEGMENTS s
LEFT JOIN RDB\$RELATION_CONSTRAINTS r ON r.RDB\$INDEX_NAME = s.RDB\$INDEX_NAME
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = [
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
];
}
return $keys;
}
/**
* Returns list of indices in given table (the constraints are not listed).
* @param string
* @return array
*/
public function getIndices($table)
{
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = [];
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
return $indices;
}
/**
* Returns list of constraints in given table.
* @param string
* @return array
*/
public function getConstraints($table)
{
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND (
RDB\$UNIQUE_FLAG IS NOT NULL
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = [];
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
return $constraints;
}
/**
* Returns metadata for all triggers in a table or database.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
* @param string
* @param string
* @return array
*/
public function getTriggersMeta($table = NULL)
{
$res = $this->query("
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'BEFORE'
WHEN 2 THEN 'AFTER'
WHEN 3 THEN 'BEFORE'
WHEN 4 THEN 'AFTER'
WHEN 5 THEN 'BEFORE'
WHEN 6 THEN 'AFTER'
END AS TRIGGER_TYPE,
CASE RDB\$TRIGGER_TYPE
WHEN 1 THEN 'INSERT'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'UPDATE'
WHEN 4 THEN 'UPDATE'
WHEN 5 THEN 'DELETE'
WHEN 6 THEN 'DELETE'
END AS TRIGGER_EVENT,
CASE RDB\$TRIGGER_INACTIVE
WHEN 1 THEN 'FALSE' ELSE 'TRUE'
END AS TRIGGER_ENABLED
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = [];
while ($row = $res->fetch(TRUE)) {
$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;
}
/**
* Returns list of triggers for given table.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
* @param string
* @return array
*/
public function getTriggers($table = NULL)
{
$q = "SELECT TRIM(RDB\$TRIGGER_NAME)
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0";
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = [];
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
return $triggers;
}
/**
* Returns metadata from stored procedures and their input and output parameters.
* @param string
* @return array
*/
public function getProceduresMeta()
{
$res = $this->query("
SELECT
TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME,
TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME,
CASE p.RDB\$PARAMETER_TYPE
WHEN 0 THEN 'INPUT'
WHEN 1 THEN 'OUTPUT'
ELSE 'UNKNOWN'
END AS PARAMETER_TYPE,
CASE f.RDB\$FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'INT64'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS FIELD_TYPE,
f.RDB\$FIELD_LENGTH AS FIELD_LENGTH,
p.RDB\$PARAMETER_NUMBER AS PARAMETER_NUMBER
FROM RDB\$PROCEDURE_PARAMETERS p
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
$num = $row['PARAMETER_NUMBER'];
$procedures[$key]['name'] = $row['PROCEDURE_NAME'];
$procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME'];
$procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
$procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
}
return $procedures;
}
/**
* Returns list of stored procedures.
* @return array
*/
public function getProcedures()
{
$res = $this->query("
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = [];
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
return $procedures;
}
/**
* Returns list of generators.
* @return array
*/
public function getGenerators()
{
$res = $this->query("
SELECT TRIM(RDB\$GENERATOR_NAME)
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = [];
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
return $generators;
}
/**
* Returns list of user defined functions (UDF).
* @return array
*/
public function getFunctions()
{
$res = $this->query("
SELECT TRIM(RDB\$FUNCTION_NAME)
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = [];
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
return $functions;
}
}

View File

@@ -0,0 +1,386 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for MS SQL database.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class MsSqlDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new Dibi\NotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @mssql_connect($config['host'], $config['username'], $config['password'], TRUE); // intentionally @
} else {
$this->connection = @mssql_pconnect($config['host'], $config['username'], $config['password']); // intentionally @
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($this->escapeIdentifier($config['database']), $this->connection)) { // intentionally @
throw new Dibi\DriverException("Can't select DB '$config[database]'.");
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@mssql_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($res === FALSE) {
throw new Dibi\DriverException(mssql_get_last_message(), 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return mssql_rows_affected($this->connection);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
$res = mssql_query('SELECT @@IDENTITY', $this->connection);
if (is_resource($res)) {
$row = mssql_fetch_row($res);
return $row[0];
}
return FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
$this->query('BEGIN TRANSACTION');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
$this->query('COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
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 Dibi\Reflector
*/
public function getReflector()
{
return new MsSqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return mssql_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return mssql_fetch_array($this->resultSet, $assoc ? MSSQL_ASSOC : MSSQL_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
return mssql_data_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
mssql_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$columns[] = [
'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'],
'nativetype' => $row['type'],
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -0,0 +1,216 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MS SQL databases.
* @internal
*/
class MsSqlReflector implements Dibi\Reflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query('
SELECT TABLE_NAME, TABLE_TYPE
FROM INFORMATION_SCHEMA.TABLES
');
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = [
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
}
return $tables;
}
/**
* Returns count of rows in a table
* @param string
* @return int
*/
public function getTableCount($table, $fallback = TRUE)
{
if (empty($table)) {
return FALSE;
}
$result = $this->driver->query("
SELECT MAX(rowcnt)
FROM sys.sysindexes
WHERE id=OBJECT_ID({$this->driver->escapeIdentifier($table)})
");
$row = $result->fetch(FALSE);
if (!is_array($row) || count($row) < 1) {
if ($fallback) {
$row = $this->driver->query("SELECT COUNT(*) FROM {$this->driver->escapeIdentifier($table)}")->fetch(FALSE);
$count = intval($row[0]);
} else {
$count = FALSE;
}
} else {
$count = intval($row[0]);
}
return $count;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = $this->driver->query("
SELECT * FROM
INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = {$this->driver->escapeText($table)}
ORDER BY TABLE_NAME, ORDINAL_POSITION
");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$size = FALSE;
$type = strtoupper($row['DATA_TYPE']);
$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]) {
$size = $row[$size_cols[$type]];
}
}
$columns[] = [
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $type,
'size' => $size,
'unsigned' => NULL,
'nullable' => $row['IS_NULLABLE'] === 'YES',
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => FALSE,
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query(
"SELECT ind.name index_name, ind.index_id, ic.index_column_id,
col.name column_name, ind.is_unique, ind.is_primary_key
FROM sys.indexes ind
INNER JOIN sys.index_columns ic ON
(ind.object_id = ic.object_id AND ind.index_id = ic.index_id)
INNER JOIN sys.columns col ON
(ic.object_id = col.object_id and ic.column_id = col.column_id)
INNER JOIN sys.tables t ON
(ind.object_id = t.object_id)
WHERE t.name = {$this->driver->escapeText($table)}
AND t.is_ms_shipped = 0
ORDER BY
t.name, ind.name, ind.index_id, ic.index_column_id
");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$index_name = $row['index_name'];
if (!isset($indexes[$index_name])) {
$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'] = [];
}
$indexes[$index_name]['columns'][] = $row['column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$res = $this->driver->query("
SELECT f.name AS foreign_key,
OBJECT_NAME(f.parent_object_id) AS table_name,
COL_NAME(fc.parent_object_id,
fc.parent_column_id) AS column_name,
OBJECT_NAME (f.referenced_object_id) AS reference_table_name,
COL_NAME(fc.referenced_object_id,
fc.referenced_column_id) AS reference_column_name,
fc.*
FROM sys.foreign_keys AS f
INNER JOIN sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
WHERE OBJECT_NAME(f.parent_object_id) = {$this->driver->escapeText($table)}
");
$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'] = [$row['column_name']]; // local columns
$keys[$key_name]['table'] = $row['reference_table_name']; // referenced table
$keys[$key_name]['foreign'] = [$row['reference_column_name']]; // referenced columns
$keys[$key_name]['onDelete'] = FALSE;
$keys[$key_name]['onUpdate'] = FALSE;
} else {
$keys[$key_name]['local'][] = $row['column_name']; // local columns
$keys[$key_name]['foreign'][] = $row['reference_column_name']; // referenced columns
}
}
return array_values($keys);
}
}

View File

@@ -0,0 +1,480 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for MySQL database.
*
* Driver options:
* - host => the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - flags (int) => driver specific constants (MYSQL_CLIENT_*)
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
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 */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysql')) {
throw new Dibi\NotSupportedException("PHP extension 'mysql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
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) {
$config['host'] = $host;
$config['port'] = ini_get('mysql.default_port');
} else {
if (!isset($config['socket'])) {
$config['socket'] = ini_get('mysql.default_socket');
}
$config['host'] = NULL;
}
}
if (empty($config['socket'])) {
$host = $config['host'] . (empty($config['port']) ? '' : ':' . $config['port']);
} else {
$host = ':' . $config['socket'];
}
if (empty($config['persistent'])) {
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @
} else {
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
}
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(mysql_error(), mysql_errno());
}
if (isset($config['charset'])) {
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 Dibi\DriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
}
if (isset($config['sqlmode'])) {
$this->query("SET sql_mode='$config[sqlmode]'");
}
if (isset($config['timezone'])) {
$this->query("SET time_zone='$config[timezone]'");
}
$this->buffered = empty($config['unbuffered']);
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@mysql_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
if ($this->buffered) {
$res = @mysql_query($sql, $this->connection); // intentionally @
} else {
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
}
if ($code = mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
/**
* Retrieves information about the most recently executed query.
* @return array
*/
public function getInfo()
{
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
return $res;
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return mysql_affected_rows($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 mysql_insert_id($this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
}
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
}
public function escapeIdentifier($value)
{
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($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 : '');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysql_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return mysql_fetch_array($this->resultSet, $assoc ? MYSQL_ASSOC : MYSQL_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
*/
public function seek($row)
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
return mysql_data_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
mysql_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$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;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -0,0 +1,135 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for MySQL databases.
* @internal
*/
class MySqlReflector implements Dibi\Reflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query('SHOW FULL TABLES');
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = [
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = $this->driver->query("SHOW FULL COLUMNS FROM {$this->driver->escapeIdentifier($table)}");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$columns[] = [
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'unsigned' => (bool) strstr($row['Type'], 'unsigned'),
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$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'];
$indexes[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$indexes[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
* @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->escapeText($table)}")->fetch(TRUE);
if ($data['ENGINE'] !== 'InnoDB') {
throw new Dibi\NotSupportedException("Foreign keys are not supported in {$data['ENGINE']} tables.");
}
$res = $this->driver->query("
SELECT rc.CONSTRAINT_NAME, rc.UPDATE_RULE, rc.DELETE_RULE, kcu.REFERENCED_TABLE_NAME,
GROUP_CONCAT(kcu.REFERENCED_COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) AS REFERENCED_COLUMNS,
GROUP_CONCAT(kcu.COLUMN_NAME ORDER BY kcu.ORDINAL_POSITION) AS COLUMNS
FROM information_schema.REFERENTIAL_CONSTRAINTS rc
INNER JOIN information_schema.KEY_COLUMN_USAGE kcu ON
kcu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
AND kcu.CONSTRAINT_SCHEMA = rc.CONSTRAINT_SCHEMA
WHERE rc.CONSTRAINT_SCHEMA = DATABASE()
AND rc.TABLE_NAME = {$this->driver->escapeText($table)}
GROUP BY rc.CONSTRAINT_NAME
");
$foreignKeys = [];
while ($row = $res->fetch(TRUE)) {
$keyName = $row['CONSTRAINT_NAME'];
$foreignKeys[$keyName]['name'] = $keyName;
$foreignKeys[$keyName]['local'] = explode(',', $row['COLUMNS']);
$foreignKeys[$keyName]['table'] = $row['REFERENCED_TABLE_NAME'];
$foreignKeys[$keyName]['foreign'] = explode(',', $row['REFERENCED_COLUMNS']);
$foreignKeys[$keyName]['onDelete'] = $row['DELETE_RULE'];
$foreignKeys[$keyName]['onUpdate'] = $row['UPDATE_RULE'];
}
return array_values($foreignKeys);
}
}

View File

@@ -0,0 +1,499 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for MySQL database via improved extension.
*
* Driver options:
* - host => the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options}
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect}
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
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 */
private $connection;
/** @var mysqli_result Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysqli')) {
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$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) {
$config['host'] = $host;
$config['port'] = ini_get('mysqli.default_port');
} else {
$config['host'] = NULL;
$config['port'] = NULL;
}
}
$foo = & $config['flags'];
$foo = & $config['database'];
$this->connection = mysqli_init();
if (isset($config['options'])) {
if (is_scalar($config['options'])) {
$config['flags'] = $config['options']; // back compatibility
trigger_error(__CLASS__ . ": configuration item 'options' must be array; for constants MYSQLI_CLIENT_* use 'flags'.", E_USER_NOTICE);
} else {
foreach ((array) $config['options'] as $key => $value) {
mysqli_options($this->connection, $key, $value);
}
}
}
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new Dibi\DriverException(mysqli_connect_error(), $errno);
}
}
if (isset($config['charset'])) {
if (!@mysqli_set_charset($this->connection, $config['charset'])) {
$this->query("SET NAMES '$config[charset]'");
}
}
if (isset($config['sqlmode'])) {
$this->query("SET sql_mode='$config[sqlmode]'");
}
if (isset($config['timezone'])) {
$this->query("SET time_zone='$config[timezone]'");
}
$this->buffered = empty($config['unbuffered']);
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@mysqli_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @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 ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
}
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1216, 1217, 1451, 1452, 1701], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1062, 1557, 1569, 1586], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1048, 1121, 1138, 1171, 1252, 1263, 1566], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Retrieves information about the most recently executed query.
* @return array
*/
public function getInfo()
{
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) {
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
return $res;
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return mysqli_affected_rows($this->connection) === -1 ? FALSE : mysqli_affected_rows($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 mysqli_insert_id($this->connection);
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
* @return mysqli
*/
public function getResource()
{
return @$this->connection->thread_id ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return Dibi\ResultDriver
*/
public function createResultDriver(\mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
public function escapeBinary($value)
{
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
}
public function escapeIdentifier($value)
{
return '`' . str_replace('`', '``', $value) . '`';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($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 : '');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && @$this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysqli_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return mysqli_fetch_array($this->resultSet, $assoc ? MYSQLI_ASSOC : MYSQLI_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Dibi\Exception
*/
public function seek($row)
{
if (!$this->buffered) {
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
return mysqli_data_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
mysqli_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
static $types;
if ($types === NULL) {
$consts = get_defined_constants(TRUE);
$types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12);
}
}
$types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
}
$count = mysqli_num_fields($this->resultSet);
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$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;
}
/**
* Returns the result set resource.
* @return mysqli_result
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
}
}

View File

@@ -0,0 +1,496 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver interacting with databases via ODBC connections.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var int Cursor */
private $row = 0;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('odbc')) {
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
$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 @
} else {
$this->connection = @odbc_pconnect($config['dsn'], $config['username'], $config['password']); // intentionally @
}
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@odbc_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) {
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);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.');
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!odbc_autocommit($this->connection, FALSE)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!odbc_commit($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!odbc_rollback($this->connection)) {
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !odbc_autocommit($this->connection);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier($value)
{
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("#m/d/Y#");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("#m/d/Y H:i:s#");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
// will return -1 with many drivers :-(
return odbc_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)
{
if ($assoc) {
return odbc_fetch_array($this->resultSet, ++$this->row);
} else {
$set = $this->resultSet;
if (!odbc_fetch_row($set, ++$this->row)) {
return FALSE;
}
$count = odbc_num_fields($set);
$cols = [];
for ($i = 1; $i <= $count; $i++) {
$cols[] = odbc_result($set, $i);
}
return $cols;
}
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
$this->row = $row;
return TRUE;
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
odbc_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$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;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* Dibi\Reflector ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = odbc_tables($this->connection);
$tables = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = [
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
];
}
}
odbc_free_result($res);
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = odbc_columns($this->connection);
$columns = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$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);
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
}
}

View File

@@ -0,0 +1,494 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Oracle database.
*
* Driver options:
* - database => the name of the local Oracle instance or the name of the entry in tnsnames.ora
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - schema => alters session schema
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - resource (resource) => existing connection resource
* - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool */
private $autocommit = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var int|FALSE Number of affected rows */
private $affectedRows = FALSE;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('oci8')) {
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
$foo = & $config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (empty($config['persistent'])) {
$this->connection = @oci_new_connect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
} else {
$this->connection = @oci_pconnect($config['username'], $config['password'], $config['database'], $config['charset']); // intentionally @
}
if (!$this->connection) {
$err = oci_error();
throw new Dibi\DriverException($err['message'], $err['code']);
}
if (isset($config['schema'])) {
$this->query('ALTER SESSION SET CURRENT_SCHEMA=' . $config['schema']);
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@oci_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @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 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 Dibi\DriverException($err['message'], $err['code'], $sql);
}
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if (in_array($code, [1, 2299, 38911], TRUE)) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [1400], TRUE)) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (in_array($code, [2266, 2291, 2292], TRUE)) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
$row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(TRUE);
return isset($row['ID']) ? (int) $row['ID'] : FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
*/
public function begin($savepoint = NULL)
{
$this->autocommit = FALSE;
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!oci_commit($this->connection)) {
$err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
}
public function escapeIdentifier($value)
{
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} 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 !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return oci_fetch_array($this->resultSet, ($assoc ? OCI_ASSOC : OCI_NUM) | OCI_RETURN_NULLS);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotImplementedException;
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
oci_free_statement($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$type = oci_field_type($this->resultSet, $i);
$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;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* Dibi\Reflector ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->query('SELECT * FROM cat');
$tables = [];
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'VIEW',
];
}
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
}
}

View File

@@ -0,0 +1,558 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use PDO;
/**
* The dibi driver for PDO.
*
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - options (array) => driver specific options {@see PDO::__construct}
* - resource (PDO) => existing connection
* - version
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class PdoDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var PDO Connection resource */
private $connection;
/** @var \PDOStatement Resultset resource */
private $resultSet;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/** @var string */
private $serverVersion;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new Dibi\NotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
$foo = & $config['dsn'];
$foo = & $config['options'];
Dibi\Helpers::alias($config, 'resource', 'pdo');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
unset($config['resource'], $config['pdo']);
} else {
try {
$this->connection = new PDO($config['dsn'], $config['username'], $config['password'], $config['options']);
} catch (\PDOException $e) {
if ($e->getMessage() === 'could not find driver') {
throw new Dibi\NotSupportedException('PHP extension for PDO is not loaded.');
}
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
$this->serverVersion = isset($config['version'])
? $config['version']
: @$this->connection->getAttribute(PDO::ATTR_SERVER_VERSION); // @ - may be not supported
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection = NULL;
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
// must detect if SQL returns result set or num of affected rows
$cmd = strtoupper(substr(ltrim($sql), 0, 6));
static $list = ['UPDATE' => 1, 'DELETE' => 1, 'INSERT' => 1, 'REPLAC' => 1];
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows !== FALSE) {
return;
}
} else {
$res = $this->connection->query($sql);
if ($res) {
return $this->createResultDriver($res);
}
}
list($sqlState, $code, $message) = $this->connection->errorInfo();
$message = "SQLSTATE[$sqlState]: $message";
switch ($this->driverName) {
case 'mysql':
throw MySqliDriver::createException($message, $code, $sql);
case 'oci':
throw OracleDriver::createException($message, $code, $sql);
case 'pgsql':
throw PostgreDriver::createException($message, $sqlState, $sql);
case 'sqlite':
throw Sqlite3Driver::createException($message, $code, $sql);
default:
throw new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertId();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!$this->connection->beginTransaction()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!$this->connection->commit()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!$this->connection->rollBack()) {
$err = $this->connection->errorInfo();
throw new Dibi\DriverException("SQLSTATE[$err[0]]: $err[2]", $err[1]);
}
}
/**
* Returns the connection resource.
* @return PDO
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
switch ($this->driverName) {
case 'mysql':
return new MySqlReflector($this);
case 'sqlite':
return new SqliteReflector($this);
default:
throw new Dibi\NotSupportedException;
}
}
/**
* Result set driver factory.
* @param \PDOStatement
* @return Dibi\ResultDriver
*/
public function createResultDriver(\PDOStatement $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @return string encoded value
*/
public function escapeText($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_STR);
}
}
public function escapeBinary($value)
{
if ($this->driverName === 'odbc') {
return "'" . str_replace("'", "''", $value) . "'";
} else {
return $this->connection->quote($value, PDO::PARAM_LOB);
}
}
public function escapeIdentifier($value)
{
switch ($this->driverName) {
case 'mysql':
return '`' . str_replace('`', '``', $value) . '`';
case 'oci':
case 'pgsql':
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'mssql':
return '[' . str_replace(['[', ']'], ['[[', ']]'], $value) . ']';
case 'dblib':
case 'sqlsrv':
return '[' . str_replace(']', ']]', $value) . ']';
default:
return $value;
}
}
public function escapeBool($value)
{
if ($this->driverName === 'pgsql') {
return $value ? 'TRUE' : 'FALSE';
} else {
return $value ? 1 : 0;
}
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? '#m/d/Y#' : "'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->driverName === 'odbc' ? "#m/d/Y H:i:s#" : "'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
switch ($this->driverName) {
case 'mysql':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'oci':
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'pgsql':
$bs = substr($this->connection->quote('\\', PDO::PARAM_STR), 1, -1); // standard_conforming_strings = on/off
$value = substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
case 'sqlite':
$value = addcslashes(substr($this->connection->quote($value, PDO::PARAM_STR), 1, -1), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
case 'odbc':
case 'mssql':
case 'dblib':
case 'sqlsrv':
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
default:
throw new Dibi\NotImplementedException;
}
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
switch ($this->driverName) {
case 'mysql':
if ($limit !== NULL || $offset) {
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit === NULL ? '18446744073709551615' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'pgsql':
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
break;
case 'sqlite':
if ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
break;
case 'oci':
if ($offset) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t '
. ($limit !== NULL ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '')
. ') WHERE "__rnum" > '. (int) $offset;
} elseif ($limit !== NULL) {
$sql = 'SELECT * FROM (' . $sql . ') WHERE ROWNUM <= ' . (int) $limit;
}
break;
case 'mssql':
case 'sqlsrv':
case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
if ($limit !== NULL) {
$sql = sprintf('%s OFFSET %d ROWS FETCH NEXT %d ROWS ONLY', rtrim($sql), $offset, $limit);
} elseif ($offset) {
$sql = sprintf('%s OFFSET %d ROWS', rtrim($sql), $offset);
}
break;
}
// intentionally break omitted
case 'odbc':
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
break;
}
// intentionally break omitted
default:
throw new Dibi\NotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
/********************* result set ****************d*g**/
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return $this->resultSet->rowCount();
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return $this->resultSet->fetch($assoc ? PDO::FETCH_ASSOC : PDO::FETCH_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws Dibi\Exception
*/
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new Dibi\NotSupportedException('Driver does not support meta data.');
}
$row = $row + [
'table' => NULL,
'native_type' => 'VAR_STRING',
];
$columns[] = [
'name' => $row['name'],
'table' => $row['table'],
'nativetype' => $row['native_type'],
'type' => $row['native_type'] === 'TIME' && $this->driverName === 'mysql' ? Dibi\Type::TIME_INTERVAL : NULL,
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return \PDOStatement
*/
public function getResultResource()
{
return $this->resultSet;
}
}

View File

@@ -0,0 +1,720 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for PostgreSQL database.
*
* Driver options:
* - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API
* - string => or use connection string
* - schema => the schema search path
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pgsql')) {
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
$error = NULL;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$config += [
'charset' => 'utf8',
];
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
Dibi\Helpers::alias($config, 'user', 'username');
Dibi\Helpers::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
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);
}
restore_error_handler();
}
if (!is_resource($this->connection)) {
throw new Dibi\DriverException($error ?: 'Connecting error.');
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw self::createException(pg_last_error($this->connection));
}
if (isset($config['schema'])) {
$this->query('SET search_path TO "' . $config['schema'] . '"');
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@pg_close($this->connection); // @ - connection can be already disconnected
}
/**
* Pings database.
* @return bool
*/
public function ping()
{
return pg_ping($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw self::createException(pg_last_error($this->connection), NULL, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
if (pg_num_fields($res)) {
return $this->createResultDriver($res);
}
}
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code = NULL, $sql = NULL)
{
if ($code === NULL && preg_match('#^ERROR:\s+(\S+):\s*#', $message, $m)) {
$code = $m[1];
$message = substr($message, strlen($m[0]));
}
if ($code === '0A000' && strpos($message, 'truncate') !== FALSE) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23502') {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif ($code === '23503') {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} elseif ($code === '23505') {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\DriverException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
if ($sequence === NULL) {
// PostgreSQL 8.1 is needed
$res = $this->query('SELECT LASTVAL()');
} else {
$res = $this->query("SELECT CURRVAL('$sequence')");
}
if (!$res) {
return FALSE;
}
$row = $res->fetch(FALSE);
return is_array($row) ? $row[0] : FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
}
public function escapeBinary($value)
{
if (!is_resource($this->connection)) {
throw new Dibi\Exception('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
}
public function escapeIdentifier($value)
{
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
}
public function escapeBool($value)
{
return $value ? 'TRUE' : 'FALSE';
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$bs = pg_escape_string($this->connection, '\\'); // standard_conforming_strings = on/off
$value = pg_escape_string($this->connection, $value);
$value = strtr($value, ['%' => $bs . '%', '_' => $bs . '_', '\\' => '\\\\']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
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.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
}
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return pg_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return pg_fetch_array($this->resultSet, NULL, $assoc ? PGSQL_ASSOC : PGSQL_NUM);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
return pg_result_seek($this->resultSet, $row);
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
pg_free_result($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = pg_num_fields($this->resultSet);
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = [
'name' => pg_field_name($this->resultSet, $i),
'table' => pg_field_table($this->resultSet, $i),
'nativetype' => pg_field_type($this->resultSet, $i),
];
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$columns[] = $row;
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* Dibi\Reflector ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$version = pg_parameter_status($this->getResource(), 'server_version');
if ($version < 7.4) {
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
}
$query = "
SELECT
table_name AS name,
CASE table_type
WHEN 'VIEW' THEN 1
ELSE 0
END AS view
FROM
information_schema.tables
WHERE
table_schema = ANY (current_schemas(false))";
if ($version >= 9.3) {
$query .= '
UNION ALL
SELECT
matviewname, 1
FROM
pg_matviews
WHERE
schemaname = ANY (current_schemas(false))';
}
$res = $this->query($query);
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : [];
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT indkey
FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid AND pg_index.indisprimary
WHERE pg_class.oid = $_table::regclass
");
$primary = (int) pg_fetch_object($res->resultSet)->indkey;
$res = $this->query("
SELECT *
FROM information_schema.columns c
JOIN pg_class ON pg_class.relname = c.table_name
JOIN pg_namespace nsp ON nsp.oid = pg_class.relnamespace AND nsp.nspname = c.table_schema
WHERE pg_class.oid = $_table::regclass
ORDER BY c.ordinal_position
");
if (!$res->getRowCount()) {
$res = $this->query("
SELECT
a.attname AS column_name,
pg_type.typname AS udt_name,
a.attlen AS numeric_precision,
a.atttypmod-4 AS character_maximum_length,
NOT a.attnotnull AS is_nullable,
a.attnum AS ordinal_position,
adef.adsrc AS column_default
FROM
pg_attribute a
JOIN pg_type ON a.atttypid = pg_type.oid
JOIN pg_class cls ON a.attrelid = cls.oid
LEFT JOIN pg_attrdef adef ON adef.adnum = a.attnum AND adef.adrelid = a.attrelid
WHERE
cls.relkind IN ('r', 'v', 'mv')
AND a.attrelid = $_table::regclass
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY ordinal_position
");
}
$columns = [];
while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$columns[] = [
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
'size' => $size > 0 ? $size : NULL,
'nullable' => $row['is_nullable'] === 'YES' || $row['is_nullable'] === 't',
'default' => $row['column_default'],
'autoincrement' => (int) $row['ordinal_position'] === $primary && substr($row['column_default'], 0, 7) === 'nextval',
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT
a.attnum AS ordinal_position,
a.attname AS column_name
FROM
pg_attribute a
JOIN pg_class cls ON a.attrelid = cls.oid
WHERE
a.attrelid = $_table::regclass
AND a.attnum > 0
AND NOT a.attisdropped
ORDER BY ordinal_position
");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
$res = $this->query("
SELECT pg_class2.relname, indisunique, indisprimary, indkey
FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
INNER JOIN pg_class as pg_class2 on pg_class2.oid = pg_index.indexrelid
WHERE pg_class.oid = $_table::regclass
");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't';
foreach (explode(' ', $row['indkey']) as $index) {
$indexes[$row['relname']]['columns'][] = $columns[$index];
}
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT
c.conname AS name,
lt.attname AS local,
c.confrelid::regclass AS table,
ft.attname AS foreign,
CASE c.confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onUpdate\",
CASE c.confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onDelete\",
c.conkey,
lt.attnum AS lnum,
c.confkey,
ft.attnum AS fnum
FROM
pg_constraint c
JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey)
JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey)
WHERE
c.contype = 'f'
AND
c.conrelid = $_table::regclass
");
$fKeys = $references = [];
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = [
'name' => $row['name'],
'table' => $row['table'],
'local' => [],
'foreign' => [],
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
];
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));
$references[$row['name']] = array_combine($l, $f);
}
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
$fKeys[$row['name']]['local'][] = $row['local'];
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
}
}
return $fKeys;
}
}

View File

@@ -0,0 +1,472 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use SQLite3;
/**
* The dibi driver for SQLite3 database.
*
* Driver options:
* - database (or file) => the filename of the SQLite3 database
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var SQLite3 Connection resource */
private $connection;
/** @var \SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
Dibi\Helpers::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else {
try {
$this->connection = new SQLite3($config['database']);
} catch (\Exception $e) {
throw new Dibi\DriverException($e->getMessage(), $e->getCode());
}
}
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL;
}
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query('PRAGMA foreign_keys = ON');
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection->close();
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
if ($this->dbcharset !== NULL) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
}
$res = @$this->connection->query($sql); // intentionally @
if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof \SQLite3Result) {
return $this->createResultDriver($res);
}
}
/**
* @return Dibi\DriverException
*/
public static function createException($message, $code, $sql)
{
if ($code !== 19) {
return new Dibi\DriverException($message, $code, $sql);
} elseif (strpos($message, 'must be unique') !== FALSE
|| strpos($message, 'is not unique') !== FALSE
|| strpos($message, 'UNIQUE constraint failed') !== FALSE
) {
return new Dibi\UniqueConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'may not be NULL') !== FALSE
|| strpos($message, 'NOT NULL constraint failed') !== FALSE
) {
return new Dibi\NotNullConstraintViolationException($message, $code, $sql);
} elseif (strpos($message, 'foreign key constraint failed') !== FALSE
|| strpos($message, 'FOREIGN KEY constraint failed') !== FALSE
) {
return new Dibi\ForeignKeyConstraintViolationException($message, $code, $sql);
} else {
return new Dibi\ConstraintViolationException($message, $code, $sql);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->connection->changes();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertRowID();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return new SqliteReflector($this);
}
/**
* Result set driver factory.
* @param \SQLite3Result
* @return Dibi\ResultDriver
*/
public function createResultDriver(\SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . $this->connection->escapeString($value) . "'";
}
public function escapeBinary($value)
{
return "X'" . bin2hex((string) $value) . "'";
}
public function escapeIdentifier($value)
{
return '[' . strtr($value, '[]', ' ') . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDate);
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format($this->fmtDateTime);
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->resultSet && @$this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
* @throws Dibi\NotSupportedException
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = [];
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(['[', ']'], '', $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 Dibi\NotSupportedException
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
$this->resultSet->finalize();
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
for ($i = 0; $i < $count; $i++) {
$columns[] = [
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
* @param string function name
* @param mixed callback
* @param int num of arguments
* @return void
*/
public function registerFunction($name, callable $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
* @param string function name
* @param mixed callback called for each row of the result set
* @param mixed callback called to aggregate the "stepped" data from each row
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, callable $rowCallback, callable $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -0,0 +1,154 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for SQLite database.
* @internal
*/
class SqliteReflector implements Dibi\Reflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query("
SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
UNION ALL
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = [];
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$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[] = [
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list({$this->driver->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->escapeIdentifier($index)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
}
$columns = $this->getColumns($table);
foreach ($indexes as $index => $values) {
$column = $indexes[$index]['columns'][0];
$primary = FALSE;
foreach ($columns as $info) {
if ($column == $info['name']) {
$primary = $info['vendor']['pk'];
break;
}
}
$indexes[$index]['primary'] = (bool) $primary;
}
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = [
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => [$column['name']],
];
break;
}
}
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
$res = $this->driver->query("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
$keys[$row['id']]['table'] = $row['table']; // referenced table
$keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns
$keys[$row['id']]['onDelete'] = $row['on_delete'];
$keys[$row['id']]['onUpdate'] = $row['on_update'];
if ($keys[$row['id']]['foreign'][0] == NULL) {
$keys[$row['id']]['foreign'] = NULL;
}
}
return array_values($keys);
}
}

View File

@@ -0,0 +1,416 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use Dibi\Connection;
use Dibi\Helpers;
/**
* 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)
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => connection options {@link https://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $version;
/**
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws Dibi\Exception
*/
public function connect(array & $config)
{
Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
$options = $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 Dibi\DriverException($info[0]['message'], $info[0]['code']);
}
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
@sqlsrv_close($this->connection); // @ - connection can be already disconnected
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$this->affectedRows = FALSE;
$res = sqlsrv_query($this->connection, $sql);
if ($res === FALSE) {
$info = sqlsrv_errors();
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);
}
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
public function getAffectedRows()
{
return $this->affectedRows;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
$res = sqlsrv_query($this->connection, 'SELECT SCOPE_IDENTITY()');
if (is_resource($res)) {
$row = sqlsrv_fetch_array($res, SQLSRV_FETCH_NUMERIC);
return $row[0];
}
return FALSE;
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
sqlsrv_begin_transaction($this->connection);
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
sqlsrv_commit($this->connection);
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
sqlsrv_rollback($this->connection);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return Dibi\Reflector
*/
public function getReflector()
{
return new SqlsrvReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return Dibi\ResultDriver
*/
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
* @return string encoded value
*/
public function escapeText($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier($value)
{
// @see https://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(']', ']]', $value) . ']';
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string
* @return string
*/
public function unescapeBinary($value)
{
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} 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);
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
return sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
public function free()
{
sqlsrv_free_stmt($this->resultSet);
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$columns = [];
foreach ((array) sqlsrv_field_metadata($this->resultSet) as $fieldMetadata) {
$columns[] = [
'name' => $fieldMetadata['Name'],
'fullname' => $fieldMetadata['Name'],
'nativetype' => $fieldMetadata['Type'],
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for Microsoft SQL Server and SQL Azure databases.
* @internal
*/
class SqlsrvReflector implements Dibi\Reflector
{
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$res = $this->driver->query("SELECT TABLE_NAME, TABLE_TYPE FROM INFORMATION_SCHEMA.TABLES WHERE [TABLE_SCHEMA] = 'dbo'");
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = [
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
];
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$res = $this->driver->query("
SELECT c.name as COLUMN_NAME, c.is_identity AS AUTO_INCREMENT
FROM sys.columns c
INNER JOIN sys.tables t ON c.object_id = t.object_id
WHERE t.name = {$this->driver->escapeText($table)}
");
$autoIncrements = [];
while ($row = $res->fetch(TRUE)) {
$autoIncrements[$row['COLUMN_NAME']] = (bool) $row['AUTO_INCREMENT'];
}
$res = $this->driver->query("
SELECT C.COLUMN_NAME, C.DATA_TYPE, C.CHARACTER_MAXIMUM_LENGTH , C.COLUMN_DEFAULT , C.NUMERIC_PRECISION, C.NUMERIC_SCALE , C.IS_NULLABLE, Case When Z.CONSTRAINT_NAME Is Null Then 0 Else 1 End As IsPartOfPrimaryKey
FROM INFORMATION_SCHEMA.COLUMNS As C
Outer Apply (
SELECT CCU.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS As TC
Join INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE As CCU
On CCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
WHERE TC.TABLE_SCHEMA = C.TABLE_SCHEMA
And TC.TABLE_NAME = C.TABLE_NAME
And TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
And CCU.COLUMN_NAME = C.COLUMN_NAME
) As Z
WHERE C.TABLE_NAME = {$this->driver->escapeText($table)}
");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[] = [
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => strtoupper($row['DATA_TYPE']),
'size' => $row['CHARACTER_MAXIMUM_LENGTH'],
'unsigned' => TRUE,
'nullable' => $row['IS_NULLABLE'] === 'YES',
'default' => $row['COLUMN_DEFAULT'],
'autoincrement' => $autoIncrements[$row['COLUMN_NAME']],
'vendor' => $row,
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$keyUsagesRes = $this->driver->query(sprintf("EXEC [sys].[sp_helpindex] @objname = N%s", $this->driver->escapeText($table)));
$keyUsages = [];
while ($row = $keyUsagesRes->fetch(TRUE)) {
$keyUsages[$row['index_name']] = explode(',', $row['index_keys']);
}
$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['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);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new Dibi\NotImplementedException;
}
}

98
src/Dibi/Event.php Normal file
View File

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

521
src/Dibi/Fluent.php Normal file
View File

@@ -0,0 +1,521 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @method Fluent select(...$field)
* @method Fluent distinct()
* @method Fluent from($table)
* @method Fluent where(...$cond)
* @method Fluent groupBy(...$field)
* @method Fluent having(...$cond)
* @method Fluent orderBy(...$field)
* @method Fluent limit(int $limit)
* @method Fluent offset(int $offset)
* @method Fluent join(...$table)
* @method Fluent leftJoin(...$table)
* @method Fluent innerJoin(...$table)
* @method Fluent rightJoin(...$table)
* @method Fluent outerJoin(...$table)
* @method Fluent as(...$field)
* @method Fluent on(...$cond)
* @method Fluent using(...$cond)
*/
class Fluent implements IDataSource
{
use Strict;
const REMOVE = FALSE;
/** @var array */
public static $masks = [
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'],
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'],
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'],
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'],
];
/** @var array default modifiers for arrays */
public static $modifiers = [
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%in',
'VALUES' => '%l',
'SET' => '%a',
'WHERE' => '%and',
'HAVING' => '%and',
'ORDER BY' => '%by',
'GROUP BY' => '%by',
];
/** @var array clauses separators */
public static $separators = [
'SELECT' => ',',
'FROM' => ',',
'WHERE' => 'AND',
'GROUP BY' => ',',
'HAVING' => 'AND',
'ORDER BY' => ',',
'LIMIT' => FALSE,
'OFFSET' => FALSE,
'SET' => ',',
'VALUES' => ',',
'INTO' => FALSE,
];
/** @var array clauses */
public static $clauseSwitches = [
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
];
/** @var Connection */
private $connection;
/** @var array */
private $setups = [];
/** @var string */
private $command;
/** @var array */
private $clauses = [];
/** @var array */
private $flags = [];
/** @var array */
private $cursor;
/** @var HashMap normalized clauses */
private static $normalizer;
/**
* @param Connection
*/
public function __construct(Connection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
}
}
/**
* Appends new argument to the clause.
* @param string clause name
* @param array arguments
* @return self
*/
public function __call($clause, $args)
{
$clause = self::$normalizer->$clause;
// lazy initialization
if ($this->command === NULL) {
if (isset(self::$masks[$clause])) {
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
}
$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]];
}
if (array_key_exists($clause, $this->clauses)) {
// append to clause
$this->cursor = & $this->clauses[$clause];
// TODO: really delete?
if ($args === [self::REMOVE]) {
$this->cursor = NULL;
return $this;
}
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace
$this->cursor = [];
} elseif (!empty($this->cursor)) {
$this->cursor[] = $sep;
}
}
} else {
// append to currect flow
if ($args === [self::REMOVE]) {
return $this;
}
$this->cursor[] = $clause;
}
if ($this->cursor === NULL) {
$this->cursor = [];
}
// special types or argument
if (count($args) === 1) {
$arg = $args[0];
// TODO: really ignore TRUE?
if ($arg === TRUE) { // flag
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = [$clause === 'AS' ? '%N' : '%n', $arg];
} elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = [self::$modifiers[$clause], $arg];
} elseif (is_string(key($arg))) { // associative array
$args = ['%a', $arg];
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = new Literal("($arg)");
}
$this->cursor[] = $arg;
}
return $this;
}
/**
* Switch to a clause.
* @param string clause name
* @return self
*/
public function clause($clause)
{
$this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($this->cursor === NULL) {
$this->cursor = [];
}
return $this;
}
/**
* Removes a clause.
* @param string clause name
* @return self
*/
public function removeClause($clause)
{
$this->clauses[self::$normalizer->$clause] = NULL;
return $this;
}
/**
* Change a SQL flag.
* @param string flag name
* @param bool value
* @return self
*/
public function setFlag($flag, $value = TRUE)
{
$flag = strtoupper($flag);
if ($value) {
$this->flags[$flag] = TRUE;
} else {
unset($this->flags[$flag]);
}
return $this;
}
/**
* Is a flag set?
* @param string flag name
* @return bool
*/
final public function getFlag($flag)
{
return isset($this->flags[strtoupper($flag)]);
}
/**
* Returns SQL command.
* @return string
*/
final public function getCommand()
{
return $this->command;
}
/**
* Returns the dibi connection.
* @return Connection
*/
final public function getConnection()
{
return $this->connection;
}
/**
* Adds Result setup.
* @param string method
* @param mixed args
* @return self
*/
public function setupResult($method)
{
$this->setups[] = func_get_args();
return $this;
}
/********************* executing ****************d*g**/
/**
* Generates and executes SQL query.
* @param mixed what to return?
* @return Result|int result set object (if any)
* @throws Exception
*/
public function execute($return = NULL)
{
$res = $this->query($this->_export());
switch ($return) {
case \dibi::IDENTIFIER:
return $this->connection->getInsertId();
case \dibi::AFFECTED_ROWS:
return $this->connection->getAffectedRows();
default:
return $res;
}
}
/**
* Generates, executes SQL query and fetches the single row.
* @return Row|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
}
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
*/
public function fetchSingle()
{
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
}
/**
* Fetches all records from table.
* @param int offset
* @param int limit
* @return array
*/
public function fetchAll($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll();
}
/**
* Fetches all records from table and returns associative tree.
* @param string associative descriptor
* @return array
*/
public function fetchAssoc($assoc)
{
return $this->query($this->_export())->fetchAssoc($assoc);
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
* @param string value
* @return array
*/
public function fetchPairs($key = NULL, $value = NULL)
{
return $this->query($this->_export())->fetchPairs($key, $value);
}
/**
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return ResultIterator
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator();
}
/**
* Generates and prints SQL query or it's part.
* @param string clause name
* @return bool
*/
public function test($clause = NULL)
{
return $this->connection->test($this->_export($clause));
}
/**
* @return int
*/
public function count()
{
return (int) $this->query([
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle();
}
/**
* @return Result
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array([$res, array_shift($setup)], $setup);
}
return $res;
}
/********************* exporting ****************d*g**/
/**
* @return DataSource
*/
public function toDataSource()
{
return new DataSource($this->connection->translate($this->_export()), $this->connection);
}
/**
* Returns SQL query.
* @return string
*/
final public function __toString()
{
try {
return $this->connection->translate($this->_export());
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/**
* Generates parameters for Translator.
* @param string clause name
* @return 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 = [$clause => $this->clauses[$clause]];
} else {
return [];
}
}
foreach ($data as $clause => $statement) {
if ($statement !== NULL) {
$args[] = $clause;
if ($clause === $this->command && $this->flags) {
$args[] = implode(' ', array_keys($this->flags));
}
foreach ($statement as $arg) {
$args[] = $arg;
}
}
}
return $args;
}
/**
* Format camelCase clause name to UPPER CASE.
* @param string
* @return string
* @internal
*/
public static function _formatClause($s)
{
if ($s === 'order' || $s === 'group') {
$s .= 'By';
trigger_error("Did you mean '$s'?", E_USER_NOTICE);
}
return strtoupper(preg_replace('#[a-z](?=[A-Z])#', '$0 ', $s));
}
public function __clone()
{
// remove references
foreach ($this->clauses as $clause => $val) {
$this->clauses[$clause] = & $val;
unset($val);
}
$this->cursor = & $foo;
}
}

67
src/Dibi/HashMap.php Normal file
View File

@@ -0,0 +1,67 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Lazy cached storage.
* @internal
*/
abstract class HashMapBase
{
private $callback;
public function __construct(callable $callback)
{
$this->callback = $callback;
}
public function setCallback(callable $callback)
{
$this->callback = $callback;
}
public function getCallback()
{
return $this->callback;
}
}
/**
* Lazy cached storage.
*
* @internal
*/
final class HashMap extends HashMapBase
{
public function __set($nm, $val)
{
if ($nm == '') {
$nm = "\xFF";
}
$this->$nm = $val;
}
public function __get($nm)
{
if ($nm == '') {
$nm = "\xFF";
return isset($this->$nm) ? $this->$nm : $this->$nm = call_user_func($this->getCallback(), '');
} else {
return $this->$nm = call_user_func($this->getCallback(), $nm);
}
}
}

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

36
src/Dibi/Literal.php Normal file
View File

@@ -0,0 +1,36 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* SQL literal value.
*/
class Literal
{
use Strict;
/** @var string */
private $value;
public function __construct($value)
{
$this->value = (string) $value;
}
/**
* @return string
*/
public function __toString()
{
return $this->value;
}
}

View File

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

View File

@@ -0,0 +1,95 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi FirePHP logger.
*/
class FirePhpLogger
{
use Dibi\Strict;
/** maximum number of rows */
public static $maxQueries = 30;
/** maximum SQL length */
public static $maxLength = 1000;
/** size of json stream chunk */
public static $streamChunkSize = 4990;
/** @var int */
public $filter;
/** @var int Elapsed time for all queries */
public $totalTime = 0;
/** @var int Number of all queries */
public $numOfQueries = 0;
/** @var array */
private static $fireTable = [['Time', 'SQL Statement', 'Rows', 'Connection']];
/**
* @return bool
*/
public static function isAvailable()
{
return isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
}
public function __construct($filter = NULL)
{
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
}
/**
* After event notification.
* @return void
*/
public function logEvent(Dibi\Event $event)
{
if (headers_sent() || ($event->type & $this->filter) === 0 || count(self::$fireTable) > self::$maxQueries) {
return;
}
if (!$this->numOfQueries) {
header('X-Wf-Protocol-dibi: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
header('X-Wf-dibi-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
header('X-Wf-dibi-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');
}
$this->totalTime += $event->time;
$this->numOfQueries++;
self::$fireTable[] = [
sprintf('%0.3f', $event->time * 1000),
strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql,
$event->result instanceof \Exception ? 'ERROR' : (string) $event->count,
$event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'),
];
$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
}
header("X-Wf-dibi-1-1-d$num: |$s|");
}
}

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 $size
* @property-read bool $unsigned
* @property-read bool $nullable
* @property-read bool $autoIncrement
* @property-read mixed $default
*/
class Column
{
use Dibi\Strict;
/** @var Dibi\Reflector|NULL when created by Result */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
public function __construct(Dibi\Reflector $reflector = NULL, array $info)
{
$this->reflector = $reflector;
$this->info = $info;
}
/**
* @return string
*/
public function getName()
{
return $this->info['name'];
}
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
public function hasTable()
{
return !empty($this->info['table']);
}
/**
* @return Table
*/
public function getTable()
{
if (empty($this->info['table']) || !$this->reflector) {
throw new Dibi\Exception("Table is unknown or not available.");
}
return new Table($this->reflector, ['name' => $this->info['table']]);
}
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) && $this->info['table'] != NULL ? $this->info['table'] : NULL; // intentionally ==
}
/**
* @return string
*/
public function getType()
{
return Dibi\Helpers::getTypeCache()->{$this->info['nativetype']};
}
/**
* @return mixed
*/
public function getNativeType()
{
return $this->info['nativetype'];
}
/**
* @return int
*/
public function getSize()
{
return isset($this->info['size']) ? (int) $this->info['size'] : NULL;
}
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool
*/
public function isNullable()
{
return isset($this->info['nullable']) ? (bool) $this->info['nullable'] : NULL;
}
/**
* @return bool
*/
public function isAutoIncrement()
{
return isset($this->info['autoincrement']) ? (bool) $this->info['autoincrement'] : NULL;
}
/**
* @return mixed
*/
public function getDefault()
{
return isset($this->info['default']) ? $this->info['default'] : NULL;
}
/**
* @param string
* @return mixed
*/
public function getVendorInfo($key)
{
return isset($this->info['vendor'][$key]) ? $this->info['vendor'][$key] : NULL;
}
}

View File

@@ -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;
}
}

620
src/Dibi/Result.php Normal file
View File

@@ -0,0 +1,620 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi result set.
*
* <code>
* $result = dibi::query('SELECT * FROM [table]');
*
* $row = $result->fetch();
* $value = $result->fetchSingle();
* $table = $result->fetchAll();
* $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('col1');
* $assoc = $result->fetchAssoc('col1[]col2->col3');
*
* unset($result);
* </code>
*
* @property-read int $rowCount
*/
class Result implements IDataSource
{
use Strict;
/** @var array ResultDriver */
private $driver;
/** @var array Translate table */
private $types = [];
/** @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 = 'Dibi\Row';
/** @var callable returned object factory*/
private $rowFactory;
/** @var array format */
private $formats = [];
/**
* @param ResultDriver
*/
public function __construct($driver)
{
$this->driver = $driver;
$this->detectTypes();
}
/**
* @deprecated
*/
final public function getResource()
{
trigger_error(__METHOD__ . '() is deprecated, use getResultDriver()->getResultResource().', E_USER_DEPRECATED);
return $this->getResultDriver()->getResultResource();
}
/**
* Frees the resources allocated for this result set.
* @return void
*/
final public function free()
{
if ($this->driver !== NULL) {
$this->driver->free();
$this->driver = $this->meta = NULL;
}
}
/**
* Safe access to property $driver.
* @return ResultDriver
* @throws \RuntimeException
*/
final public function getResultDriver()
{
if ($this->driver === NULL) {
throw new \RuntimeException('Result-set was released from memory.');
}
return $this->driver;
}
/********************* rows ****************d*g**/
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Exception
*/
final public function seek($row)
{
return ($row !== 0 || $this->fetched) ? (bool) $this->getResultDriver()->seek($row) : TRUE;
}
/**
* Required by the Countable interface.
* @return int
*/
final public function count()
{
return $this->getResultDriver()->getRowCount();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
final public function getRowCount()
{
return $this->getResultDriver()->getRowCount();
}
/**
* Required by the IteratorAggregate interface.
* @return ResultIterator
*/
final public function getIterator()
{
return new ResultIterator($this);
}
/********************* fetching rows ****************d*g**/
/**
* Set fetched object class. This class should extend the Row class.
* @param string
* @return self
*/
public function setRowClass($class)
{
$this->rowClass = $class;
return $this;
}
/**
* Returns fetched object class name.
* @return string
*/
public function getRowClass()
{
return $this->rowClass;
}
/**
* Set a factory to create fetched object instances. These should extend the Row class.
* @return self
*/
public function setRowFactory(callable $callback)
{
$this->rowFactory = $callback;
return $this;
}
/**
* Fetches the row at current position, process optional type conversion.
* and moves the internal cursor to the next position
* @return Row|FALSE array on success, FALSE if no next record
*/
final public function fetch()
{
$row = $this->getResultDriver()->fetch(TRUE);
if (!is_array($row)) {
return FALSE;
}
$this->fetched = TRUE;
$this->normalize($row);
if ($this->rowFactory) {
return call_user_func($this->rowFactory, $row);
} elseif ($this->rowClass) {
$row = new $this->rowClass($row);
}
return $row;
}
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
*/
final public function fetchSingle()
{
$row = $this->getResultDriver()->fetch(TRUE);
if (!is_array($row)) {
return FALSE;
}
$this->fetched = TRUE;
$this->normalize($row);
return reset($row);
}
/**
* Fetches all records from table.
* @param int offset
* @param int limit
* @return Row[]
*/
final public function fetchAll($offset = NULL, $limit = NULL)
{
$limit = $limit === NULL ? -1 : (int) $limit;
$this->seek((int) $offset);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
}
$data = [];
do {
if ($limit === 0) {
break;
}
$limit--;
$data[] = $row;
} while ($row = $this->fetch());
return $data;
}
/**
* Fetches all records from table and returns associative tree.
* Examples:
* - associative descriptor: col1[]col2->col3
* builds a tree: $tree[$val1][$index][$val2]->col3[$val3] = {record}
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return array
* @throws \InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
if (strpos($assoc, ',') !== FALSE) {
return $this->oldFetchAssoc($assoc);
}
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
}
$data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, 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.");
}
}
if ($as === '->') { // must not be last
array_pop($assoc);
}
if (empty($assoc)) {
$assoc[] = '[]';
}
// make associative tree
do {
$x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node
$x = & $x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i + 1]};
continue 2;
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i + 1]};
}
} elseif ($as !== '|') { // associative-array node
$x = & $x[$row->$as];
}
}
if ($x === NULL) { // build leaf
$x = $row;
}
} while ($row = $this->fetch());
unset($x);
return $data;
}
/**
* @deprecated
*/
private function oldFetchAssoc($assoc)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
}
$data = NULL;
$assoc = explode(',', $assoc);
// strip leading = and @
$leaf = '@'; // gap
$last = count($assoc) - 1;
while ($assoc[$last] === '=' || $assoc[$last] === '@') {
$leaf = $assoc[$last];
unset($assoc[$last]);
$last--;
if ($last < 0) {
$assoc[] = '#';
break;
}
}
do {
$x = & $data;
foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node
$x = & $x[];
} elseif ($as === '=') { // "record" node
if ($x === NULL) {
$x = $row->toArray();
$x = & $x[ $assoc[$i + 1] ];
$x = NULL; // prepare child node
} else {
$x = & $x[ $assoc[$i + 1] ];
}
} elseif ($as === '@') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i + 1]};
}
} else { // associative-array node
$x = & $x[$row->$as];
}
}
if ($x === NULL) { // build leaf
if ($leaf === '=') {
$x = $row->toArray();
} else {
$x = $row;
}
}
} while ($row = $this->fetch());
unset($x);
return $data;
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
* @param string value
* @return array
* @throws \InvalidArgumentException
*/
final public function fetchPairs($key = NULL, $value = NULL)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) {
return []; // empty result set
}
$data = [];
if ($value === NULL) {
if ($key !== NULL) {
throw new \InvalidArgumentException('Either none or both columns must be specified.');
}
// autodetect
$tmp = array_keys($row->toArray());
$key = $tmp[0];
if (count($row) < 2) { // indexed-array
do {
$data[] = $row[$key];
} while ($row = $this->fetch());
return $data;
}
$value = $tmp[1];
} else {
if (!property_exists($row, $value)) {
throw new \InvalidArgumentException("Unknown value column '$value'.");
}
if ($key === NULL) { // indexed-array
do {
$data[] = $row[$value];
} while ($row = $this->fetch());
return $data;
}
if (!property_exists($row, $key)) {
throw new \InvalidArgumentException("Unknown key column '$key'.");
}
}
do {
$data[ (string) $row[$key] ] = $row[$value];
} while ($row = $this->fetch());
return $data;
}
/********************* column types ****************d*g**/
/**
* Autodetect column types.
* @return void
*/
private function detectTypes()
{
$cache = Helpers::getTypeCache();
try {
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = isset($col['type']) ? $col['type'] : $cache->{$col['nativetype']};
}
} catch (NotSupportedException $e) {
}
}
/**
* Converts values to specified type and format.
* @param array
* @return void
*/
private function normalize(array & $row)
{
foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL
continue;
}
$value = $row[$key];
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
} elseif ($type === Type::INTEGER) {
$row[$key] = is_float($tmp = $value * 1)
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === Type::FLOAT) {
$value = ltrim($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 === Type::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === Type::DATETIME || $type === Type::DATE || $type === Type::TIME) {
if ((int) $value !== 0 || substr((string) $value, 0, 3) === '00:') { // '', 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 === 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);
}
}
}
/**
* Define column type.
* @param string column
* @param string type (use constant Type::*)
* @return self
*/
final public function setType($col, $type)
{
$this->types[$col] = $type;
return $this;
}
/**
* Returns column type.
* @return string
*/
final public function getType($col)
{
return isset($this->types[$col]) ? $this->types[$col] : NULL;
}
/**
* Sets data format.
* @param string type (use constant Type::*)
* @param string format
* @return self
*/
final public function setFormat($type, $format)
{
$this->formats[$type] = $format;
return $this;
}
/**
* Returns data format.
* @return string
*/
final public function getFormat($type)
{
return isset($this->formats[$type]) ? $this->formats[$type] : NULL;
}
/********************* meta info ****************d*g**/
/**
* Returns a meta information about the current result set.
* @return Reflection\Result
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new Reflection\Result($this->getResultDriver());
}
return $this->meta;
}
/**
* @return Reflection\Column[]
*/
final public function getColumns()
{
return $this->getInfo()->getColumns();
}
/********************* misc tools ****************d*g**/
/**
* Displays complete result set as HTML or text table for debug purposes.
* @return void
*/
final public function dump()
{
echo Helpers::dump($this);
}
}

108
src/Dibi/ResultIterator.php Normal file
View File

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

95
src/Dibi/Row.php Normal file
View File

@@ -0,0 +1,95 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Result set single row.
*/
class Row implements \ArrayAccess, \IteratorAggregate, \Countable
{
public function __construct($arr)
{
foreach ($arr as $k => $v) {
$this->$k = $v;
}
}
public function toArray()
{
return (array) $this;
}
/**
* Converts value to DateTime object.
* @param string key
* @param string format
* @return \DateTime
*/
public function asDateTime($key, $format = NULL)
{
$time = $this[$key];
if (!$time instanceof DateTime) {
if ((int) $time === 0 && substr((string) $time, 0, 3) !== '00:') { // '', NULL, FALSE, '0000-00-00', ...
return NULL;
}
$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**/
final public function count()
{
return count((array) $this);
}
final public function getIterator()
{
return new \ArrayIterator($this);
}
final public function offsetSet($nm, $val)
{
$this->$nm = $val;
}
final public function offsetGet($nm)
{
return $this->$nm;
}
final public function offsetExists($nm)
{
return isset($this->$nm);
}
final public function offsetUnset($nm)
{
unset($this->$nm);
}
}

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 callabke
* @return mixed
*/
public static function extensionMethod($name, $callback = NULL)
{
if (strpos($name, '::') === FALSE) {
$class = get_called_class();
} else {
list($class, $name) = explode('::', $name);
$class = (new ReflectionClass($class))->getName();
}
if (self::$extMethods === NULL) { // for backwards compatibility
$list = get_defined_functions();
foreach ($list['user'] as $fce) {
$pair = explode('_prototype_', $fce);
if (count($pair) === 2) {
trigger_error("Extension method defined as $fce() is deprecated, use $class::extensionMethod('$name', ...).", E_USER_DEPRECATED);
self::$extMethods[$pair[1]][(new ReflectionClass($pair[0]))->getName()] = $fce;
self::$extMethods[$pair[1]][''] = NULL;
}
}
}
$list = & self::$extMethods[strtolower($name)];
if ($callback === NULL) { // getter
$cache = & $list[''][$class];
if (isset($cache)) {
return $cache;
}
foreach ([$class] + class_parents($class) + class_implements($class) as $cl) {
if (isset($list[$cl])) {
return $cache = $list[$cl];
}
}
return $cache = FALSE;
} else { // setter
$list[$class] = $callback;
$list[''] = NULL;
}
}
}

611
src/Dibi/Translator.php Normal file
View File

@@ -0,0 +1,611 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL translator.
*/
final class Translator
{
use Strict;
/** @var Connection */
private $connection;
/** @var Driver */
private $driver;
/** @var int */
private $cursor = 0;
/** @var array */
private $args;
/** @var string[] */
private $errors;
/** @var bool */
private $comment = FALSE;
/** @var int */
private $ifLevel = 0;
/** @var int */
private $ifLevelStart = 0;
/** @var int */
private $limit;
/** @var int */
private $offset;
/** @var HashMap */
private $identifiers;
public function __construct(Connection $connection)
{
$this->connection = $connection;
$this->driver = $connection->getDriver();
$this->identifiers = new HashMap([$this, 'delimite']);
}
/**
* Generates SQL. Can be called only once.
* @param array
* @return string
* @throws Exception
*/
public function translate(array $args)
{
$args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]);
}
$this->args = $args;
$commandIns = NULL;
$lastArr = NULL;
$cursor = & $this->cursor;
$comment = & $this->comment;
// iterate
$sql = [];
while ($cursor < count($this->args)) {
$arg = $this->args[$cursor];
$cursor++;
// simple string means SQL
if (is_string($arg)) {
// speed-up - is regexp required?
$toSkip = strcspn($arg, '`[\'":%?');
if (strlen($arg) === $toSkip) { // needn't be translated
$sql[] = $arg;
} else {
$sql[] = substr($arg, 0, $toSkip)
/*
. preg_replace_callback('/
(?=[`[\'":%?]) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
\[(.+?)\]| ## 2) [identifier]
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
(")((?:""|[^"])*)"| ## 5,6) "string"
(\'|")| ## 7) lone quote
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) :substitution:
%([a-zA-Z~][a-zA-Z0-9~]{0,5})|## 10) modifier
(\?) ## 11) placeholder
)/xs',
*/ // note: this can change $this->args & $this->cursor & ...
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z~][a-zA-Z0-9~]{0,5})|(\?))/s',
[$this, 'cb'],
substr($arg, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
}
}
continue;
}
if ($comment) {
$sql[] = '...';
continue;
}
if ($arg instanceof \Traversable) {
$arg = iterator_to_array($arg);
}
if (is_array($arg) && is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
if ($lastArr === $cursor - 1) {
$sql[] = ',';
}
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
}
$lastArr = $cursor;
continue;
}
// default processing
$sql[] = $this->formatValue($arg, FALSE);
} // while
if ($comment) {
$sql[] = '*/';
}
$sql = implode(' ', $sql);
if ($this->errors) {
throw new Exception('SQL translate error: ' . trim(reset($this->errors), '*'), 0, $sql);
}
// apply limit
if ($this->limit !== NULL || $this->offset !== NULL) {
$this->driver->applyLimit($sql, $this->limit, $this->offset);
}
return $sql;
}
/**
* Apply modifier to single value.
* @param mixed
* @param string
* @return string
*/
public function formatValue($value, $modifier)
{
if ($this->comment) {
return '...';
}
// array processing (with or without modifier)
if ($value instanceof \Traversable) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
$vx = $kx = [];
switch ($modifier) {
case 'and':
case 'or': // key=val AND key IS NULL AND ...
if (empty($value)) {
return '1=1';
}
foreach ($value as $k => $v) {
if (is_string($k)) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$k = $this->identifiers->{$pair[0]} . ' ';
if (!isset($pair[1])) {
$v = $this->formatValue($v, FALSE);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
} elseif ($pair[1] === 'ex') {
$vx[] = $k . $this->formatValue($v, 'ex');
} else {
$v = $this->formatValue($v, $pair[1]);
if ($pair[1] === 'l' || $pair[1] === 'in') {
$op = 'IN ';
} elseif (strpos($pair[1], 'like') !== FALSE) {
$op = 'LIKE ';
} elseif ($v === 'NULL') {
$op = 'IS ';
} else {
$op = '= ';
}
$vx[] = $k . $op . $v;
}
} else {
$vx[] = $this->formatValue($v, 'ex');
}
}
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) {
if (is_string($k)) {
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->driver->escapeIdentifier($v));
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]};
}
}
return implode(', ', $vx);
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]} . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return implode(', ', $vx);
case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
case 'v': // (key, key, ...) VALUES (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
foreach ($value as $k => $v) {
if (is_array($v)) {
if (isset($proto)) {
if ($proto !== array_keys($v)) {
return $this->errors[] = '**Multi-insert array "' . $k . '" is different**';
}
} else {
$proto = array_keys($v);
}
} else {
return $this->errors[] = '**Unexpected type ' . (is_object($v) ? get_class($v) : gettype($v)) . '**';
}
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
foreach ($v as $k2 => $v2) {
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE));
}
}
foreach ($vx as $k => $v) {
$vx[$k] = '(' . implode(', ', $v) . ')';
}
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
case 'by': // key ASC, key DESC
foreach ($value as $k => $v) {
if (is_array($v)) {
$vx[] = $this->formatValue($v, 'ex');
} elseif (is_string($k)) {
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
$vx[] = $this->identifiers->$k . ' ' . $v;
} else {
$vx[] = $this->identifiers->$v;
}
}
return implode(', ', $vx);
case 'ex':
case 'sql':
return call_user_func_array([$this->connection, 'translate'], $value);
default: // value, value, value - all with the same modifier
foreach ($value as $v) {
$vx[] = $this->formatValue($v, $modifier);
}
return implode(', ', $vx);
}
}
// with modifier procession
if ($modifier) {
if ($value !== NULL && !is_scalar($value)) { // array is already processed
if ($value instanceof Literal && ($modifier === 'sql' || $modifier === 'SQL')) {
$modifier = 'SQL';
} elseif (($value instanceof \DateTime || $value instanceof \DateTimeInterface) && ($modifier === 'd' || $modifier === 't')) {
// continue
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
}
}
switch ($modifier) {
case 's': // string
return $value === NULL ? 'NULL' : $this->driver->escapeText($value);
case 'bin':// binary
return $value === NULL ? 'NULL' : $this->driver->escapeBinary($value);
case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escapeBool($value);
case 'sN': // string or NULL
case 'sn':
return $value == '' ? 'NULL' : $this->driver->escapeText($value); // notice two equal signs
case 'in': // deprecated
trigger_error('Modifier %in is deprecated, use %iN.', E_USER_DEPRECATED);
// intentionally break omitted
case 'iN': // signed int or NULL
if ($value == '') {
$value = NULL;
}
// intentionally break omitted
case 'i': // signed int
case 'u': // unsigned int, ignored
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && preg_match('#[+-]?\d++(?:e\d+)?\z#A', $value)) {
return $value; // support for long numbers - keep them unchanged
} elseif (is_string($value) && substr($value, 1, 1) === 'x' && is_numeric($value)) {
trigger_error('Support for hex strings has been deprecated.', E_USER_DEPRECATED);
return (string) hexdec($value);
} else {
return (string) (int) $value;
}
case 'f': // float
if ($value === NULL) {
return 'NULL';
} elseif (is_string($value) && is_numeric($value) && substr($value, 1, 1) !== 'x') {
return $value; // support for extreme numbers - keep them unchanged
} else {
return rtrim(rtrim(number_format($value + 0, 10, '.', ''), '0'), '.');
}
case 'd': // date
case 't': // datetime
case 'dt': // datetime
if ($value === NULL) {
return 'NULL';
} else {
return $modifier === 'd' ? $this->driver->escapeDate($value) : $this->driver->escapeDateTime($value);
}
case 'by':
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;
// speed-up - is regexp required?
$toSkip = strcspn($value, '`[\'":');
if (strlen($value) !== $toSkip) {
$value = substr($value, 0, $toSkip)
. preg_replace_callback(
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
[$this, 'cb'],
substr($value, $toSkip)
);
if (preg_last_error()) {
throw new PcreException;
}
}
return $value;
case 'SQL': // preserve as real SQL (TODO: rename to %sql)
return (string) $value;
case 'like~': // LIKE string%
return $this->driver->escapeLike($value, 1);
case '~like': // LIKE %string
return $this->driver->escapeLike($value, -1);
case '~like~': // LIKE %string%
return $this->driver->escapeLike($value, 0);
case 'and':
case 'or':
case 'a':
case 'l':
case 'v':
$type = gettype($value);
return $this->errors[] = "**Invalid combination of type $type and modifier %$modifier**";
default:
return $this->errors[] = "**Unknown or unexpected modifier %$modifier**";
}
}
// without modifier procession
if (is_string($value)) {
return $this->driver->escapeText($value);
} elseif (is_int($value)) {
return (string) $value;
} elseif (is_float($value)) {
return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.');
} elseif (is_bool($value)) {
return $this->driver->escapeBool($value);
} elseif ($value === NULL) {
return 'NULL';
} elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) {
return $this->driver->escapeDateTime($value);
} elseif ($value instanceof Literal) {
return (string) $value;
} else {
$type = is_object($value) ? get_class($value) : gettype($value);
return $this->errors[] = "**Unexpected $type**";
}
}
/**
* PREG callback from translate() or formatValue().
* @param array
* @return string
*/
private function cb($matches)
{
// [1] => `ident`
// [2] => [ident]
// [3] => '
// [4] => string
// [5] => "
// [6] => string
// [7] => lone-quote
// [8] => substitution
// [9] => substitution flag
// [10] => modifier (when called from self::translate())
// [11] => placeholder (when called from self::translate())
if (!empty($matches[11])) { // placeholder
$cursor = & $this->cursor;
if ($cursor >= count($this->args)) {
return $this->errors[] = '**Extra placeholder**';
}
$cursor++;
return $this->formatValue($this->args[$cursor - 1], FALSE);
}
if (!empty($matches[10])) { // modifier
$mod = $matches[10];
$cursor = & $this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
return $this->errors[] = "**Extra modifier %$mod**";
}
if ($mod === 'if') {
$this->ifLevel++;
$cursor++;
if (!$this->comment && !$this->args[$cursor - 1]) {
// open comment
$this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE;
return '/*';
}
return '';
} elseif ($mod === 'else') {
if ($this->ifLevelStart === $this->ifLevel) {
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
} elseif (!$this->comment) {
$this->ifLevelStart = $this->ifLevel;
$this->comment = TRUE;
return '/*';
}
} elseif ($mod === 'end') {
$this->ifLevel--;
if ($this->ifLevelStart === $this->ifLevel + 1) {
// close comment
$this->ifLevelStart = 0;
$this->comment = FALSE;
return '*/';
}
return '';
} elseif ($mod === 'ex') { // array expansion
array_splice($this->args, $cursor, 1, $this->args[$cursor]);
return '';
} elseif ($mod === 'lmt') { // apply limit
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(limit $arg)";
} else {
$this->limit = (int) $arg;
}
return '';
} elseif ($mod === 'ofs') { // apply offset
$arg = $this->args[$cursor++];
if ($arg === NULL) {
} elseif ($this->comment) {
return "(offset $arg)";
} else {
$this->offset = (int) $arg;
}
return '';
} else { // default processing
$cursor++;
return $this->formatValue($this->args[$cursor - 1], $mod);
}
}
if ($this->comment) {
return '...';
}
if ($matches[1]) { // SQL identifiers: `ident`
return $this->identifiers->{$matches[1]};
} elseif ($matches[2]) { // SQL identifiers: [ident]
return $this->identifiers->{$matches[2]};
} elseif ($matches[3]) { // SQL strings: '...'
return $this->driver->escapeText(str_replace("''", "'", $matches[4]));
} elseif ($matches[5]) { // SQL strings: "..."
return $this->driver->escapeText(str_replace('""', '"', $matches[6]));
} elseif ($matches[7]) { // string quote
return $this->errors[] = '**Alone quote**';
}
if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1);
$m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
}
throw new \Exception('this should be never executed');
}
/**
* Apply substitutions to indentifier and delimites it.
* @param string indentifier
* @return string
* @internal
*/
public function delimite($value)
{
$value = $this->connection->substitute($value);
$parts = explode('.', $value);
foreach ($parts as & $v) {
if ($v !== '*') {
$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__);
}
}

454
src/Dibi/dibi.php Normal file
View File

@@ -0,0 +1,454 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* 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.
*/
class dibi
{
use Dibi\Strict;
const
AFFECTED_ROWS = 'a',
IDENTIFIER = 'n';
/** version */
const
VERSION = '3.0.5',
REVISION = 'released on 2016-07-20';
/** sorting order */
const
ASC = 'ASC',
DESC = 'DESC';
/** @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 Dibi\Connection[] Connection registry storage for DibiConnection objects */
private static $registry = [];
/** @var Dibi\Connection Current connection */
private static $connection;
/** @var string Last SQL command @see dibi::query() */
public static $sql;
/** @var int Elapsed time for last query */
public static $elapsedTime;
/** @var int Elapsed time for all queries */
public static $totalTime;
/** @var int Number or queries */
public static $numOfQueries = 0;
/** @var string Default dibi driver */
public static $defaultDriver = 'mysqli';
/**
* Static class - cannot be instantiated.
*/
final public function __construct()
{
throw new LogicException('Cannot instantiate static class ' . get_class($this));
}
/********************* connections handling ****************d*g**/
/**
* Creates a new Connection object and connects it to specified database.
* @param mixed connection parameters
* @param string connection name
* @return Dibi\Connection
* @throws Dibi\Exception
*/
public static function connect($config = [], $name = 0)
{
return self::$connection = self::$registry[$name] = new Dibi\Connection($config, $name);
}
/**
* Disconnects from database (doesn't destroy Connection object).
* @return void
*/
public static function disconnect()
{
self::getConnection()->disconnect();
}
/**
* Returns TRUE when connection was established.
* @return bool
*/
public static function isConnected()
{
return (self::$connection !== NULL) && self::$connection->isConnected();
}
/**
* Retrieve active connection.
* @param string connection registy name
* @return Dibi\Connection
* @throws Dibi\Exception
*/
public static function getConnection($name = NULL)
{
if ($name === NULL) {
if (self::$connection === NULL) {
throw new Dibi\Exception('Dibi is not connected to database.');
}
return self::$connection;
}
if (!isset(self::$registry[$name])) {
throw new Dibi\Exception("There is no connection named '$name'.");
}
return self::$registry[$name];
}
/**
* Sets connection.
* @param Dibi\Connection
* @return Dibi\Connection
*/
public static function setConnection(Dibi\Connection $connection)
{
return self::$connection = $connection;
}
/**
* @deprecated
*/
public static function activate($name)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
self::$connection = self::getConnection($name);
}
/********************* monostate for active connection ****************d*g**/
/**
* Generates and executes SQL query - Monostate for Dibi\Connection::query().
* @param array|mixed one or more arguments
* @return Dibi\Result|int result set object (if any)
* @throws Dibi\Exception
*/
public static function query($args)
{
$args = func_get_args();
return self::getConnection()->query($args);
}
/**
* Executes the SQL query - Monostate for Dibi\Connection::nativeQuery().
* @param string SQL statement.
* @return Dibi\Result|int result set object (if any)
*/
public static function nativeQuery($sql)
{
return self::getConnection()->nativeQuery($sql);
}
/**
* Generates and prints SQL query - Monostate for Dibi\Connection::test().
* @param array|mixed one or more arguments
* @return bool
*/
public static function test($args)
{
$args = func_get_args();
return self::getConnection()->test($args);
}
/**
* Generates and returns SQL query as DataSource - Monostate for Dibi\Connection::test().
* @param array|mixed one or more arguments
* @return Dibi\DataSource
*/
public static function dataSource($args)
{
$args = func_get_args();
return self::getConnection()->dataSource($args);
}
/**
* Executes SQL query and fetch result - Monostate for Dibi\Connection::query() & fetch().
* @param array|mixed one or more arguments
* @return Dibi\Row
* @throws Dibi\Exception
*/
public static function fetch($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - Monostate for Dibi\Connection::query() & fetchAll().
* @param array|mixed one or more arguments
* @return Dibi\Row[]
* @throws Dibi\Exception
*/
public static function fetchAll($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - Monostate for Dibi\Connection::query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws Dibi\Exception
*/
public static function fetchSingle($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - Monostate for Dibi\Connection::query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws Dibi\Exception
*/
public static function fetchPairs($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchPairs();
}
/**
* Gets the number of affected rows.
* Monostate for Dibi\Connection::getAffectedRows()
* @return int number of rows
* @throws Dibi\Exception
*/
public static function getAffectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws Dibi\Exception
*/
public static function affectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* Monostate for Dibi\Connection::getInsertId()
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
*/
public static function getInsertId($sequence = NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws Dibi\Exception
*/
public static function insertId($sequence = NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Begins a transaction - Monostate for Dibi\Connection::begin().
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function begin($savepoint = NULL)
{
self::getConnection()->begin($savepoint);
}
/**
* Commits statements in a transaction - Monostate for Dibi\Connection::commit($savepoint = NULL).
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function commit($savepoint = NULL)
{
self::getConnection()->commit($savepoint);
}
/**
* Rollback changes in a transaction - Monostate for Dibi\Connection::rollback().
* @param string optional savepoint name
* @return void
* @throws Dibi\Exception
*/
public static function rollback($savepoint = NULL)
{
self::getConnection()->rollback($savepoint);
}
/**
* Gets a information about the current database - Monostate for Dibi\Connection::getDatabaseInfo().
* @return Dibi\Reflection\Database
*/
public static function getDatabaseInfo()
{
return self::getConnection()->getDatabaseInfo();
}
/**
* Import SQL dump from file - extreme fast!
* @param string filename
* @return int count of sql commands
*/
public static function loadFile($file)
{
return Dibi\Helpers::loadFromFile(self::getConnection(), $file);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return Dibi\Fluent
*/
public static function command()
{
return self::getConnection()->command();
}
/**
* @param string column name
* @return Dibi\Fluent
*/
public static function select($args)
{
$args = func_get_args();
return call_user_func_array([self::getConnection(), 'select'], $args);
}
/**
* @param string table
* @param array
* @return Dibi\Fluent
*/
public static function update($table, $args)
{
return self::getConnection()->update($table, $args);
}
/**
* @param string table
* @param array
* @return Dibi\Fluent
*/
public static function insert($table, $args)
{
return self::getConnection()->insert($table, $args);
}
/**
* @param string table
* @return Dibi\Fluent
*/
public static function delete($table)
{
return self::getConnection()->delete($table);
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap - Monostate for Dibi\Connection::getSubstitutes().
* @return Dibi\HashMap
*/
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/********************* misc tools ****************d*g**/
/**
* 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)
{
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 = NULL, $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
{
}

222
src/Dibi/interfaces.php Normal file
View File

@@ -0,0 +1,222 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Provides an interface between a dataset and data-aware components.
*/
interface IDataSource extends \Countable, \IteratorAggregate
{
//function \IteratorAggregate::getIterator();
//function \Countable::count();
}
/**
* dibi driver interface.
*/
interface Driver
{
/**
* Connects to a database.
* @param array
* @return void
* @throws Exception
*/
function connect(array & $config);
/**
* Disconnects from a database.
* @return void
* @throws Exception
*/
function disconnect();
/**
* Internal: Executes the SQL query.
* @param string SQL statement.
* @return ResultDriver|NULL
* @throws DriverException
*/
function query($sql);
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
*/
function getAffectedRows();
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
function getInsertId($sequence);
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DriverException
*/
function begin($savepoint = NULL);
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DriverException
*/
function commit($savepoint = NULL);
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DriverException
*/
function rollback($savepoint = NULL);
/**
* Returns the connection resource.
* @return mixed
*/
function getResource();
/**
* Returns the connection reflector.
* @return Reflector
*/
function getReflector();
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @return string encoded value
*/
function escapeText($value);
function escapeBinary($value);
function escapeIdentifier($value);
function escapeBool($value);
function escapeDate($value);
function escapeDateTime($value);
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
function escapeLike($value, $pos);
/**
* Injects LIMIT/OFFSET to the SQL query.
* @return void
*/
function applyLimit(& $sql, $limit, $offset);
}
/**
* dibi result set driver interface.
*/
interface ResultDriver
{
/**
* Returns the number of rows in a result set.
* @return int
*/
function getRowCount();
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws Exception
*/
function seek($row);
/**
* 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
* @internal
*/
function fetch($type);
/**
* Frees the resources allocated for this result set.
* @param resource result set resource
* @return void
*/
function free();
/**
* Returns metadata for all columns in a result set.
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
*/
function getResultColumns();
/**
* Returns the result set resource.
* @return mixed
*/
function getResultResource();
/**
* Decodes data from result set.
* @param string
* @return string
*/
function unescapeBinary($value);
}
/**
* dibi driver reflection.
*/
interface Reflector
{
/**
* Returns list of tables.
* @return array of {name [, (bool) view ]}
*/
function getTables();
/**
* Returns metadata for all columns in a table.
* @param string
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
*/
function getColumns($table);
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array of {name, (array of names) columns [, (bool) unique, (bool) primary ]}
*/
function getIndexes($table);
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
function getForeignKeys($table);
}

144
src/loader.php Normal file
View File

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

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