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

Compare commits

...

257 Commits

Author SHA1 Message Date
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
151 changed files with 9438 additions and 9247 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

36
.travis.yml Normal file
View File

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

39
appveyor.yml Normal file
View File

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

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

View File

@@ -1,657 +1,3 @@
<?php
/**
* dibi - smart database abstraction layer (http://dibiphp.com)
*
* Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* Check PHP configuration.
*/
if (version_compare(PHP_VERSION, '5.2.0', '<')) {
throw new Exception('dibi needs PHP 5.2.0 or newer.');
}
@set_magic_quotes_runtime(FALSE); // intentionally @
require_once dirname(__FILE__) . '/libs/interfaces.php';
require_once dirname(__FILE__) . '/libs/DibiDateTime.php';
require_once dirname(__FILE__) . '/libs/DibiObject.php';
require_once dirname(__FILE__) . '/libs/DibiLiteral.php';
require_once dirname(__FILE__) . '/libs/DibiHashMap.php';
require_once dirname(__FILE__) . '/libs/DibiException.php';
require_once dirname(__FILE__) . '/libs/DibiConnection.php';
require_once dirname(__FILE__) . '/libs/DibiResult.php';
require_once dirname(__FILE__) . '/libs/DibiResultIterator.php';
require_once dirname(__FILE__) . '/libs/DibiRow.php';
require_once dirname(__FILE__) . '/libs/DibiTranslator.php';
require_once dirname(__FILE__) . '/libs/DibiDataSource.php';
require_once dirname(__FILE__) . '/libs/DibiFluent.php';
require_once dirname(__FILE__) . '/libs/DibiDatabaseInfo.php';
require_once dirname(__FILE__) . '/libs/DibiEvent.php';
require_once dirname(__FILE__) . '/libs/DibiFileLogger.php';
require_once dirname(__FILE__) . '/libs/DibiFirePhpLogger.php';
if (interface_exists('Nette\Diagnostics\IBarPanel') || interface_exists('IBarPanel')) {
require_once dirname(__FILE__) . '/Nette/DibiNettePanel.php';
}
/**
* Interface for database drivers.
*
* This class is static container class for creating DB objects and
* store connections info.
*
* @author David Grudl
* @package dibi
*/
class dibi
{
/** column type */
const TEXT = 's', // as 'string'
BINARY = 'bin',
BOOL = 'b',
INTEGER = 'i',
FLOAT = 'f',
DATE = 'd',
DATETIME = 't',
TIME = 't';
const IDENTIFIER = 'n';
/** @deprecated */
const FIELD_TEXT = dibi::TEXT,
FIELD_BINARY = dibi::BINARY,
FIELD_BOOL = dibi::BOOL,
FIELD_INTEGER = dibi::INTEGER,
FIELD_FLOAT = dibi::FLOAT,
FIELD_DATE = dibi::DATE,
FIELD_DATETIME = dibi::DATETIME,
FIELD_TIME = dibi::TIME;
/** version */
const VERSION = '2.0',
REVISION = '$WCREV$ released on $WCDATE$';
/** sorting order */
const ASC = 'ASC',
DESC = 'DESC';
/** @var DibiConnection[] Connection registry storage for DibiConnection objects */
private static $registry = array();
/** @var DibiConnection Current connection */
private static $connection;
/** @var array @see addHandler */
private static $handlers = array();
/** @var string Last SQL command @see dibi::query() */
public static $sql;
/** @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 = 'mysql';
/**
* 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 DibiConnection object and connects it to specified database.
* @param mixed connection parameters
* @param string connection name
* @return DibiConnection
* @throws DibiException
*/
public static function connect($config = array(), $name = 0)
{
return self::$connection = self::$registry[$name] = new DibiConnection($config, $name);
}
/**
* Disconnects from database (doesn't destroy DibiConnection 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 DibiConnection
* @throws DibiException
*/
public static function getConnection($name = NULL)
{
if ($name === NULL) {
if (self::$connection === NULL) {
throw new DibiException('Dibi is not connected to database.');
}
return self::$connection;
}
if (!isset(self::$registry[$name])) {
throw new DibiException("There is no connection named '$name'.");
}
return self::$registry[$name];
}
/**
* Sets connection.
* @param DibiConnection
* @return DibiConnection
*/
public static function setConnection(DibiConnection $connection)
{
return self::$connection = $connection;
}
/**
* Change active connection.
* @param string connection registy name
* @return void
* @throws DibiException
*/
public static function activate($name)
{
self::$connection = self::getConnection($name);
}
/********************* monostate for active connection ****************d*g**/
/**
* Generates and executes SQL query - Monostate for DibiConnection::query().
* @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any)
* @throws DibiException
*/
public static function query($args)
{
$args = func_get_args();
return self::getConnection()->query($args);
}
/**
* Executes the SQL query - Monostate for DibiConnection::nativeQuery().
* @param string SQL statement.
* @return DibiResult|int result set object (if any)
*/
public static function nativeQuery($sql)
{
return self::getConnection()->nativeQuery($sql);
}
/**
* Generates and prints SQL query - Monostate for DibiConnection::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 DibiDataSource - Monostate for DibiConnection::test().
* @param array|mixed one or more arguments
* @return DibiDataSource
*/
public static function dataSource($args)
{
$args = func_get_args();
return self::getConnection()->dataSource($args);
}
/**
* Executes SQL query and fetch result - Monostate for DibiConnection::query() & fetch().
* @param array|mixed one or more arguments
* @return DibiRow
* @throws DibiException
*/
public static function fetch($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetch();
}
/**
* Executes SQL query and fetch results - Monostate for DibiConnection::query() & fetchAll().
* @param array|mixed one or more arguments
* @return array of DibiRow
* @throws DibiException
*/
public static function fetchAll($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchAll();
}
/**
* Executes SQL query and fetch first column - Monostate for DibiConnection::query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public static function fetchSingle($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchSingle();
}
/**
* Executes SQL query and fetch pairs - Monostate for DibiConnection::query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
public static function fetchPairs($args)
{
$args = func_get_args();
return self::getConnection()->query($args)->fetchPairs();
}
/**
* Gets the number of affected rows.
* Monostate for DibiConnection::getAffectedRows()
* @return int number of rows
* @throws DibiException
*/
public static function getAffectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Gets the number of affected rows. Alias for getAffectedRows().
* @return int number of rows
* @throws DibiException
*/
public static function affectedRows()
{
return self::getConnection()->getAffectedRows();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* Monostate for DibiConnection::getInsertId()
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public static function getInsertId($sequence=NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId().
* @param string optional sequence name
* @return int
* @throws DibiException
*/
public static function insertId($sequence=NULL)
{
return self::getConnection()->getInsertId($sequence);
}
/**
* Begins a transaction - Monostate for DibiConnection::begin().
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function begin($savepoint = NULL)
{
self::getConnection()->begin($savepoint);
}
/**
* Commits statements in a transaction - Monostate for DibiConnection::commit($savepoint = NULL).
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function commit($savepoint = NULL)
{
self::getConnection()->commit($savepoint);
}
/**
* Rollback changes in a transaction - Monostate for DibiConnection::rollback().
* @param string optional savepoint name
* @return void
* @throws DibiException
*/
public static function rollback($savepoint = NULL)
{
self::getConnection()->rollback($savepoint);
}
/**
* Gets a information about the current database - Monostate for DibiConnection::getDatabaseInfo().
* @return DibiDatabaseInfo
*/
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 self::getConnection()->loadFile($file);
}
/**
* Replacement for majority of dibi::methods() in future.
*/
public static function __callStatic($name, $args)
{
//if ($name = 'select', 'update', ...') {
// return self::command()->$name($args);
//}
return call_user_func_array(array(self::getConnection(), $name), $args);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return DibiFluent
*/
public static function command()
{
return self::getConnection()->command();
}
/**
* @param string column name
* @return DibiFluent
*/
public static function select($args)
{
$args = func_get_args();
return call_user_func_array(array(self::getConnection(), 'select'), $args);
}
/**
* @param string table
* @param array
* @return DibiFluent
*/
public static function update($table, $args)
{
return self::getConnection()->update($table, $args);
}
/**
* @param string table
* @param array
* @return DibiFluent
*/
public static function insert($table, $args)
{
return self::getConnection()->insert($table, $args);
}
/**
* @param string table
* @return DibiFluent
*/
public static function delete($table)
{
return self::getConnection()->delete($table);
}
/********************* data types ****************d*g**/
/**
* @return DibiDateTime
*/
public static function datetime($time = NULL)
{
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING);
return new DibiDateTime($time);
}
/**
* @deprecated
*/
public static function date($date = NULL)
{
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING);
return new DibiDateTime($date);
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
* @return DibiHashMap
*/
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/** @deprecated */
public static function addSubst($expr, $subst)
{
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->expr = val; instead.', E_USER_WARNING);
self::getSubstitutes()->$expr = $subst;
}
/** @deprecated */
public static function removeSubst($expr)
{
trigger_error(__METHOD__ . '() is deprecated; use unset(dibi::getSubstitutes()->expr) instead.', E_USER_WARNING);
$substitutes = self::getSubstitutes();
if ($expr === TRUE) {
foreach ($substitutes as $expr => $foo) {
unset($substitutes->$expr);
}
} else {
unset($substitutes->$expr);
}
}
/** @deprecated */
public static function setSubstFallback($callback)
{
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->setCallback() instead.', E_USER_WARNING);
self::getSubstitutes()->setCallback($callback);
}
/********************* misc tools ****************d*g**/
/**
* Prints out a syntax highlighted version of the SQL command or DibiResult.
* @param string|DibiResult
* @param bool return output instead of printing it?
* @return string
*/
public static function dump($sql = NULL, $return = FALSE)
{
ob_start();
if ($sql instanceof DibiResult) {
$sql->dump();
} else {
if ($sql === NULL) $sql = self::$sql;
static $keywords1 = 'SELECT|(?:ON\s+DUPLICATE\s+KEY)?UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|CALL|UNION|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|OFFSET|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|IGNORE|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|RLIKE|REGEXP|TRUE|FALSE';
// insert new lines
$sql = " $sql ";
$sql = preg_replace("#(?<=[\\s,(])($keywords1)(?=[\\s,)])#i", "\n\$1", $sql);
// reduce spaces
$sql = preg_replace('#[ \t]{2,}#', " ", $sql);
$sql = wordwrap($sql, 100);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
if (PHP_SAPI === 'cli') {
echo trim($sql) . "\n\n";
} else {
// syntax highlight
$sql = htmlSpecialChars($sql);
$sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql);
echo '<pre class="dump">', trim($sql), "</pre>\n";
}
}
if ($return) {
return ob_get_clean();
} else {
ob_end_flush();
}
}
private static function highlightCallback($matches)
{
if (!empty($matches[1])) // comment
return '<em style="color:gray">' . $matches[1] . '</em>';
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>';
}
}
trigger_error('Dibi was moved to /src/loader.php', E_USER_WARNING);
require __DIR__ . '/../src/loader.php';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,165 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* dibi common exception.
*
* @author David Grudl
* @package dibi
*/
class DibiException extends Exception
{
/** @var string */
private $sql;
/**
* Construct a dibi exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $sql = NULL)
{
parent::__construct($message, (int) $code);
$this->sql = $sql;
}
/**
* @return string The SQL passed to the constructor
*/
final public function getSql()
{
return $this->sql;
}
/**
* @return string string represenation of exception with SQL command
*/
public function __toString()
{
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
}
/**
* database server exception.
*
* @author David Grudl
* @package dibi
*/
class DibiDriverException extends DibiException
{
/********************* error catching ****************d*g**/
/** @var string */
private static $errorMsg;
/**
* Starts catching potential errors/warnings.
* @return void
*/
public static function tryError()
{
set_error_handler(array(__CLASS__, '_errorHandler'), E_ALL);
self::$errorMsg = NULL;
}
/**
* Returns catched error/warning message.
* @param string catched message
* @return bool
*/
public static function catchError(& $message)
{
restore_error_handler();
$message = self::$errorMsg;
self::$errorMsg = NULL;
return $message !== NULL;
}
/**
* Internal error handler. Do not call directly.
* @internal
*/
public static function _errorHandler($code, $message)
{
restore_error_handler();
if (ini_get('html_errors')) {
$message = strip_tags($message);
$message = html_entity_decode($message);
}
self::$errorMsg = $message;
}
}
/**
* PCRE exception.
*
* @author David Grudl
* @package dibi
*/
class DibiPcreException extends Exception {
public function __construct($message = '%msg.')
{
static $messages = array(
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
);
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
/**
* @package dibi
*/
class DibiNotImplementedException extends DibiException
{}
/**
* @package dibi
*/
class DibiNotSupportedException extends DibiException
{}

View File

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

View File

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

View File

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

View File

@@ -1,600 +0,0 @@
<?php
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* dibi SQL translator.
*
* @author David Grudl
* @package dibi
*/
final class DibiTranslator extends DibiObject
{
/** @var DibiConnection */
private $connection;
/** @var IDibiDriver */
private $driver;
/** @var int */
private $cursor;
/** @var array */
private $args;
/** @var bool */
private $hasError;
/** @var bool */
private $comment;
/** @var int */
private $ifLevel;
/** @var int */
private $ifLevelStart;
/** @var int */
private $limit;
/** @var int */
private $offset;
/** @var DibiHashMap */
private $identifiers;
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
}
/**
* Generates SQL.
* @param array
* @return string
* @throws DibiException
*/
public function translate(array $args)
{
$this->identifiers = new DibiHashMap(array($this, 'delimite'));
$this->driver = $this->connection->getDriver();
$args = array_values($args);
while (count($args) === 1 && is_array($args[0])) { // implicit array expansion
$args = array_values($args[0]);
}
$this->args = $args;
$this->limit = -1;
$this->offset = 0;
$this->hasError = FALSE;
$commandIns = NULL;
$lastArr = NULL;
// shortcuts
$cursor = & $this->cursor;
$cursor = 0;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
$comment = & $this->comment;
$comment = FALSE;
// iterate
$sql = array();
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',
array($this, 'cb'),
substr($arg, $toSkip)
);
if (preg_last_error()) throw new DibiPcreException;
}
continue;
}
if ($comment) {
$sql[] = '...';
continue;
}
if ($arg instanceof Traversable) {
$arg = iterator_to_array($arg);
}
if (is_array($arg)) {
if (is_string(key($arg))) {
// associative array -> autoselect between SET or VALUES & LIST
if ($commandIns === NULL) {
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
if ($lastArr === $cursor - 1) $sql[] = ',';
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
}
$lastArr = $cursor;
continue;
}
}
// default processing
$sql[] = $this->formatValue($arg, FALSE);
} // while
if ($comment) $sql[] = "*/";
$sql = implode(' ', $sql);
if ($this->hasError) {
throw new DibiException('SQL translate error', 0, $sql);
}
// apply limit
if ($this->limit > -1 || $this->offset > 0) {
$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 = array();
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') { // TODO: this will be removed
$vx[] = $k . $this->formatValue($v, 'ex');
} else {
$v = $this->formatValue($v, $pair[1]);
if ($pair[1] === 'l' || $pair[1] === 'in') {
$op = 'IN ';
} elseif (strpos($pair[1], 'like') !== FALSE) {
$op = 'LIKE ';
} elseif ($v === 'NULL') {
$op = 'IS ';
} else {
$op = '= ';
}
$vx[] = $k . $op . $v;
}
} else {
$vx[] = $this->formatValue($v, 'ex');
}
}
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) {
if (is_string($k)) {
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]};
}
}
return implode(', ', $vx);
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]} . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return implode(', ', $vx);
case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
case 'v': // (key, key, ...) VALUES (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
foreach ($value as $k => $v) {
if (is_array($v)) {
if (isset($proto)) {
if ($proto !== array_keys($v)) {
$this->hasError = TRUE;
return '**Multi-insert array "' . $k . '" is different.**';
}
} else {
$proto = array_keys($v);
}
} else {
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($v) . '**';
}
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
foreach ($v as $k2 => $v2) {
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE));
}
}
foreach ($vx as $k => $v) {
$vx[$k] = '(' . implode(', ', $v) . ')';
}
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
case 'by': // key ASC, key DESC
foreach ($value as $k => $v) {
if (is_array($v)) {
$vx[] = $this->formatValue($v, 'ex');
} elseif (is_string($k)) {
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
$vx[] = $this->identifiers->$k . ' ' . $v;
} else {
$vx[] = $this->identifiers->$v;
}
}
return implode(', ', $vx);
case 'ex':
case 'sql':
$translator = new self($this->connection);
return $translator->translate($value);
default: // value, value, value - all with the same modifier
foreach ($value as $v) {
$vx[] = $this->formatValue($v, $modifier);
}
return implode(', ', $vx);
}
}
// with modifier procession
if ($modifier) {
if ($value !== NULL && !is_scalar($value) && !($value instanceof DateTime)) { // array is already processed
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
}
switch ($modifier) {
case 's': // string
case 'bin':// binary
case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
case 'sN': // string or NULL
case 'sn':
return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
case 'iN': // signed int or NULL
case 'in': // deprecated
if ($value == '') $value = NULL;
// intentionally break omitted
case 'i': // signed int
case 'u': // unsigned int, ignored
// support for long numbers - keep them unchanged
if (is_string($value) && preg_match('#[+-]?\d++(e\d+)?$#A', $value)) {
return $value;
} else {
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
}
case 'f': // float
// support for extreme numbers - keep them unchanged
if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) {
return $value; // something like -9E-005 is accepted by SQL, HEX values are not
} else {
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value + 0, 5, '.', ''), '0'), '.');
}
case 'd': // date
case 't': // datetime
if ($value === NULL) {
return 'NULL';
} else {
if (is_numeric($value)) {
$value = (int) $value; // timestamp
} elseif (is_string($value)) {
$value = new DateTime($value);
}
return $this->driver->escape($value, $modifier);
}
case 'by':
case 'n': // identifier name
return $this->identifiers->$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',
array($this, 'cb'),
substr($value, $toSkip)
);
if (preg_last_error()) throw new DibiPcreException;
}
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':
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
default:
$this->hasError = TRUE;
return "**Unknown or invalid modifier %$modifier**";
}
}
// without modifier procession
if (is_string($value)) {
return $this->driver->escape($value, dibi::TEXT);
} elseif (is_int($value)) {
return (string) $value;
} elseif (is_float($value)) {
return rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
} elseif (is_bool($value)) {
return $this->driver->escape($value, dibi::BOOL);
} elseif ($value === NULL) {
return 'NULL';
} elseif ($value instanceof DateTime) {
return $this->driver->escape($value, dibi::DATETIME);
} elseif ($value instanceof DibiLiteral) {
return (string) $value;
} else {
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
}
}
/**
* 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)) {
$this->hasError = TRUE;
return "**Extra placeholder**";
}
$cursor++;
return $this->formatValue($this->args[$cursor - 1], FALSE);
}
if (!empty($matches[10])) { // modifier
$mod = $matches[10];
$cursor = & $this->cursor;
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
$this->hasError = TRUE;
return "**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
if ($this->args[$cursor] !== NULL) $this->limit = (int) $this->args[$cursor];
$cursor++;
return '';
} elseif ($mod === 'ofs') { // apply offset
if ($this->args[$cursor] !== NULL) $this->offset = (int) $this->args[$cursor];
$cursor++;
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]};
if ($matches[2]) // SQL identifiers: [ident]
return $this->identifiers->{$matches[2]};
if ($matches[3]) // SQL strings: '...'
return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::TEXT);
if ($matches[5]) // SQL strings: "..."
return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::TEXT);
if ($matches[7]) { // string quote
$this->hasError = TRUE;
return '**Alone quote**';
}
if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1);
$m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
}
die('this should be never executed');
}
/**
* 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->escape($v, dibi::IDENTIFIER);
}
return implode('.', $parts);
}
}

1
examples/.gitignore vendored
View File

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

File diff suppressed because one or more lines are too long

View File

@@ -1,62 +0,0 @@
Licenses
========
Good news! You may use Nette Framework 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 Nette Framework in commercial projects as long as the copyright header
remains intact.
Please be advised that the name "Nette Framework" is a protected trademark and its
usage has some limitations. So please do not use word "Nette" in the name of your
project or top-level domain, and choose a name that stands on its own merits.
If your stuff is good, it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2004, 2012 David Grudl (http://davidgrudl.com)
All rights reserved.
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 "Nette Framework" 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,3 +0,0 @@
This file is part of Nette Framework
For more information please see http://nette.org

View File

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

BIN
examples/data/arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

View File

@@ -2,7 +2,7 @@ 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;
margin: 1.6em;
padding: 0;
}
@@ -41,24 +41,24 @@ table.dump th {
}
/* dump() */
pre.nette-dump, pre.dump {
pre.tracy-dump, pre.dump {
color: #444; background: white;
border: 1px solid silver;
padding: 1em;
margin: 1em 0;
}
pre.nette-dump .php-array, pre.nette-dump .php-object {
pre.tracy-dump .php-array, pre.tracy-dump .php-object {
color: #C22;
}
pre.nette-dump .php-string {
pre.tracy-dump .php-string {
color: #080;
}
pre.nette-dump .php-int, pre.nette-dump .php-float {
pre.tracy-dump .php-int, pre.tracy-dump .php-float {
color: #37D;
}
pre.nette-dump .php-null, pre.nette-dump .php-bool {
pre.tracy-dump .php-null, pre.tracy-dump .php-bool {
color: black;
}
pre.nette-dump .php-visibility {
pre.tracy-dump .php-visibility {
font-size: 85%; color: #999;
}

View File

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

View File

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

View File

@@ -4,15 +4,17 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
ndebug();
Tracy\Debugger::enable();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
/*
@@ -30,38 +32,40 @@ product_id | title
// fetch a single row
echo "<h2>fetch()</h2>\n";
$row = dibi::fetch('SELECT title FROM products');
dump($row); // Chair
Tracy\Dumper::dump($row); // Chair
// fetch a single value
echo "<h2>fetchSingle()</h2>\n";
$value = dibi::fetchSingle('SELECT title FROM products');
dump($value); // Chair
Tracy\Dumper::dump($value); // Chair
// fetch complete result set
echo "<h2>fetchAll()</h2>\n";
$all = dibi::fetchAll('SELECT * FROM products');
dump($all);
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
dump($assoc);
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');
dump($pairs);
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) {
dump($row);
Tracy\Dumper::dump($row);
}
@@ -73,14 +77,16 @@ $res = dibi::query('
INNER JOIN customers USING (customer_id)
');
echo "<h2>fetchAssoc('customers.name|products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name|products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('name|title')</h2>\n";
$assoc = $res->fetchAssoc('name|title'); // key
Tracy\Dumper::dump($assoc);
echo "<h2>fetchAssoc('customers.name[]products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name[]products.title'); // key
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('customers.name->products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name->products.title'); // key
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

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

View File

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

View File

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

View File

@@ -4,14 +4,13 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// some variables
@@ -32,20 +31,17 @@ dibi::test('
// -> SELECT * FROM customers WHERE name LIKE 'K%'
// if & else & (optional) end
dibi::test("
dibi::test('
SELECT *
FROM people
WHERE id > 0
%if", ($foo > 0), "AND foo=?", $foo, "
%else %if", ($bar > 0), "AND bar=?", $bar, "
");
%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 *
@@ -56,3 +52,10 @@ dibi::test('
%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

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

View File

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

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

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

@@ -4,28 +4,28 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
if (@!include __DIR__ . '/../vendor/autoload.php') {
die('Install dependencies using `composer install --dev`');
}
ndebug();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
Tracy\Debugger::enable();
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// using the "prototype" to add custom method to class DibiResult
function DibiResult_prototype_fetchShuffle(DibiResult $obj)
{
// using the "prototype" to add custom method to class Dibi\Result
Dibi\Result::extensionMethod('fetchShuffle', function (Dibi\Result $obj) {
$all = $obj->fetchAll();
shuffle($all);
return $all;
}
});
// fetch complete result set shuffled
$res = dibi::query('SELECT * FROM [customers]');
$all = $res->fetchShuffle();
dump($all);
Tracy\Dumper::dump($all);

View File

@@ -4,24 +4,23 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
$id = 10;
$record = array(
'title' => 'Super product',
'price' => 318,
$record = [
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
);
];
// SELECT ...
dibi::select('product_id')->as('id')
@@ -35,7 +34,6 @@ dibi::select('product_id')->as('id')
// USING (product_id) INNER JOIN customers USING (customer_id) ORDER BY [title]
// SELECT ...
echo dibi::select('title')->as('id')
->from('products')
@@ -43,7 +41,6 @@ echo dibi::select('title')->as('id')
// -> Chair (as result of query: SELECT [title] AS [id] FROM [products])
// INSERT ...
dibi::insert('products', $record)
->setFlag('IGNORE')
@@ -51,7 +48,6 @@ dibi::insert('products', $record)
// -> INSERT IGNORE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1)
// UPDATE ...
dibi::update('products', $record)
->where('product_id = ?', $id)
@@ -59,7 +55,6 @@ dibi::update('products', $record)
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
// DELETE ...
dibi::delete('products')
->where('product_id = ?', $id)
@@ -67,7 +62,6 @@ dibi::delete('products')
// -> DELETE FROM [products] WHERE product_id = 10
// custom commands
dibi::command()
->update('products')
@@ -77,7 +71,6 @@ dibi::command()
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
dibi::command()
->truncate('products')
->test();

View File

@@ -4,14 +4,13 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
]);
// no limit
@@ -19,13 +18,11 @@ 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

View File

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

View File

@@ -1,4 +1,4 @@
<?php ob_start(1) // needed by FirePHP ?>
<?php ob_start() // needed by FirePHP ?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
@@ -6,21 +6,20 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
require __DIR__ . '/../src/loader.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
'profiler' => array(
dibi::connect([
'driver' => 'sqlite3',
'database' => 'data/sample.s3db',
'profiler' => [
'run' => TRUE,
)
));
],
]);
// execute some queries...
for ($i=0; $i<20; $i++) {
for ($i = 0; $i < 20; $i++) {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', $i);
}

View File

@@ -4,28 +4,22 @@
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
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]");
dibi::test('SELECT * FROM [:blog:items]');
// -> SELECT * FROM [wp_items]
// create new substitution :: (empty) ==> my_
dibi::getSubstitutes()->{''} = 'my_';
@@ -33,9 +27,6 @@ dibi::test("UPDATE ::table SET [text]='Hello World'");
// -> UPDATE my_table SET [text]='Hello World'
// create substitutions using fallback callback
function substFallBack($expr)
{

View File

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

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,61 +0,0 @@
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 library. If the GPL
fits better to your project, you can use the Dibi under this license.
You don't have to notify anyone which license you are using. You can freely
use Dibi in commercial projects as long as the copyright header
remains intact.
Please do not use word "Dibi" in the name of your project or top-level domain,
and choose a name that stands on its own merits. If your stuff is good,
it will not take long to establish a reputation for yourselves.
New BSD License
---------------
Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com)
All rights reserved.
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

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

@@ -1,31 +0,0 @@
Introduction
------------
Thank you for downloading Dibi!
Database access functions in PHP are not standardised. This is class library
to hide the differences between the different databases access.
Documentation and Examples
--------------------------
Refer to the 'examples' directory for examples. Dibi documentation is
available on the homepage:
http://dibiphp.com
Dibi.minified
-------------
This is shrinked single-file version of whole Dibi, useful when you don't
want to modify library, but just use it.
This is exactly the same as normal version, just only comments and
whitespaces are removed.
-----
For more information, visit the author's weblog (in czech language):
http://phpfashion.com

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,55 @@
<?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.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');
$connection->addSetup([$panel, 'register'], [$connection]);
}
}
}

View File

@@ -2,31 +2,27 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Bridges\Nette;
if (interface_exists('Nette\Diagnostics\IBarPanel')) {
class_alias('Nette\Diagnostics\IBarPanel', 'IBarPanel');
}
use Dibi;
use Dibi\Event;
use Dibi\Helpers;
use Nette;
use Nette\Diagnostics\Debugger;
/**
* Dibi panel for Nette\Diagnostics.
*
* @author David Grudl
* @package dibi\nette
*/
class DibiNettePanel extends DibiObject implements IBarPanel
class Panel implements Nette\Diagnostics\IBarPanel
{
use Dibi\Strict;
/** @var int maximum SQL length */
static public $maxLength = 1000;
public static $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
@@ -35,41 +31,29 @@ class DibiNettePanel extends DibiObject implements IBarPanel
public $filter;
/** @var array */
private $events = array();
private $events = [];
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Event::QUERY;
$this->explain = $explain;
}
public function register(DibiConnection $connection)
public function register(Dibi\Connection $connection)
{
if (is_callable('Nette\Diagnostics\Debugger::enable') && !class_exists('NDebugger')) {
class_alias('Nette\Diagnostics\Debugger', 'NDebugger'); // PHP 5.2 code compatibility
}
if (is_callable('NDebugger::enable')) {
NDebugger::$bar && NDebugger::$bar->addPanel($this);
NDebugger::$blueScreen && NDebugger::$blueScreen->addPanel(array($this, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
} elseif (is_callable('Debugger::enable')) {
Debugger::$bar && Debugger::$bar->addPanel($this);
Debugger::$blueScreen && Debugger::$blueScreen->addPanel(array($this, 'renderException'), __CLASS__);
$connection->onEvent[] = array($this, 'logEvent');
}
Debugger::getBar()->addPanel($this);
Debugger::getBlueScreen()->addPanel([__CLASS__, 'renderException']);
$connection->onEvent[] = [$this, 'logEvent'];
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
public function logEvent(Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
@@ -78,65 +62,68 @@ class DibiNettePanel extends DibiObject implements IBarPanel
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public function renderException($e)
public static function renderException($e)
{
if ($e instanceof DibiException && $e->getSql()) {
return array(
if ($e instanceof Dibi\Exception && $e->getSql()) {
return [
'tab' => 'SQL',
'panel' => dibi::dump($e->getSql(), TRUE),
);
'panel' => Helpers::dump($e->getSql(), TRUE),
];
}
}
/**
* Returns HTML code for custom tab. (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" />'
. dibi::$numOfQueries . ' queries'
. (dibi::$totalTime ? ' / ' . sprintf('%0.1f', dibi::$totalTime * 1000) . 'ms' : '')
. 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()
{
$s = NULL;
$totalTime = $s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$totalTime += $event->time;
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === DibiEvent::SELECT) {
if ($this->explain && $event->type === Event::SELECT) {
try {
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$backup = [$event->connection->onEvent, \dibi::$numOfQueries, \dibi::$totalTime];
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $e) {}
list($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime) = $backup;
$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='#' class='nette-toggler' rel='#nette-debug-DibiProfiler-row-$counter'>explain&nbsp;&#x25ba;</a>";
$s .= "<br /><a href='#nette-debug-DibiProfiler-row-$counter' class='nette-toggler nette-toggle-collapsed' rel='#nette-debug-DibiProfiler-row-$counter'>explain</a>";
}
$s .= '</td><td class="nette-DibiProfiler-sql">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
$s .= '</td><td class="nette-DibiProfiler-sql">' . Helpers::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
@@ -145,7 +132,7 @@ class DibiNettePanel extends DibiObject implements IBarPanel
if (!class_exists($helpers)) {
$helpers = class_exists('NDebugHelpers') ? 'NDebugHelpers' : 'DebugHelpers';
}
$s .= call_user_func(array($helpers, 'editorLink'), $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
$s .= call_user_func([$helpers, 'editorLink'], $event->source[0], $event->source[1])->class('nette-DibiProfiler-source');
}
$s .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
@@ -155,7 +142,7 @@ class DibiNettePanel extends DibiObject implements IBarPanel
'<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: ' . dibi::$numOfQueries . (dibi::$totalTime === NULL ? '' : ', time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms') . '</h1>
<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 . '

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

View File

@@ -2,50 +2,43 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
use Traversable;
/**
* dibi connection.
*
* @author David Grudl
* @package dibi
*
* @property-read bool $connected
* @property-read mixed $config
* @property-read IDibiDriver $driver
* @property-read int $affectedRows
* @property-read int $insertId
* @property-read DibiDatabaseInfo $databaseInfo
*/
class DibiConnection extends DibiObject
class Connection
{
/** @var array of function(DibiEvent $event); Occurs after query is executed */
use Strict;
/** @var array of function (Event $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var IDibiDriver */
/** @var Driver */
private $driver;
/** @var DibiTranslator */
/** @var Translator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var DibiHashMap Substitutes for identifiers */
/** @var HashMap Substitutes for identifiers */
private $substitutes;
/**
* Connection options: (see driver-specific options too)
* - lazy (bool) => if TRUE, connection will be established only when required
@@ -58,75 +51,74 @@ class DibiConnection extends DibiObject
* @param mixed connection parameters
* @param string connection name
* @throws DibiException
* @throws Exception
*/
public function __construct($config, $name = NULL)
{
class_exists('dibi'); // ensure class dibi is loaded
// DSN string
if (is_string($config)) {
parse_str($config, $config);
} elseif ($config instanceof Traversable) {
$tmp = array();
$tmp = [];
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} elseif (!is_array($config)) {
throw new InvalidArgumentException('Configuration must be array, string or object.');
throw new \InvalidArgumentException('Configuration must be array, string or object.');
}
self::alias($config, 'username', 'user');
self::alias($config, 'password', 'pass');
self::alias($config, 'host', 'hostname');
self::alias($config, 'result|formatDate', 'resultDate');
self::alias($config, 'result|formatDateTime', 'resultDateTime');
Helpers::alias($config, 'username', 'user');
Helpers::alias($config, 'password', 'pass');
Helpers::alias($config, 'host', 'hostname');
Helpers::alias($config, 'result|formatDate', 'resultDate');
Helpers::alias($config, 'result|formatDateTime', 'resultDateTime');
if (!isset($config['driver'])) {
$config['driver'] = dibi::$defaultDriver;
$config['driver'] = \dibi::$defaultDriver;
}
$driver = preg_replace('#[^a-z0-9_]#', '_', strtolower($config['driver']));
$class = "Dibi" . $driver . "Driver";
if (!class_exists($class, FALSE)) {
include_once dirname(__FILE__) . "/../drivers/$driver.php";
if (!class_exists($class, FALSE)) {
throw new DibiException("Unable to create instance of dibi driver '$class'.");
if ($config['driver'] instanceof Driver) {
$this->driver = $config['driver'];
$config['driver'] = get_class($this->driver);
} elseif (is_subclass_of($config['driver'], 'Dibi\Driver')) {
$this->driver = new $config['driver'];
} else {
$class = preg_replace(['#\W#', '#sql#'], ['_', 'Sql'], ucfirst(strtolower($config['driver'])));
$class = "Dibi\\Drivers\\{$class}Driver";
if (!class_exists($class)) {
throw new Exception("Unable to create instance of dibi driver '$class'.");
}
$this->driver = new $class;
}
$config['name'] = $name;
$this->config = $config;
$this->driver = new $class;
$this->translator = new DibiTranslator($this);
// profiler
$profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = array('run' => (bool) $profilerCfg);
$profilerCfg = ['run' => (bool) $profilerCfg];
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : Event::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
$this->onEvent[] = [new Loggers\FileLogger($profilerCfg['file'], $filter), 'logEvent'];
}
if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
if (Loggers\FirePhpLogger::isAvailable()) {
$this->onEvent[] = [new Loggers\FirePhpLogger($filter), 'logEvent'];
}
if (class_exists('DibiNettePanel', FALSE)) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
if (!interface_exists('Tracy\IBarPanel') && interface_exists('Nette\Diagnostics\IBarPanel') && class_exists('Dibi\Bridges\Nette\Panel')) {
$panel = new Bridges\Nette\Panel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
}
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
$this->substitutes = new HashMap(function ($expr) { return ":$expr:"; });
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
$this->substitutes->$key = $value;
@@ -139,7 +131,6 @@ class DibiConnection extends DibiObject
}
/**
* Automatically frees the resources allocated for this result set.
* @return void
@@ -151,27 +142,25 @@ class DibiConnection extends DibiObject
}
/**
* Connects to a database.
* @return void
*/
final public function connect()
{
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
$event = $this->onEvent ? new Event($this, Event::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Disconnects from a database.
* @return void
@@ -183,7 +172,6 @@ class DibiConnection extends DibiObject
}
/**
* Returns TRUE when connection was established.
* @return bool
@@ -194,7 +182,6 @@ class DibiConnection extends DibiObject
}
/**
* Returns configuration variable. If no $key is passed, returns the entire array.
* @see self::__construct
@@ -216,30 +203,17 @@ class DibiConnection extends DibiObject
}
/**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(&$config, $key, $alias)
/** @deprecated */
public static function alias(& $config, $key, $alias)
{
$foo = & $config;
foreach (explode('|', $key) as $key) $foo = & $foo[$key];
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
trigger_error(__METHOD__ . '() is deprecated, use Helpers::alias().', E_USER_DEPRECATED);
Helpers::alias($config, $key, $alias);
}
/**
* Returns the driver and connects to a database in lazy mode.
* @return IDibiDriver
* @return Driver
*/
final public function getDriver()
{
@@ -248,12 +222,11 @@ class DibiConnection extends DibiObject
}
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set object (if any)
* @throws Exception
*/
final public function query($args)
{
@@ -262,12 +235,11 @@ class DibiConnection extends DibiObject
}
/**
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @throws Exception
*/
final public function translate($args)
{
@@ -276,7 +248,6 @@ class DibiConnection extends DibiObject
}
/**
* Generates and prints SQL query.
* @param array|mixed one or more arguments
@@ -286,12 +257,12 @@ class DibiConnection extends DibiObject
{
$args = func_get_args();
try {
dibi::dump($this->translateArgs($args));
Helpers::dump($this->translateArgs($args));
return TRUE;
} catch (DibiException $e) {
} catch (Exception $e) {
if ($e->getSql()) {
dibi::dump($e->getSql());
Helpers::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
@@ -300,21 +271,19 @@ class DibiConnection extends DibiObject
}
/**
* Generates (translates) and returns SQL query as DibiDataSource.
* Generates (translates) and returns SQL query as DataSource.
* @param array|mixed one or more arguments
* @return DibiDataSource
* @throws DibiException
* @return DataSource
* @throws Exception
*/
final public function dataSource($args)
{
$args = func_get_args();
return new DibiDataSource($this->translateArgs($args), $this);
return new DataSource($this->translateArgs($args), $this);
}
/**
* Generates SQL query.
* @param array
@@ -323,28 +292,30 @@ class DibiConnection extends DibiObject
private function translateArgs($args)
{
$this->connected || $this->connect();
return $this->translator->translate($args);
if (!$this->translator) {
$this->translator = new Translator($this);
}
$translator = clone $this->translator;
return $translator->translate($args);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set object (if any)
* @throws Exception
*/
final public function nativeQuery($sql)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
dibi::$numOfQueries++;
dibi::$sql = $sql;
\dibi::$sql = $sql;
$event = $this->onEvent ? new Event($this, Event::QUERY, $sql) : NULL;
try {
$res = $this->driver->query($sql);
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
@@ -360,26 +331,26 @@ class DibiConnection extends DibiObject
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int number of rows
* @throws DibiException
* @throws Exception
*/
public function getAffectedRows()
{
$this->connected || $this->connect();
$rows = $this->driver->getAffectedRows();
if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.');
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 DibiException
* @throws Exception
*/
public function affectedRows()
{
@@ -387,28 +358,28 @@ class DibiConnection extends DibiObject
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string optional sequence name
* @return int
* @throws DibiException
* @throws Exception
*/
public function getInsertId($sequence = NULL)
{
$this->connected || $this->connect();
$id = $this->driver->getInsertId($sequence);
if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.');
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 DibiException
* @throws Exception
*/
public function insertId($sequence = NULL)
{
@@ -416,7 +387,6 @@ class DibiConnection extends DibiObject
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
@@ -425,19 +395,18 @@ class DibiConnection extends DibiObject
public function begin($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::BEGIN, $savepoint) : NULL;
try {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
@@ -446,19 +415,18 @@ class DibiConnection extends DibiObject
public function commit($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::COMMIT, $savepoint) : NULL;
try {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
@@ -467,50 +435,46 @@ class DibiConnection extends DibiObject
public function rollback($savepoint = NULL)
{
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
$event = $this->onEvent ? new Event($this, Event::ROLLBACK, $savepoint) : NULL;
try {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
} catch (Exception $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Result set factory.
* @param IDibiResultDriver
* @return DibiResult
* @param ResultDriver
* @return Result
*/
public function createResultSet(IDibiResultDriver $resultDriver)
public function createResultSet(ResultDriver $resultDriver)
{
$res = new DibiResult($resultDriver);
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
$res = new Result($resultDriver);
return $res->setFormat(Type::DATE, $this->config['result']['formatDate'])
->setFormat(Type::DATETIME, $this->config['result']['formatDateTime']);
}
/********************* fluent SQL builders ****************d*g**/
/**
* @return DibiFluent
* @return Fluent
*/
public function command()
{
return new DibiFluent($this);
return new Fluent($this);
}
/**
* @param string column name
* @return DibiFluent
* @return Fluent
*/
public function select($args)
{
@@ -519,43 +483,40 @@ class DibiConnection extends DibiObject
}
/**
* @param string table
* @param array
* @return DibiFluent
* @return Fluent
*/
public function update($table, $args)
{
if (!(is_array($args) || $args instanceof Traversable)) {
throw new InvalidArgumentException('Arguments must be array or Traversable.');
throw new \InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->update('%n', $table)->set($args);
}
/**
* @param string table
* @param array
* @return DibiFluent
* @return Fluent
*/
public function insert($table, $args)
{
if ($args instanceof Traversable) {
$args = iterator_to_array($args);
} elseif (!is_array($args)) {
throw new InvalidArgumentException('Arguments must be array or Traversable.');
throw new \InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->insert()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
}
/**
* @param string table
* @return DibiFluent
* @return Fluent
*/
public function delete($table)
{
@@ -563,14 +524,12 @@ class DibiConnection extends DibiObject
}
/********************* substitutions ****************d*g**/
/**
* Returns substitution hashmap.
* @return DibiHashMap
* @return HashMap
*/
public function getSubstitutes()
{
@@ -578,37 +537,26 @@ class DibiConnection extends DibiObject
}
/**
* Provides substitution.
* @return string
*/
public function substitute($value)
{
return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
return strpos($value, ':') === FALSE
? $value
: preg_replace_callback('#:([^:\s]*):#', function ($m) { return $this->substitutes->{$m[1]}; }, $value);
}
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
}
/********************* shortcuts ****************d*g**/
/**
* Executes SQL query and fetch result - shortcut for query() & fetch().
* @param array|mixed one or more arguments
* @return DibiRow
* @throws DibiException
* @return Row
* @throws Exception
*/
public function fetch($args)
{
@@ -617,12 +565,11 @@ class DibiConnection extends DibiObject
}
/**
* Executes SQL query and fetch results - shortcut for query() & fetchAll().
* @param array|mixed one or more arguments
* @return array of DibiRow
* @throws DibiException
* @return Row[]
* @throws Exception
*/
public function fetchAll($args)
{
@@ -631,12 +578,11 @@ class DibiConnection extends DibiObject
}
/**
* Executes SQL query and fetch first column - shortcut for query() & fetchSingle().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @throws Exception
*/
public function fetchSingle($args)
{
@@ -645,12 +591,11 @@ class DibiConnection extends DibiObject
}
/**
* Executes SQL query and fetch pairs - shortcut for query() & fetchPairs().
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
* @throws Exception
*/
public function fetchPairs($args)
{
@@ -659,71 +604,63 @@ class DibiConnection extends DibiObject
}
/**
* @return Literal
*/
public static function literal($value)
{
return new Literal($value);
}
/********************* misc ****************d*g**/
/**
* Import SQL dump from file - extreme fast!
* Import SQL dump from file.
* @param string filename
* @return int count of sql commands
*/
public function loadFile($file)
{
$this->connected || $this->connect();
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$sql = '';
while (!feof($handle)) {
$s = fgets($handle);
$sql .= $s;
if (substr(rtrim($s), -1) === ';') {
$this->driver->query($sql);
$sql = '';
$count++;
}
}
fclose($handle);
return $count;
return Helpers::loadFromFile($this, $file);
}
/**
* Gets a information about the current database.
* @return DibiDatabaseInfo
* @return Reflection\Database
*/
public function getDatabaseInfo()
{
$this->connected || $this->connect();
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
return new Reflection\Database($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
}
/**
* Prevents unserialization.
*/
public function __wakeup()
{
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
/**
* Prevents serialization.
*/
public function __sleep()
{
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
throw new NotSupportedException('You cannot serialize or unserialize ' . get_class($this) . ' instances.');
}
protected function onEvent($arg)
{
foreach ($this->onEvent ?: [] as $handler) {
call_user_func($handler, $arg);
}
}
}

View File

@@ -2,35 +2,27 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Default implementation of IDataSource for dibi.
*
* @author David Grudl
* @package dibi
*
* @property-read DibiConnection $connection
* @property-read DibiResult $result
* @property-read DibiResultIterator $iterator
* @property-read int $totalCount
*/
class DibiDataSource extends DibiObject implements IDataSource
class DataSource implements IDataSource
{
/** @var DibiConnection */
use Strict;
/** @var Connection */
private $connection;
/** @var string */
private $sql;
/** @var DibiResult */
/** @var Result */
private $result;
/** @var int */
@@ -40,13 +32,13 @@ class DibiDataSource extends DibiObject implements IDataSource
private $totalCount;
/** @var array */
private $cols = array();
private $cols = [];
/** @var array */
private $sorting = array();
private $sorting = [];
/** @var array */
private $conds = array();
private $conds = [];
/** @var int */
private $offset;
@@ -55,15 +47,14 @@ class DibiDataSource extends DibiObject implements IDataSource
private $limit;
/**
* @param string SQL command or table or view name, as data source
* @param DibiConnection connection
* @param Connection connection
*/
public function __construct($sql, DibiConnection $connection)
public function __construct($sql, Connection $connection)
{
if (strpbrk($sql, " \t\r\n") === FALSE) {
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
$this->sql = $connection->getDriver()->escapeIdentifier($sql); // table name
} else {
$this->sql = '(' . $sql . ') t'; // SQL command
}
@@ -71,12 +62,11 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Selects columns to query.
* @param string|array column name or array of column names
* @param string column alias
* @return DibiDataSource provides a fluent interface
* @param string column alias
* @return self
*/
public function select($col, $as = NULL)
{
@@ -90,11 +80,10 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Adds conditions to query.
* @param mixed conditions
* @return DibiDataSource provides a fluent interface
* @return self
*/
public function where($cond)
{
@@ -109,12 +98,11 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Selects columns to order by.
* @param string|array column name or array of column names
* @param string sorting direction
* @return DibiDataSource provides a fluent interface
* @param string sorting direction
* @return self
*/
public function orderBy($row, $sorting = 'ASC')
{
@@ -128,12 +116,11 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Limits number of rows.
* @param int limit
* @param int offset
* @return DibiDataSource provides a fluent interface
* @return self
*/
public function applyLimit($limit, $offset = NULL)
{
@@ -144,10 +131,9 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Returns the dibi connection.
* @return DibiConnection
* @return Connection
*/
final public function getConnection()
{
@@ -155,14 +141,12 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/********************* executing ****************d*g**/
/**
* Returns (and queries) DibiResult.
* @return DibiResult
* Returns (and queries) Result.
* @return Result
*/
public function getResult()
{
@@ -173,9 +157,8 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* @return DibiResultIterator
* @return ResultIterator
*/
public function getIterator()
{
@@ -183,10 +166,9 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Generates, executes SQL query and fetches the single row.
* @return DibiRow|FALSE array on success, FALSE if no next record
* @return Row|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
@@ -194,7 +176,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
@@ -205,7 +186,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Fetches all records from table.
* @return array
@@ -216,7 +196,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Fetches all records from table and returns associative tree.
* @param string associative descriptor
@@ -228,7 +207,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
@@ -241,7 +219,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Discards the internal cache.
* @return void
@@ -252,14 +229,12 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/********************* exporting ****************d*g**/
/**
* Returns this data source wrapped in DibiFluent object.
* @return DibiFluent
* Returns this data source wrapped in Fluent object.
* @return Fluent
*/
public function toFluent()
{
@@ -267,10 +242,9 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Returns this data source wrapped in DibiDataSource object.
* @return DibiDataSource
* Returns this data source wrapped in DataSource object.
* @return DataSource
*/
public function toDataSource()
{
@@ -278,7 +252,6 @@ class DibiDataSource extends DibiObject implements IDataSource
}
/**
* Returns SQL query.
* @return string
@@ -289,21 +262,19 @@ class DibiDataSource extends DibiObject implements IDataSource
return $this->connection->translate('
SELECT %n', (empty($this->cols) ? '*' : $this->cols), '
FROM %SQL', $this->sql, '
%ex', $this->conds ? array('WHERE %and', $this->conds) : NULL, '
%ex', $this->sorting ? array('ORDER BY %by', $this->sorting) : NULL, '
%ex', $this->conds ? ['WHERE %and', $this->conds] : NULL, '
%ex', $this->sorting ? ['ORDER BY %by', $this->sorting] : NULL, '
%ofs %lmt', $this->offset, $this->limit
);
} catch (Exception $e) {
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/********************* counting ****************d*g**/
/**
* Returns the number of rows in a given data source.
* @return int
@@ -321,7 +292,6 @@ FROM %SQL', $this->sql, '
}
/**
* Returns the number of rows in a given data source.
* @return int

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

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

View File

@@ -2,13 +2,13 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Firebird/InterBase database.
@@ -20,13 +20,12 @@
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author Tomáš Kraina, Roman Sklenář
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class FirebirdDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
const ERROR_EXCEPTION_THROWN = -836;
/** @var resource Connection resource */
@@ -46,56 +45,51 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
DibiConnection::alias($config, 'database', 'db');
Dibi\Helpers::alias($config, 'database', 'db');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
if (!isset($config['username'])) $config['username'] = ini_get('ibase.default_password');
if (!isset($config['password'])) $config['password'] = ini_get('ibase.default_user');
if (!isset($config['database'])) $config['database'] = ini_get('ibase.default_db');
if (!isset($config['charset'])) $config['charset'] = ini_get('ibase.default_charset');
if (!isset($config['buffers'])) $config['buffers'] = 0;
$config += [
'username' => ini_get('ibase.default_password'),
'password' => ini_get('ibase.default_user'),
'database' => ini_get('ibase.default_db'),
'charset' => ini_get('ibase.default_charset'),
'buffers' => 0,
];
DibiDriverException::tryError();
if (empty($config['persistent'])) {
$this->connection = ibase_connect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
} else {
$this->connection = ibase_pconnect($config['database'], $config['username'], $config['password'], $config['charset'], $config['buffers']); // intentionally @
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, ibase_errcode());
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode());
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode());
}
}
}
/**
* Disconnects from a database.
* @return void
@@ -106,31 +100,25 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException|Dibi\Exception
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$res = ibase_query($resource, $sql);
if (DibiDriverException::catchError($msg)) {
if ($res === FALSE) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2], $sql);
} else {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), dibi::$sql);
throw new Dibi\DriverException(ibase_errmsg(), ibase_errcode(), $sql);
}
}
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -138,7 +126,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
@@ -149,7 +136,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @param string generator name
@@ -161,66 +147,62 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->resource);
$this->transaction = ibase_trans($this->getResource());
$this->inTransaction = TRUE;
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
throw new Dibi\DriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
throw new Dibi\NotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
throw new Dibi\DriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
}
/**
* Is in transaction?
* @return bool
@@ -231,7 +213,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns the connection resource.
* @return resource
@@ -242,10 +223,9 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -253,11 +233,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -267,43 +246,55 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/********************* SQL ********************/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return $value;
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeBinary($value)
{
return "'" . str_replace("'", "''", $value) . "'";
}
public function escapeIdentifier($value)
{
return $value;
}
public function escapeBool($value)
{
return $value ? 1 : 0;
}
public function escapeDate($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d'");
}
public function escapeDateTime($value)
{
if (!$value instanceof \DateTime && !$value instanceof \DateTimeInterface) {
$value = new Dibi\DateTime($value);
}
return $value->format("'Y-m-d H:i:s'");
}
/**
* Encodes string for use in a LIKE statement.
@@ -313,49 +304,45 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function escapeLike($value, $pos)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) return;
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
if ($limit >= 0 && $offset > 0) {
// see http://scott.yang.id.au/2004/01/limit-in-select-statements-in-firebird/
$sql = 'SELECT FIRST ' . (int) $limit . ($offset > 0 ? ' SKIP ' . (int) $offset : '') . ' * FROM (' . $sql . ')';
}
}
/********************* result set ********************/
/**
* Automatically frees the resources allocated for this result set.
* @return void
@@ -366,18 +353,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
return ibase_num_fields($this->resultSet);
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
@@ -385,16 +370,15 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
*/
public function fetch($assoc)
{
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode()) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
preg_match('/exception (\d+) (\w+) (.*)/i', ibase_errmsg(), $match);
throw new DibiProcedureException($match[3], $match[1], $match[2], dibi::$sql);
preg_match('/exception (\d+) (\w+) (.*)/is', ibase_errmsg(), $match);
throw new Dibi\ProcedureException($match[3], $match[1], $match[2]);
} else {
throw new DibiDriverException($msg, ibase_errcode(), dibi::$sql);
throw new Dibi\DriverException($msg, ibase_errcode());
}
}
@@ -402,20 +386,18 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @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 DibiNotSupportedException("Firebird/Interbase do not support seek in result set.");
throw new Dibi\NotSupportedException('Firebird/Interbase do not support seek in result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -427,10 +409,9 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns the result set resource.
* @return mysqli_result
* @return resource
*/
public function getResultResource()
{
@@ -439,7 +420,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -447,23 +427,21 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
public function getResultColumns()
{
$count = ibase_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
);
];
}
return $columns;
}
/********************* IDibiReflector ********************/
/********************* Dibi\Reflector ********************/
/**
@@ -478,18 +456,17 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$tables = array();
$tables = [];
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'TRUE',
);
];
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
@@ -528,10 +505,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
ORDER BY r.RDB\$FIELD_POSITION;"
);
$columns = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$columns[$key] = array(
$columns[$key] = [
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
@@ -539,13 +516,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
'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
@@ -567,7 +543,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$indexes = array();
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$indexes[$key]['name'] = $key;
@@ -580,7 +556,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
@@ -598,20 +573,19 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$keys = array();
$keys = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$keys[$key] = array(
$keys[$key] = [
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
);
];
}
return $keys;
}
/**
* Returns list of indices in given table (the constraints are not listed).
* @param string
@@ -626,7 +600,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$indices = array();
$indices = [];
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
@@ -634,7 +608,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns list of constraints in given table.
* @param string
@@ -651,7 +624,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$constraints = array();
$constraints = [];
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
@@ -659,7 +632,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns metadata for all triggers in a table or database.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
@@ -693,23 +665,22 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
END AS TRIGGER_ENABLED
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table');")
. ($table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$triggers = array();
$triggers = [];
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = array(
$triggers[$row['TRIGGER_NAME']] = [
'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']),
'event' => trim($row['TRIGGER_EVENT']),
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
);
];
}
return $triggers;
}
/**
* Returns list of triggers for given table.
* (Only if user has permissions on ALTER TABLE, INSERT/UPDATE/DELETE record in table)
@@ -721,10 +692,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
$q = "SELECT TRIM(RDB\$TRIGGER_NAME)
FROM RDB\$TRIGGERS
WHERE RDB\$SYSTEM_FLAG = 0";
$q .= $table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table')";
$q .= $table === NULL ? ';' : " AND RDB\$RELATION_NAME = UPPER('$table')";
$res = $this->query($q);
$triggers = array();
$triggers = [];
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
@@ -732,7 +703,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns metadata from stored procedures and their input and output parameters.
* @param string
@@ -772,7 +742,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$procedures = array();
$procedures = [];
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
@@ -786,7 +756,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns list of stored procedures.
* @return array
@@ -797,7 +766,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$procedures = array();
$procedures = [];
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
@@ -805,7 +774,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns list of generators.
* @return array
@@ -817,7 +785,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$generators = array();
$generators = [];
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
@@ -825,7 +793,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
/**
* Returns list of user defined functions (UDF).
* @return array
@@ -837,7 +804,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$functions = array();
$functions = [];
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
@@ -845,44 +812,3 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultD
}
}
/**
* Database procedure exception.
*
* @author Roman Sklenář
* @copyright Copyright (c) 2010
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}

View File

@@ -2,15 +2,12 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/mysql.reflector.php';
use Dibi;
/**
@@ -29,13 +26,12 @@ require_once dirname(__FILE__) . '/mysql.reflector.php';
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class MySqlDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
@@ -53,42 +49,45 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
private $buffered;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysql')) {
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'mysql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
DibiConnection::alias($config, 'flags', 'options');
if (!isset($config['charset'])) $config['charset'] = 'utf8';
if (!isset($config['username'])) $config['username'] = ini_get('mysql.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('mysql.default_password');
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');
if (!isset($config['socket'])) {
$config['socket'] = ini_get('mysql.default_socket');
}
$config['host'] = NULL;
}
}
@@ -107,23 +106,18 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(mysql_error(), mysql_errno());
throw new Dibi\DriverException(mysql_error(), mysql_errno());
}
if (isset($config['charset'])) {
$ok = FALSE;
if (function_exists('mysql_set_charset')) {
// affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.2.3)
$ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
}
if (!$ok) {
if (!@mysql_set_charset($config['charset'], $this->connection)) { // intentionally @
$this->query("SET NAMES '$config[charset]'");
}
}
if (isset($config['database'])) {
if (!@mysql_select_db($config['database'], $this->connection)) { // intentionally @
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
throw new Dibi\DriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
}
@@ -131,13 +125,14 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
$this->query("SET sql_mode='$config[sqlmode]'");
}
$this->query("SET time_zone='" . date('P') . "'");
if (isset($config['timezone'])) {
$this->query("SET time_zone='$config[timezone]'");
}
$this->buffered = empty($config['unbuffered']);
}
/**
* Disconnects from a database.
* @return void
@@ -148,12 +143,11 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -163,8 +157,8 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
$res = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
}
if (mysql_errno($this->connection)) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection), $sql);
if ($code = mysql_errno($this->connection)) {
throw MySqliDriver::createException(mysql_error($this->connection), $code, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
@@ -172,16 +166,17 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Retrieves information about the most recently executed query.
* @return array
*/
public function getInfo()
{
$res = array();
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) throw new DibiPcreException;
if (preg_last_error()) {
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
@@ -190,7 +185,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
@@ -201,7 +195,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
@@ -212,12 +205,11 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -225,12 +217,11 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -238,12 +229,11 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -251,7 +241,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Returns the connection resource.
* @return mixed
@@ -262,22 +251,20 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -287,52 +274,62 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::BINARY:
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "_binary'" . mysql_real_escape_string($value, $this->connection) . "'";
case dibi::IDENTIFIER:
// @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
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.
@@ -347,46 +344,45 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) return;
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} 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
@@ -397,7 +393,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Returns the number of rows in a result set.
* @return int
@@ -405,13 +400,12 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysql_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
@@ -423,24 +417,22 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @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 DibiNotSupportedException('Cannot seek an unbuffered result set.');
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
@@ -452,7 +444,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -460,22 +451,22 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriv
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'name' => $row['name'],
'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => strtoupper($row['type']),
'type' => $row['type'] === 'time' ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row,
);
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed

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

@@ -2,15 +2,12 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/mysql.reflector.php';
use Dibi;
/**
@@ -30,13 +27,12 @@ require_once dirname(__FILE__) . '/mysql.reflector.php';
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
class MySqliDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
const ERROR_DATA_TRUNCATED = 1265;
@@ -54,25 +50,23 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
private $buffered;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysqli')) {
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'mysqli' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) {
@@ -80,11 +74,14 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
} else {
// default values
if (!isset($config['charset'])) $config['charset'] = 'utf8';
if (!isset($config['username'])) $config['username'] = ini_get('mysqli.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('mysqli.default_pw');
if (!isset($config['socket'])) $config['socket'] = ini_get('mysqli.default_socket');
if (!isset($config['port'])) $config['port'] = NULL;
$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) {
@@ -113,17 +110,12 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new DibiDriverException(mysqli_connect_error(), $errno);
throw new Dibi\DriverException(mysqli_connect_error(), $errno);
}
}
if (isset($config['charset'])) {
$ok = FALSE;
if (version_compare(PHP_VERSION , '5.1.5', '>=')) {
// affects the character set used by mysql_real_escape_string() (was added in MySQL 5.0.7 and PHP 5.0.5, fixed in PHP 5.1.5)
$ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @
}
if (!$ok) {
if (!@mysqli_set_charset($this->connection, $config['charset'])) {
$this->query("SET NAMES '$config[charset]'");
}
}
@@ -132,13 +124,14 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
$this->query("SET sql_mode='$config[sqlmode]'");
}
$this->query("SET time_zone='" . date('P') . "'");
if (isset($config['timezone'])) {
$this->query("SET time_zone='$config[timezone]'");
}
$this->buffered = empty($config['unbuffered']);
}
/**
* Disconnects from a database.
* @return void
@@ -149,19 +142,18 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
if ($code = mysqli_errno($this->connection)) {
throw self::createException(mysqli_error($this->connection), $code, $sql);
} elseif (is_object($res)) {
return $this->createResultDriver($res);
@@ -169,6 +161,25 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* @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.
@@ -176,9 +187,11 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getInfo()
{
$res = array();
$res = [];
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) throw new DibiPcreException;
if (preg_last_error()) {
throw new Dibi\PcreException;
}
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
@@ -187,18 +200,16 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* 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);
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
@@ -209,12 +220,11 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -222,12 +232,11 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -235,12 +244,11 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -248,7 +256,6 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns the connection resource.
* @return mysqli
@@ -259,24 +266,22 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
return new MySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver(mysqli_result $resource)
public function createResultDriver(\mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -284,45 +289,55 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
return "'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::BINARY:
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::IDENTIFIER:
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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.
@@ -337,46 +352,45 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) return;
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
// see http://dev.mysql.com/doc/refman/5.0/en/select.html
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
} 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
@@ -387,7 +401,6 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns the number of rows in a result set.
* @return int
@@ -395,13 +408,12 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
return mysqli_num_rows($this->resultSet);
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
@@ -413,23 +425,21 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @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 DibiNotSupportedException('Cannot seek an unbuffered result set.');
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
@@ -441,7 +451,6 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -449,9 +458,10 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getResultColumns()
{
static $types;
if (empty($types)) {
if ($types === NULL) {
$consts = get_defined_constants(TRUE);
foreach ($consts['mysqli'] as $key => $value) {
$types = [];
foreach (isset($consts['mysqli']) ? $consts['mysqli'] : [] as $key => $value) {
if (strncmp($key, 'MYSQLI_TYPE_', 12) === 0) {
$types[$value] = substr($key, 12);
}
@@ -460,22 +470,22 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
$count = mysqli_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$columns[] = array(
$columns[] = [
'name' => $row['name'],
'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
'nativetype' => $types[$row['type']],
'nativetype' => isset($types[$row['type']]) ? $types[$row['type']] : $row['type'],
'type' => $row['type'] === MYSQLI_TYPE_TIME ? Dibi\Type::TIME_INTERVAL : NULL,
'vendor' => $row,
);
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mysqli_result
@@ -483,7 +493,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getResultResource()
{
$this->autoFree = FALSE;
return @$this->resultSet->type === NULL ? NULL : $this->resultSet;
return $this->resultSet;
}
}

View File

@@ -2,13 +2,13 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver interacting with databases via ODBC connections.
@@ -19,13 +19,12 @@
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class OdbcDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -42,33 +41,33 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
private $row = 0;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('odbc')) {
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'odbc' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// default values
if (!isset($config['username'])) $config['username'] = ini_get('odbc.default_user');
if (!isset($config['password'])) $config['password'] = ini_get('odbc.default_pw');
if (!isset($config['dsn'])) $config['dsn'] = ini_get('odbc.default_db');
$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 @
@@ -78,12 +77,11 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
if (!is_resource($this->connection)) {
throw new DibiDriverException(odbc_errormsg() . ' ' . odbc_error());
throw new Dibi\DriverException(odbc_errormsg() . ' ' . odbc_error());
}
}
/**
* Disconnects from a database.
* @return void
@@ -94,12 +92,11 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -107,7 +104,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
@@ -116,7 +113,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
@@ -127,65 +123,60 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* 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 DibiNotSupportedException('ODBC does not support autoincrementing.');
throw new Dibi\NotSupportedException('ODBC does not support autoincrementing.');
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
if (!odbc_autocommit($this->connection, FALSE)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!odbc_commit($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!odbc_rollback($this->connection)) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
throw new Dibi\DriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection));
}
odbc_autocommit($this->connection, TRUE);
}
/**
* Is in transaction?
* @return bool
@@ -196,7 +187,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns the connection resource.
* @return mixed
@@ -207,10 +197,9 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -218,11 +207,10 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -232,43 +220,55 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("#m/d/Y#") : date("#m/d/Y#", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("#m/d/Y H:i:s#") : date("#m/d/Y H:i:s#", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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.
@@ -278,52 +278,51 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
if ($offset) throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
} 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
@@ -334,7 +333,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns the number of rows in a result set.
* @return int
@@ -346,7 +344,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
@@ -358,20 +355,23 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
return odbc_fetch_array($this->resultSet, ++$this->row);
} else {
$set = $this->resultSet;
if (!odbc_fetch_row($set, ++$this->row)) return FALSE;
if (!odbc_fetch_row($set, ++$this->row)) {
return FALSE;
}
$count = odbc_num_fields($set);
$cols = array();
for ($i = 1; $i <= $count; $i++) $cols[] = odbc_result($set, $i);
$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 boolean TRUE on success, FALSE if unable to seek to specified record
* @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)
{
@@ -380,7 +380,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -392,7 +391,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -400,20 +398,19 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$columns[] = array(
'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype'=> odbc_field_type($this->resultSet, $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
@@ -425,9 +422,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -437,13 +432,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getTables()
{
$res = odbc_tables($this->connection);
$tables = array();
$tables = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$tables[] = array(
$tables[] = [
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
);
];
}
}
odbc_free_result($res);
@@ -451,7 +446,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns metadata for all columns in a table.
* @param string
@@ -460,17 +454,17 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
public function getColumns($table)
{
$res = odbc_columns($this->connection);
$columns = array();
$columns = [];
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$columns[] = array(
$columns[] = [
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $row['TYPE_NAME'],
'size' => $row['COLUMN_SIZE'],
'nullable' => (bool) $row['NULLABLE'],
'default' => $row['COLUMN_DEF'],
);
];
}
}
odbc_free_result($res);
@@ -478,7 +472,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
}
/**
* Returns metadata for all indexes in a table.
* @param string
@@ -486,11 +479,10 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function getIndexes($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
@@ -498,7 +490,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDrive
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
}

View File

@@ -2,13 +2,13 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for Oracle database.
@@ -18,16 +18,17 @@
* - 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
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - persistent => Creates persistent connections with oci_pconnect instead of oci_new_connect
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class OracleDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -44,25 +45,23 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
private $fmtDate, $fmtDateTime;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('oci8')) {
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'oci8' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
$foo = & $config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
@@ -70,18 +69,23 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
} 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 DibiDriverException($err['message'], $err['code']);
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
@@ -92,32 +96,50 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
$res = oci_parse($this->connection, $sql);
if ($res) {
oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
@oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res);
if ($err) {
throw new DibiDriverException($err['message'], $err['code'], $sql);
throw self::createException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
} else {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code'], $sql);
throw new Dibi\DriverException($err['message'], $err['code'], $sql);
}
}
/**
* @return 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.
@@ -125,11 +147,10 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getAffectedRows()
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
@@ -141,7 +162,6 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
@@ -153,41 +173,38 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
if (!oci_commit($this->connection)) {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code']);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
if (!oci_rollback($this->connection)) {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code']);
throw new Dibi\DriverException($err['message'], $err['code']);
}
$this->autocommit = TRUE;
}
/**
* Returns the connection resource.
* @return mixed
@@ -198,10 +215,9 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -209,11 +225,10 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -223,44 +238,56 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'"; // TODO: not tested
case dibi::IDENTIFIER:
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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.
@@ -276,48 +303,49 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($offset > 0) {
// see http://www.oracle.com/technology/oramag/oracle/06-sep/o56asktom.html
$sql = 'SELECT * FROM (SELECT t.*, ROWNUM AS "__rnum" FROM (' . $sql . ') t ' . ($limit >= 0 ? 'WHERE ROWNUM <= ' . ((int) $offset + (int) $limit) : '') . ') WHERE "__rnum" > '. (int) $offset;
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit >= 0) {
} 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
@@ -328,18 +356,16 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
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
@@ -351,19 +377,17 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* 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
* @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 DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -375,7 +399,6 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -383,20 +406,20 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 1; $i <= $count; $i++) {
$columns[] = array(
$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'=> oci_field_type($this->resultSet, $i),
);
'nativetype' => $type === 'NUMBER' && oci_field_scale($this->resultSet, $i) === 0 ? 'INTEGER' : $type,
];
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
@@ -408,9 +431,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -420,20 +441,19 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
public function getTables()
{
$res = $this->query('SELECT * FROM cat');
$tables = array();
$tables = [];
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$tables[] = array(
$tables[] = [
'name' => $row[0],
'view' => $row[1] === 'VIEW',
);
];
}
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
@@ -441,11 +461,10 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getColumns($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
@@ -453,11 +472,10 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getIndexes($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
@@ -465,7 +483,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDri
*/
public function getForeignKeys($table)
{
throw new DibiNotImplementedException;
throw new Dibi\NotImplementedException;
}
}

View File

@@ -0,0 +1,554 @@
<?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':
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 '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 'sqlsrv':
case 'dblib':
if (version_compare($this->serverVersion, '11.0') >= 0) { // 11 == SQL Server 2012
if ($limit !== NULL || $offset) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql .= ' OFFSET ' . (int) $offset . ' ROWS '
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
}
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

@@ -2,13 +2,13 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi driver for PostgreSQL database.
@@ -20,13 +20,12 @@
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
class PostgreDriver implements Dibi\Driver, Dibi\ResultDriver, Dibi\Reflector
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -39,78 +38,73 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var bool Escape method */
private $escMethod = FALSE;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pgsql')) {
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'pgsql' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
$error = NULL;
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
if (!isset($config['charset'])) $config['charset'] = 'utf8';
$config += [
'charset' => 'utf8',
];
if (isset($config['string'])) {
$string = $config['string'];
} else {
$string = '';
DibiConnection::alias($config, 'user', 'username');
DibiConnection::alias($config, 'dbname', 'database');
foreach (array('host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service') as $key) {
if (isset($config[$key])) $string .= $key . '=' . $config[$key] . ' ';
Dibi\Helpers::alias($config, 'user', 'username');
Dibi\Helpers::alias($config, 'dbname', 'database');
foreach (['host', 'hostaddr', 'port', 'dbname', 'user', 'password', 'connect_timeout', 'options', 'sslmode', 'service'] as $key) {
if (isset($config[$key])) {
$string .= $key . '=' . $config[$key] . ' ';
}
}
}
DibiDriverException::tryError();
set_error_handler(function($severity, $message) use (& $error) {
$error = $message;
});
if (empty($config['persistent'])) {
$this->connection = pg_connect($string, PGSQL_CONNECT_FORCE_NEW);
} else {
$this->connection = pg_pconnect($string, PGSQL_CONNECT_FORCE_NEW);
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
restore_error_handler();
}
if (!is_resource($this->connection)) {
throw new DibiDriverException('Connecting error.');
throw new Dibi\DriverException($error ?: 'Connecting error.');
}
if (isset($config['charset'])) {
DibiDriverException::tryError();
pg_set_client_encoding($this->connection, $config['charset']);
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, 0);
}
pg_set_error_verbosity($this->connection, PGSQL_ERRORS_VERBOSE);
if (isset($config['charset']) && pg_set_client_encoding($this->connection, $config['charset'])) {
throw self::createException(pg_last_error($this->connection));
}
if (isset($config['schema'])) {
$this->query('SET search_path TO "' . $config['schema'] . '"');
}
$this->escMethod = version_compare(PHP_VERSION , '5.2.0', '>=');
}
/**
* Disconnects from a database.
* @return void
@@ -121,12 +115,21 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Pings database.
* @return bool
*/
public function ping()
{
return pg_ping($this->connection);
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -134,7 +137,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
$res = @pg_query($this->connection, $sql); // intentionally @
if ($res === FALSE) {
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
throw self::createException(pg_last_error($this->connection), NULL, $sql);
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
@@ -145,6 +148,33 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* @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.
@@ -156,7 +186,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
@@ -165,24 +194,25 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
{
if ($sequence === NULL) {
// PostgreSQL 8.1 is needed
$res = $this->query("SELECT LASTVAL()");
$res = $this->query('SELECT LASTVAL()');
} else {
$res = $this->query("SELECT CURRVAL('$sequence')");
}
if (!$res) return FALSE;
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 DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -190,12 +220,11 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -203,12 +232,11 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -216,18 +244,16 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
return !in_array(pg_transaction_status($this->connection), [PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE], TRUE);
}
/**
* Returns the connection resource.
* @return mixed
@@ -238,10 +264,9 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
@@ -249,11 +274,10 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -263,60 +287,62 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
if ($this->escMethod) {
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_string($this->connection, $value) . "'";
} else {
return "'" . pg_escape_string($value) . "'";
}
case dibi::BINARY:
if ($this->escMethod) {
if (!is_resource($this->connection)) {
throw new DibiException('Lost connection to server.');
}
return "'" . pg_escape_bytea($this->connection, $value) . "'";
} else {
return "'" . pg_escape_bytea($value) . "'";
}
case dibi::IDENTIFIER:
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 'TRUE' : 'FALSE';
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
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.
@@ -326,57 +352,53 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function escapeLike($value, $pos)
{
if ($this->escMethod) {
$value = pg_escape_string($this->connection, $value);
} else {
$value = pg_escape_string($value);
}
$value = strtr($value, array( '%' => '\\\\%', '_' => '\\\\_'));
$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 value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return pg_unescape_bytea($value);
}
throw new InvalidArgumentException('Unsupported type.');
return pg_unescape_bytea($value);
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit >= 0)
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
}
if ($limit !== NULL) {
$sql .= ' LIMIT ' . (int) $limit;
if ($offset > 0)
}
if ($offset) {
$sql .= ' OFFSET ' . (int) $offset;
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
@@ -387,7 +409,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the number of rows in a result set.
* @return int
@@ -398,7 +419,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
@@ -410,11 +430,10 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* 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
* @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)
{
@@ -422,7 +441,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -434,22 +452,20 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$hasTable = version_compare(PHP_VERSION , '5.2.0', '>=');
$count = pg_num_fields($this->resultSet);
$columns = array();
$columns = [];
for ($i = 0; $i < $count; $i++) {
$row = array(
'name' => pg_field_name($this->resultSet, $i),
'table' => $hasTable ? pg_field_table($this->resultSet, $i) : NULL,
'nativetype'=> pg_field_type($this->resultSet, $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;
}
@@ -457,7 +473,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the result set resource.
* @return mixed
@@ -469,9 +484,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/********************* IDibiReflector ****************d*g**/
/********************* Dibi\Reflector ****************d*g**/
/**
@@ -480,12 +493,12 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getTables()
{
$version = pg_parameter_status($this->resource, 'server_version');
$version = pg_parameter_status($this->getResource(), 'server_version');
if ($version < 7.4) {
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
throw new Dibi\DriverException('Reflection requires PostgreSQL 7.4 and newer.');
}
$res = $this->query("
$query = "
SELECT
table_name AS name,
CASE table_type
@@ -495,12 +508,23 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
FROM
information_schema.tables
WHERE
table_schema = current_schema()
");
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : array();
}
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 : [];
}
/**
@@ -510,40 +534,66 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getColumns($table)
{
$_table = $this->escape($table, dibi::TEXT);
$_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.relname = $_table
WHERE pg_class.oid = $_table::regclass
");
$primary = (int) pg_fetch_object($res->resultSet)->indkey;
$res = $this->query("
SELECT *
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
ORDER BY ordinal_position
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
");
$columns = array();
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[] = array(
$columns[] = [
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
'size' => $size ? $size : NULL,
'nullable' => $row['is_nullable'] === 'YES',
'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
@@ -551,15 +601,22 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getIndexes($table)
{
$_table = $this->escape($table, dibi::TEXT);
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT ordinal_position, column_name
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
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 = array();
$columns = [];
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
@@ -569,10 +626,10 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
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.relname = $_table
WHERE pg_class.oid = $_table::regclass
");
$indexes = array();
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
@@ -585,7 +642,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
@@ -593,7 +649,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
*/
public function getForeignKeys($table)
{
$_table = $this->escape($table, dibi::TEXT);
$_table = $this->escapeText($this->escapeIdentifier($table));
$res = $this->query("
SELECT
@@ -634,17 +690,17 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDr
c.conrelid = $_table::regclass
");
$fKeys = $references = array();
$fKeys = $references = [];
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = array(
$fKeys[$row['name']] = [
'name' => $row['name'],
'table' => $row['table'],
'local' => array(),
'foreign' => array(),
'local' => [],
'foreign' => [],
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
);
];
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));

View File

@@ -2,15 +2,13 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
require_once dirname(__FILE__) . '/sqlite.reflector.php';
use Dibi;
use SQLite3;
/**
@@ -23,17 +21,16 @@ require_once dirname(__FILE__) . '/sqlite.reflector.php';
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
class Sqlite3Driver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var SQLite3 Connection resource */
private $connection;
/** @var SQLite3Result Resultset resource */
/** @var \SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
@@ -46,37 +43,36 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
private $dbcharset, $charset;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new DibiNotSupportedException("PHP extension 'sqlite3' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'sqlite3' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
DibiConnection::alias($config, 'database', 'file');
Dibi\Helpers::alias($config, 'database', 'file');
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource']) && $config['resource'] instanceof SQLite3) {
$this->connection = $config['resource'];
} else try {
$this->connection = new SQLite3($config['database']);
} catch (Exception $e) {
throw new DibiDriverException($e->getMessage(), $e->getCode());
} 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'];
@@ -88,12 +84,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
// 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");
$this->query('PRAGMA foreign_keys = ON');
}
}
/**
* Disconnects from a database.
* @return void
@@ -104,12 +99,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -118,15 +112,44 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
$res = @$this->connection->query($sql); // intentionally @
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
if ($code = $this->connection->lastErrorCode()) {
throw self::createException($this->connection->lastErrorMsg(), $code, $sql);
} elseif ($res instanceof SQLite3Result) {
} elseif ($res instanceof \SQLite3Result) {
return $this->createResultDriver($res);
}
}
/**
* @return 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.
@@ -138,7 +161,6 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
@@ -149,12 +171,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -162,12 +183,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -175,12 +195,11 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -188,7 +207,6 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the connection resource.
* @return mixed
@@ -199,24 +217,22 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
return new SqliteReflector($this);
}
/**
* Result set driver factory.
* @param SQLite3Result
* @return IDibiResultDriver
* @param \SQLite3Result
* @return Dibi\ResultDriver
*/
public function createResultDriver(SQLite3Result $resource)
public function createResultDriver(\SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
@@ -224,45 +240,55 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
return "'" . $this->connection->escapeString($value) . "'";
case dibi::BINARY:
return "X'" . bin2hex((string) $value) . "'";
case dibi::IDENTIFIER:
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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.
@@ -277,43 +303,44 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Dibi\Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
if ($limit < 0 && $offset < 1) return;
$sql .= ' LIMIT ' . $limit . ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
} elseif ($limit !== NULL || $offset) {
$sql .= ' LIMIT ' . ($limit === NULL ? '-1' : (int) $limit)
. ($offset ? ' OFFSET ' . (int) $offset : '');
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
@@ -324,19 +351,17 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns the number of rows in a result set.
* @return int
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
throw new Dibi\NotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
@@ -347,12 +372,12 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = array();
$tmp = [];
foreach ($row as $k => $v) {
if ($charset !== NULL && is_string($v)) {
$v = iconv($this->dbcharset, $charset, $v);
}
$tmp[str_replace(array('[', ']'), '', $k)] = $v;
$tmp[str_replace(['[', ']'], '', $k)] = $v;
}
return $tmp;
}
@@ -360,20 +385,18 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiNotSupportedException
* @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 DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -385,7 +408,6 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/**
* Returns metadata for all columns in a result set.
* @return array
@@ -393,21 +415,20 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
public function getResultColumns()
{
$count = $this->resultSet->numColumns();
$columns = array();
static $types = array(SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null');
$columns = [];
static $types = [SQLITE3_INTEGER => 'int', SQLITE3_FLOAT => 'float', SQLITE3_TEXT => 'text', SQLITE3_BLOB => 'blob', SQLITE3_NULL => 'null'];
for ($i = 0; $i < $count; $i++) {
$columns[] = array(
'name' => $this->resultSet->columnName($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
@@ -419,11 +440,9 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
* @param string function name
@@ -431,13 +450,12 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* @param int num of arguments
* @return void
*/
public function registerFunction($name, $callback, $numArgs = -1)
public function registerFunction($name, callable $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
* @param string function name
@@ -446,7 +464,7 @@ class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDr
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
public function registerAggregateFunction($name, callable $rowCallback, callable $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}

View File

@@ -2,35 +2,32 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
/**
* The dibi reflector for SQLite database.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class DibiSqliteReflector extends DibiObject implements IDibiReflector
class SqliteReflector implements Dibi\Reflector
{
/** @var IDibiDriver */
use Dibi\Strict;
/** @var Dibi\Driver */
private $driver;
public function __construct(IDibiDriver $driver)
public function __construct(Dibi\Driver $driver)
{
$this->driver = $driver;
}
/**
* Returns list of tables.
* @return array
@@ -43,7 +40,7 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = array();
$tables = [];
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
@@ -51,7 +48,6 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
}
/**
* Returns metadata for all columns in a table.
* @param string
@@ -59,19 +55,12 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getColumns($table)
{
$meta = $this->driver->query("
SELECT sql FROM sqlite_master WHERE type = 'table' AND name = '$table'
UNION ALL
SELECT sql FROM sqlite_temp_master WHERE type = 'table' AND name = '$table'"
)->fetch(TRUE);
$res = $this->driver->query("PRAGMA table_info([$table])");
$columns = array();
$res = $this->driver->query("PRAGMA table_info({$this->driver->escapeIdentifier($table)})");
$columns = [];
while ($row = $res->fetch(TRUE)) {
$column = $row['name'];
$pattern = "/(\"$column\"|\[$column\]|$column)\s+[^,]+\s+PRIMARY\s+KEY\s+AUTOINCREMENT/Ui";
$type = explode('(', $row['type']);
$columns[] = array(
$columns[] = [
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
@@ -79,15 +68,14 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
'autoincrement' => $row['pk'] && $type[0] === 'INTEGER',
'vendor' => $row,
);
];
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
@@ -95,15 +83,15 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list([$table])");
$indexes = array();
$res = $this->driver->query("PRAGMA index_list({$this->driver->escapeIdentifier($table)})");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info([$index])");
$res = $this->driver->query("PRAGMA index_info({$this->driver->escapeIdentifier($index)})");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
@@ -124,12 +112,12 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = array(
$indexes[] = [
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => array($column['name']),
);
'columns' => [$column['name']],
];
break;
}
}
@@ -139,7 +127,6 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
@@ -147,11 +134,8 @@ class DibiSqliteReflector extends DibiObject implements IDibiReflector
*/
public function getForeignKeys($table)
{
if (!($this->driver instanceof DibiSqlite3Driver)) {
// throw new DibiNotSupportedException; // @see http://www.sqlite.org/foreignkeys.html
}
$res = $this->driver->query("PRAGMA foreign_key_list([$table])");
$keys = array();
$res = $this->driver->query("PRAGMA foreign_key_list({$this->driver->escapeIdentifier($table)})");
$keys = [];
while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns

View File

@@ -2,32 +2,33 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Drivers;
use Dibi;
use Dibi\Connection;
use Dibi\Helpers;
/**
* The dibi driver for MS SQL Driver 2005 database.
* The dibi driver for Microsoft SQL Server and SQL Azure databases.
*
* Driver options:
* - host => the MS SQL server host name. It can also include a port number (hostname:port)
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => connection options {@link http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - options (array) => connection options {@link https://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
* - lazy, profiler, result, substitutes, ... => see Dibi\Connection options
*/
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
class SqlsrvDriver implements Dibi\Driver, Dibi\ResultDriver
{
use Dibi\Strict;
/** @var resource Connection resource */
private $connection;
@@ -40,47 +41,53 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $version;
/**
* @throws DibiNotSupportedException
* @throws Dibi\NotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
throw new Dibi\NotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
/**
* Connects to a database.
* @return void
* @throws DibiException
* @throws Dibi\Exception
*/
public function connect(array &$config)
public function connect(array & $config)
{
DibiConnection::alias($config, 'options|UID', 'username');
DibiConnection::alias($config, 'options|PWD', 'password');
DibiConnection::alias($config, 'options|Database', 'database');
DibiConnection::alias($config, 'options|CharacterSet', 'charset');
Helpers::alias($config, 'options|UID', 'username');
Helpers::alias($config, 'options|PWD', 'password');
Helpers::alias($config, 'options|Database', 'database');
Helpers::alias($config, 'options|CharacterSet', 'charset');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} else {
// Default values
if (!isset($config['options']['CharacterSet'])) {
$config['options']['CharacterSet'] = 'UTF-8';
}
$this->connection = sqlsrv_connect($config['host'], (array) $config['options']);
}
if (!is_resource($this->connection)) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code']);
throw new Dibi\DriverException($info[0]['message'], $info[0]['code']);
}
$this->version = sqlsrv_server_info($this->connection)['SQLServerVersion'];
}
/**
* Disconnects from a database.
* @return void
@@ -91,12 +98,11 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return Dibi\ResultDriver|NULL
* @throws Dibi\DriverException
*/
public function query($sql)
{
@@ -105,7 +111,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
if ($res === FALSE) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
throw new Dibi\DriverException($info[0]['message'], $info[0]['code'], $sql);
} elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res);
@@ -114,7 +120,6 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Gets the number of affected rows by the last INSERT, UPDATE or DELETE query.
* @return int|FALSE number of rows or FALSE on error
@@ -125,7 +130,6 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
@@ -141,12 +145,11 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function begin($savepoint = NULL)
{
@@ -154,12 +157,11 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function commit($savepoint = NULL)
{
@@ -167,12 +169,11 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws Dibi\DriverException
*/
public function rollback($savepoint = NULL)
{
@@ -180,7 +181,6 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Returns the connection resource.
* @return mixed
@@ -191,22 +191,20 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Dibi\Reflector
*/
public function getReflector()
{
throw new DibiNotSupportedException;
return new SqlsrvReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
* @return Dibi\ResultDriver
*/
public function createResultDriver($resource)
{
@@ -216,44 +214,56 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
*/
public function escape($value, $type)
public function escapeText($value)
{
switch ($type) {
case dibi::TEXT:
case dibi::BINARY:
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return $value instanceof DateTime ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
default:
throw new InvalidArgumentException('Unsupported type.');
}
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.
@@ -263,54 +273,58 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
*/
public function escapeLike($value, $pos)
{
$value = strtr($value, array("'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]'));
$value = strtr($value, ["'" => "''", '%' => '[%]', '_' => '[_]', '[' => '[[]']);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
public function unescape($value, $type)
public function unescapeBinary($value)
{
if ($type === dibi::BINARY) {
return $value;
}
throw new InvalidArgumentException('Unsupported type.');
return $value;
}
/** @deprecated */
public function escape($value, $type)
{
trigger_error(__METHOD__ . '() is deprecated.', E_USER_DEPRECATED);
return Helpers::escape($this, $value, $type);
}
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
public function applyLimit(&$sql, $limit, $offset)
public function applyLimit(& $sql, $limit, $offset)
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($limit < 0 || $offset < 0) {
throw new Dibi\NotSupportedException('Negative offset or limit.');
if ($offset) {
throw new DibiNotImplementedException('Offset is not implemented.');
} elseif (version_compare($this->version, 11, '<')) { // 11 == SQL Server 2012
if ($offset) {
throw new Dibi\NotSupportedException('Offset is not supported by this database.');
} elseif ($limit !== NULL) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
} elseif ($limit !== NULL || $offset) {
// requires ORDER BY, see https://technet.microsoft.com/en-us/library/gg699618(v=sql.110).aspx
$sql .= ' OFFSET ' . (int) $offset . ' ROWS '
. 'FETCH NEXT ' . (int) $limit . ' ROWS ONLY';
}
}
/********************* result set ****************d*g**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
@@ -321,18 +335,16 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
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
@@ -344,19 +356,17 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* 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
* @param int the 0-based cursor pos to seek to
* @return bool TRUE on success, FALSE if unable to seek to specified record
*/
public function seek($row)
{
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
throw new Dibi\NotSupportedException('Cannot seek an unbuffered result set.');
}
/**
* Frees the resources allocated for this result set.
* @return void
@@ -368,28 +378,24 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResult
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
public function getResultColumns()
{
$count = sqlsrv_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) sqlsrv_field_metadata($this->resultSet, $i);
$columns[] = array(
'name' => $row['Name'],
'fullname' => $row['Name'],
'nativetype' => $row['Type'],
);
$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

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');
$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("SELECT * FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = {$this->driver->escapeText($table)}");
$keyUsages = [];
while ($row = $keyUsagesRes->fetch(TRUE)) {
$keyUsages[$row['CONSTRAINT_NAME']][(int) $row['ORDINAL_POSITION'] - 1] = $row['COLUMN_NAME'];
}
$res = $this->driver->query("SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = {$this->driver->escapeText($table)}");
$indexes = [];
while ($row = $res->fetch(TRUE)) {
$indexes[$row['CONSTRAINT_NAME']]['name'] = $row['CONSTRAINT_NAME'];
$indexes[$row['CONSTRAINT_NAME']]['unique'] = $row['CONSTRAINT_TYPE'] === 'UNIQUE';
$indexes[$row['CONSTRAINT_NAME']]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$row['CONSTRAINT_NAME']]['columns'] = isset($keyUsages[$row['CONSTRAINT_NAME']]) ? $keyUsages[$row['CONSTRAINT_NAME']] : [];
}
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;
}
}

View File

@@ -2,23 +2,19 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Profiler & logger event.
*
* @author David Grudl
* @package dibi
*/
class DibiEvent
class Event
{
use Strict;
/** event type */
const CONNECT = 1,
SELECT = 4,
@@ -32,7 +28,7 @@ class DibiEvent
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var DibiConnection */
/** @var Connection */
public $connection;
/** @var int */
@@ -41,7 +37,7 @@ class DibiEvent
/** @var string */
public $sql;
/** @var DibiResult|DibiDriverException|NULL */
/** @var Result|DriverException|NULL */
public $result;
/** @var float */
@@ -54,8 +50,7 @@ class DibiEvent
public $source;
public function __construct(DibiConnection $connection, $type, $sql = NULL)
public function __construct(Connection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
@@ -63,39 +58,40 @@ class DibiEvent
$this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = array(
static $types = [
'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
);
];
$this->type = $types[strtoupper($matches[1])];
}
$rc = new ReflectionClass('dibi');
$rc = new \ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = array($row['file'], (int) $row['line']);
$this->source = [$row['file'], (int) $row['line']];
break;
}
}
dibi::$elapsedTime = FALSE;
\dibi::$elapsedTime = FALSE;
\dibi::$numOfQueries++;
\dibi::$sql = $sql;
}
public function done($result = NULL)
{
$this->result = $result;
try {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = $result instanceof Result ? count($result) : NULL;
} catch (Exception $e) {
$this->count = NULL;
}
$this->time += microtime(TRUE);
dibi::$elapsedTime = $this->time;
dibi::$totalTime += $this->time;
\dibi::$elapsedTime = $this->time;
\dibi::$totalTime += $this->time;
return $this;
}

View File

@@ -2,49 +2,49 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @author David Grudl
* @package dibi
*
* @property-read string $command
* @property-read DibiConnection $connection
* @property-read DibiResultIterator $iterator
* @method DibiFluent select($field)
* @method DibiFluent distinct()
* @method DibiFluent from($table)
* @method DibiFluent where($cond)
* @method DibiFluent groupBy($field)
* @method DibiFluent having($cond)
* @method DibiFluent orderBy($field)
* @method DibiFluent limit(int $limit)
* @method DibiFluent offset(int $offset)
* @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 on(...$cond)
* @method Fluent using(...$cond)
*/
class DibiFluent extends DibiObject implements IDataSource
class Fluent implements IDataSource
{
use Strict;
const REMOVE = FALSE;
/** @var array */
public static $masks = array(
'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'),
'UPDATE' => array('UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'),
'INSERT' => array('INSERT', 'INTO', 'VALUES', 'SELECT'),
'DELETE' => array('DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'),
);
public static $masks = [
'SELECT' => ['SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
'HAVING', 'ORDER BY', 'LIMIT', 'OFFSET'],
'UPDATE' => ['UPDATE', 'SET', 'WHERE', 'ORDER BY', 'LIMIT'],
'INSERT' => ['INSERT', 'INTO', 'VALUES', 'SELECT'],
'DELETE' => ['DELETE', 'FROM', 'USING', 'WHERE', 'ORDER BY', 'LIMIT'],
];
/** @var array default modifiers for arrays */
public static $modifiers = array(
public static $modifiers = [
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%in',
@@ -54,10 +54,10 @@ class DibiFluent extends DibiObject implements IDataSource
'HAVING' => '%and',
'ORDER BY' => '%by',
'GROUP BY' => '%by',
);
];
/** @var array clauses separators */
public static $separators = array(
public static $separators = [
'SELECT' => ',',
'FROM' => ',',
'WHERE' => 'AND',
@@ -69,58 +69,56 @@ class DibiFluent extends DibiObject implements IDataSource
'SET' => ',',
'VALUES' => ',',
'INTO' => FALSE,
);
];
/** @var array clauses */
public static $clauseSwitches = array(
public static $clauseSwitches = [
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
);
];
/** @var DibiConnection */
/** @var Connection */
private $connection;
/** @var array */
private $setups = array();
private $setups = [];
/** @var string */
private $command;
/** @var array */
private $clauses = array();
private $clauses = [];
/** @var array */
private $flags = array();
private $flags = [];
/** @var array */
private $cursor;
/** @var DibiHashMap normalized clauses */
/** @var HashMap normalized clauses */
private static $normalizer;
/**
* @param DibiConnection
* @param Connection
*/
public function __construct(DibiConnection $connection)
public function __construct(Connection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
self::$normalizer = new HashMap([__CLASS__, '_formatClause']);
}
}
/**
* Appends new argument to the clause.
* @param string clause name
* @param array arguments
* @return DibiFluent provides a fluent interface
* @return self
*/
public function __call($clause, $args)
{
@@ -132,7 +130,7 @@ class DibiFluent extends DibiObject implements IDataSource
$this->clauses = array_fill_keys(self::$masks[$clause], NULL);
}
$this->cursor = & $this->clauses[$clause];
$this->cursor = array();
$this->cursor = [];
$this->command = $clause;
}
@@ -146,7 +144,7 @@ class DibiFluent extends DibiObject implements IDataSource
$this->cursor = & $this->clauses[$clause];
// TODO: really delete?
if ($args === array(self::REMOVE)) {
if ($args === [self::REMOVE]) {
$this->cursor = NULL;
return $this;
}
@@ -154,7 +152,7 @@ class DibiFluent extends DibiObject implements IDataSource
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) { // means: replace
$this->cursor = array();
$this->cursor = [];
} elseif (!empty($this->cursor)) {
$this->cursor[] = $sep;
@@ -163,7 +161,7 @@ class DibiFluent extends DibiObject implements IDataSource
} else {
// append to currect flow
if ($args === array(self::REMOVE)) {
if ($args === [self::REMOVE]) {
return $this;
}
@@ -171,7 +169,7 @@ class DibiFluent extends DibiObject implements IDataSource
}
if ($this->cursor === NULL) {
$this->cursor = array();
$this->cursor = [];
}
// special types or argument
@@ -181,22 +179,22 @@ class DibiFluent extends DibiObject implements IDataSource
if ($arg === TRUE) { // flag
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*$#i', $arg)) { // identifier
$args = array('%n', $arg);
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*\z#i', $arg)) { // identifier
$args = ['%n', $arg];
} elseif (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
} elseif (is_array($arg) || ($arg instanceof \Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = array(self::$modifiers[$clause], $arg);
$args = [self::$modifiers[$clause], $arg];
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
$args = ['%a', $arg];
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = "($arg)";
$arg = new Literal("($arg)");
}
$this->cursor[] = $arg;
}
@@ -205,33 +203,26 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Switch to a clause.
* @param string clause name
* @return DibiFluent provides a fluent interface
* @return self
*/
public function clause($clause, $remove = FALSE)
public function clause($clause)
{
$this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($remove) { // deprecated, use removeClause
trigger_error(__METHOD__ . '(..., TRUE) is deprecated; use removeClause() instead.', E_USER_NOTICE);
$this->cursor = NULL;
} elseif ($this->cursor === NULL) {
$this->cursor = array();
if ($this->cursor === NULL) {
$this->cursor = [];
}
return $this;
}
/**
* Removes a clause.
* @param string clause name
* @return DibiFluent provides a fluent interface
* @return self
*/
public function removeClause($clause)
{
@@ -240,12 +231,11 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Change a SQL flag.
* @param string flag name
* @param bool value
* @return DibiFluent provides a fluent interface
* @return self
*/
public function setFlag($flag, $value = TRUE)
{
@@ -259,7 +249,6 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Is a flag set?
* @param string flag name
@@ -271,7 +260,6 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Returns SQL command.
* @return string
@@ -282,10 +270,9 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Returns the dibi connection.
* @return DibiConnection
* @return Connection
*/
final public function getConnection()
{
@@ -293,12 +280,11 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Adds DibiResult setup.
* Adds Result setup.
* @param string method
* @param mixed args
* @return DibiFluent provides a fluent interface
* @return self
*/
public function setupResult($method)
{
@@ -307,55 +293,57 @@ class DibiFluent extends DibiObject implements IDataSource
}
/********************* executing ****************d*g**/
/**
* Generates and executes SQL query.
* @param mixed what to return?
* @return DibiResult|int result set object (if any)
* @throws DibiException
* @return Result|int result set object (if any)
* @throws Exception
*/
public function execute($return = NULL)
{
$res = $this->query($this->_export());
return $return === dibi::IDENTIFIER ? $this->connection->getInsertId() : $res;
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 DibiRow|FALSE array on success, FALSE if no next record
* @return Row|FALSE array on success, FALSE if no next record
*/
public function fetch()
{
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetch();
} else {
return $this->query($this->_export())->fetch();
}
}
/**
* Like fetch(), but returns only first field.
* @return mixed value on success, FALSE if no next record
*/
public function fetchSingle()
{
if ($this->command === 'SELECT') {
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
if ($this->command === 'SELECT' && !$this->clauses['LIMIT']) {
return $this->query($this->_export(NULL, ['%lmt', 1]))->fetchSingle();
} else {
return $this->query($this->_export())->fetchSingle();
}
}
/**
* Fetches all records from table.
* @param int offset
@@ -364,11 +352,10 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchAll($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->fetchAll();
}
/**
* Fetches all records from table and returns associative tree.
* @param string associative descriptor
@@ -380,7 +367,6 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
@@ -393,20 +379,18 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return DibiResultIterator
* @return ResultIterator
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
return $this->query($this->_export(NULL, ['%ofs %lmt', $offset, $limit]))->getIterator();
}
/**
* Generates and prints SQL query or it's part.
* @param string clause name
@@ -418,47 +402,42 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* @return int
*/
public function count()
{
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]'
))->fetchSingle();
return (int) $this->query([
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') [data]',
])->fetchSingle();
}
/**
* @return DibiResult
* @return Result
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array(array($res, array_shift($setup)), $setup);
call_user_func_array([$res, array_shift($setup)], $setup);
}
return $res;
}
/********************* exporting ****************d*g**/
/**
* @return DibiDataSource
* @return DataSource
*/
public function toDataSource()
{
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
return new DataSource($this->connection->translate($this->_export()), $this->connection);
}
/**
* Returns SQL query.
* @return string
@@ -467,29 +446,32 @@ class DibiFluent extends DibiObject implements IDataSource
{
try {
return $this->connection->translate($this->_export());
} catch (Exception $e) {
} catch (\Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
/**
* Generates parameters for DibiTranslator.
* Generates parameters for Translator.
* @param string clause name
* @return array
*/
protected function _export($clause = NULL, $args = array())
protected function _export($clause = NULL, $args = [])
{
if ($clause === NULL) {
$data = $this->clauses;
if ($this->command === 'SELECT' && ($data['LIMIT'] || $data['OFFSET'])) {
$args = array_merge(['%lmt %ofs', $data['LIMIT'][0], $data['OFFSET'][0]], $args);
unset($data['LIMIT'], $data['OFFSET']);
}
} else {
$clause = self::$normalizer->$clause;
if (array_key_exists($clause, $this->clauses)) {
$data = array($clause => $this->clauses[$clause]);
$data = [$clause => $this->clauses[$clause]];
} else {
return array();
return [];
}
}
@@ -499,7 +481,9 @@ class DibiFluent extends DibiObject implements IDataSource
if ($clause === $this->command && $this->flags) {
$args[] = implode(' ', array_keys($this->flags));
}
foreach ($statement as $arg) $args[] = $arg;
foreach ($statement as $arg) {
$args[] = $arg;
}
}
}
@@ -507,7 +491,6 @@ class DibiFluent extends DibiObject implements IDataSource
}
/**
* Format camelCase clause name to UPPER CASE.
* @param string
@@ -524,7 +507,6 @@ class DibiFluent extends DibiObject implements IDataSource
}
public function __clone()
{
// remove references

View File

@@ -2,44 +2,32 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Lazy cached storage.
*
* @author David Grudl
* @package dibi
* @internal
*/
abstract class DibiHashMapBase
abstract class HashMapBase
{
private $callback;
public function __construct($callback)
public function __construct(callable $callback)
{
$this->setCallback($callback);
}
public function setCallback($callback)
{
if (!is_callable($callback)) {
$able = is_callable($callback, TRUE, $textual);
throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
}
$this->callback = $callback;
}
public function setCallback(callable $callback)
{
$this->callback = $callback;
}
public function getCallback()
{
@@ -49,14 +37,12 @@ abstract class DibiHashMapBase
}
/**
* Lazy cached storage.
*
* @author David Grudl
* @internal
*/
final class DibiHashMap extends DibiHashMapBase
final class HashMap extends HashMapBase
{
public function __set($nm, $val)
@@ -68,7 +54,6 @@ final class DibiHashMap extends DibiHashMapBase
}
public function __get($nm)
{
if ($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' => Type::TEXT,
'YEAR|BYTE|COUNTER|SERIAL|INT|LONG|SHORT|^TINY$' => Type::INTEGER,
'CURRENCY|REAL|MONEY|FLOAT|DOUBLE|DECIMAL|NUMERIC|NUMBER' => Type::FLOAT,
'^TIME$' => Type::TIME,
'TIME' => Type::DATETIME, // DATETIME, TIMESTAMP
'DATE' => Type::DATE,
'BOOL' => Type::BOOL,
];
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return NULL;
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new HashMap([__CLASS__, 'detectType']);
}
return self::$types;
}
/**
* Apply configuration alias or default values.
* @param array connect configuration
* @param string key
* @param string alias key
* @return void
*/
public static function alias(& $config, $key, $alias)
{
$foo = & $config;
foreach (explode('|', $key) as $key) {
$foo = & $foo[$key];
}
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
}
}
/**
* Import SQL dump from file.
* @return int count of sql commands
*/
public static function loadFromFile(Connection $connection, $file)
{
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new \RuntimeException("Cannot open file '$file'.");
}
$count = 0;
$delimiter = ';';
$sql = '';
$driver = $connection->getDriver();
while (!feof($handle)) {
$s = rtrim(fgets($handle));
if (substr($s, 0, 10) === 'DELIMITER ') {
$delimiter = substr($s, 10);
} elseif (substr($s, -strlen($delimiter)) === $delimiter) {
$sql .= substr($s, 0, -strlen($delimiter));
$driver->query($sql);
$sql = '';
$count++;
} else {
$sql .= $s . "\n";
}
}
if (trim($sql) !== '') {
$driver->query($sql);
$count++;
}
fclose($handle);
return $count;
}
}

View File

@@ -2,22 +2,19 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* SQL literal value.
*
* @author David Grudl
*/
class DibiLiteral extends DibiObject
class Literal
{
use Strict;
/** @var string */
private $value;
@@ -28,7 +25,6 @@ class DibiLiteral extends DibiObject
}
/**
* @return string
*/

View File

@@ -2,23 +2,21 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi\Loggers;
use Dibi;
/**
* dibi file logger.
*
* @author David Grudl
* @package dibi
*/
class DibiFileLogger extends DibiObject
class FileLogger
{
use Dibi\Strict;
/** @var string Name of the file where SQL errors should be logged */
public $file;
@@ -26,30 +24,30 @@ class DibiFileLogger extends DibiObject
public $filter;
public function __construct($file, $filter = NULL)
{
$this->file = $file;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->filter = $filter ? (int) $filter : Dibi\Event::QUERY;
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
public function logEvent(Dibi\Event $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$handle = fopen($this->file, 'a');
if (!$handle) return; // or throw exception?
if (!$handle) {
return; // or throw exception?
}
flock($handle, LOCK_EX);
if ($event->result instanceof Exception) {
if ($event->result instanceof \Exception) {
$message = $event->result->getMessage();
if ($code = $event->result->getCode()) {
$message = "[$code] $message";
@@ -63,9 +61,9 @@ class DibiFileLogger extends DibiObject
);
} else {
fwrite($handle,
"OK: " . $event->sql
'OK: ' . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f', $event->time * 1000) . ' ms'
. "\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')

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

View File

@@ -2,13 +2,10 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
@@ -21,46 +18,42 @@
* $value = $result->fetchSingle();
* $table = $result->fetchAll();
* $pairs = $result->fetchPairs();
* $assoc = $result->fetchAssoc('id');
* $assoc = $result->fetchAssoc('active,#,id');
* $assoc = $result->fetchAssoc('col1');
* $assoc = $result->fetchAssoc('col1[]col2->col3');
*
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*
* @property-read mixed $resource
* @property-read IDibiResultDriver $driver
* @property-read int $rowCount
* @property-read DibiResultIterator $iterator
* @property string $rowClass
* @property-read DibiResultInfo $info
*/
class DibiResult extends DibiObject implements IDataSource
class Result implements IDataSource
{
/** @var array IDibiResultDriver */
use Strict;
/** @var array ResultDriver */
private $driver;
/** @var array Translate table */
private $types = array();
private $types = [];
/** @var DibiResultInfo */
/** @var Reflection\Result */
private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE;
/** @var string returned object class */
private $rowClass = 'DibiRow';
private $rowClass = 'Dibi\Row';
/** @var callable returned object factory*/
private $rowFactory;
/** @var array format */
private $formats = array();
private $formats = [];
/**
* @param IDibiResultDriver
* @param ResultDriver
*/
public function __construct($driver)
{
@@ -69,17 +62,16 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* @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
@@ -93,32 +85,29 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Safe access to property $driver.
* @return IDibiResultDriver
* @throws RuntimeException
* @return ResultDriver
* @throws \RuntimeException
*/
final public function getResultDriver()
{
if ($this->driver === NULL) {
throw new RuntimeException('Result-set was released from memory.');
throw new \RuntimeException('Result-set was released from memory.');
}
return $this->driver;
}
/********************* rows ****************d*g**/
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @return bool TRUE on success, FALSE if unable to seek to specified record
* @throws Exception
*/
final public function seek($row)
{
@@ -126,7 +115,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Required by the Countable interface.
* @return int
@@ -137,7 +125,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Returns the number of rows in a result set.
* @return int
@@ -148,41 +135,23 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Returns the number of rows in a result set. Alias for getRowCount().
* @deprecated
*/
final public function rowCount()
{
trigger_error(__METHOD__ . '() is deprecated; use count($res) or $res->getRowCount() instead.', E_USER_WARNING);
return $this->getResultDriver()->getRowCount();
}
/**
* Required by the IteratorAggregate interface.
* @return DibiResultIterator
* @return ResultIterator
*/
final public function getIterator()
{
if (func_num_args()) {
trigger_error(__METHOD__ . ' arguments $offset & $limit have been dropped; use SQL clauses instead.', E_USER_WARNING);
}
return new DibiResultIterator($this);
return new ResultIterator($this);
}
/********************* fetching rows ****************d*g**/
/**
* Set fetched object class. This class should extend the DibiRow class.
* Set fetched object class. This class should extend the Row class.
* @param string
* @return DibiResult provides a fluent interface
* @return self
*/
public function setRowClass($class)
{
@@ -191,7 +160,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Returns fetched object class name.
* @return string
@@ -202,11 +170,21 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* 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 DibiRow|FALSE array on success, FALSE if no next record
* @return Row|FALSE array on success, FALSE if no next record
*/
final public function fetch()
{
@@ -216,17 +194,18 @@ class DibiResult extends DibiObject implements IDataSource
}
$this->fetched = TRUE;
$this->normalize($row);
if ($this->rowClass) {
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
* @return mixed value on success, FALSE if no next record
*/
final public function fetchSingle()
{
@@ -240,23 +219,26 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Fetches all records from table.
* @param int offset
* @param int limit
* @return array of DibiRow
* @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 array(); // empty result set
if (!$row) {
return []; // empty result set
}
$data = array();
$data = [];
do {
if ($limit === 0) break;
if ($limit === 0) {
break;
}
$limit--;
$data[] = $row;
} while ($row = $this->fetch());
@@ -265,7 +247,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Fetches all records from table and returns associative tree.
* Examples:
@@ -274,8 +255,8 @@ class DibiResult extends DibiObject implements IDataSource
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return DibiRow
* @throws InvalidArgumentException
* @return array
* @throws \InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
@@ -285,7 +266,9 @@ class DibiResult extends DibiObject implements IDataSource
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty result set
if (!$row) {
return []; // empty result set
}
$data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
@@ -294,7 +277,7 @@ class DibiResult extends DibiObject implements IDataSource
foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) {
throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
throw new \InvalidArgumentException("Unknown column '$as' in associative descriptor.");
}
}
@@ -316,16 +299,16 @@ class DibiResult extends DibiObject implements IDataSource
$x = & $x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i+1]};
$x = $row->{$assoc[$i + 1]};
continue 2;
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i+1]};
$x = & $x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i+1]};
$x = & $x->{$assoc[$i + 1]};
}
} elseif ($as !== '|') { // associative-array node
@@ -344,7 +327,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* @deprecated
*/
@@ -352,7 +334,9 @@ class DibiResult extends DibiObject implements IDataSource
{
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty result set
if (!$row) {
return []; // empty result set
}
$data = NULL;
$assoc = explode(',', $assoc);
@@ -381,22 +365,21 @@ class DibiResult extends DibiObject implements IDataSource
} elseif ($as === '=') { // "record" node
if ($x === NULL) {
$x = $row->toArray();
$x = & $x[ $assoc[$i+1] ];
$x = & $x[ $assoc[$i + 1] ];
$x = NULL; // prepare child node
} else {
$x = & $x[ $assoc[$i+1] ];
$x = & $x[ $assoc[$i + 1] ];
}
} elseif ($as === '@') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i+1]};
$x = & $x->{$assoc[$i + 1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i+1]};
$x = & $x->{$assoc[$i + 1]};
}
} else { // associative-array node
$x = & $x[$row->$as];
}
@@ -417,25 +400,26 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Fetches all records from table like $key => $value pairs.
* @param string associative key
* @param string value
* @return array
* @throws InvalidArgumentException
* @throws \InvalidArgumentException
*/
final public function fetchPairs($key = NULL, $value = NULL)
{
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty result set
if (!$row) {
return []; // empty result set
}
$data = array();
$data = [];
if ($value === NULL) {
if ($key !== NULL) {
throw new InvalidArgumentException("Either none or both columns must be specified.");
throw new \InvalidArgumentException('Either none or both columns must be specified.');
}
// autodetect
@@ -452,7 +436,7 @@ class DibiResult extends DibiObject implements IDataSource
} else {
if (!property_exists($row, $value)) {
throw new InvalidArgumentException("Unknown value column '$value'.");
throw new \InvalidArgumentException("Unknown value column '$value'.");
}
if ($key === NULL) { // indexed-array
@@ -463,39 +447,37 @@ class DibiResult extends DibiObject implements IDataSource
}
if (!property_exists($row, $key)) {
throw new InvalidArgumentException("Unknown key column '$key'.");
throw new \InvalidArgumentException("Unknown key column '$key'.");
}
}
do {
$data[ $row[$key] ] = $row[$value];
$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 = DibiColumnInfo::getTypeCache();
$cache = Helpers::getTypeCache();
try {
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = $cache->{$col['nativetype']};
$this->types[$col['name']] = isset($col['type']) ? $col['type'] : $cache->{$col['nativetype']};
}
} catch (DibiNotSupportedException $e) {}
} catch (NotSupportedException $e) {
}
}
/**
* Converts values to specified type and format.
* @param array
@@ -508,47 +490,55 @@ class DibiResult extends DibiObject implements IDataSource
continue;
}
$value = $row[$key];
if ($value === FALSE || $type === dibi::TEXT) {
if ($type === Type::TEXT) {
$row[$key] = (string) $value;
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
} elseif ($type === Type::INTEGER) {
$row[$key] = is_float($tmp = $value * 1)
? (is_string($value) ? $value : (int) $value)
: $tmp;
} elseif ($type === dibi::FLOAT) {
$row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value;
} 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 === dibi::BOOL) {
} elseif ($type === Type::BOOL) {
$row[$key] = ((bool) $value) && $value !== 'f' && $value !== 'F';
} elseif ($type === dibi::DATE || $type === dibi::DATETIME) {
if ((int) $value === 0) { // '', NULL, FALSE, '0000-00-00', ...
} elseif (empty($this->formats[$type])) { // return DateTime object (default)
$row[$key] = new DibiDateTime(is_numeric($value) ? date('Y-m-d H:i:s', $value) : $value);
} elseif ($this->formats[$type] === 'U') { // return timestamp
$row[$key] = is_numeric($value) ? (int) $value : strtotime($value);
} elseif (is_numeric($value)) { // formatted date
$row[$key] = date($this->formats[$type], $value);
} 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 {
$value = new DibiDateTime($value);
$row[$key] = $value->format($this->formats[$type]);
$row[$key] = NULL;
}
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
} elseif ($type === Type::TIME_INTERVAL) {
preg_match('#^(-?)(\d+)\D(\d+)\D(\d+)\z#', $value, $m);
$row[$key] = new \DateInterval("PT$m[2]H$m[3]M$m[4]S");
$row[$key]->invert = (int) (bool) $m[1];
} elseif ($type === Type::BINARY) {
$row[$key] = $this->getResultDriver()->unescapeBinary($value);
}
}
}
/**
* Define column type.
* @param string column
* @param string type (use constant Dibi::*)
* @return DibiResult provides a fluent interface
* @param string type (use constant Type::*)
* @return self
*/
final public function setType($col, $type)
{
@@ -557,7 +547,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Returns column type.
* @return string
@@ -568,12 +557,11 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Sets data format.
* @param string type (use constant Dibi::*)
* @param string type (use constant Type::*)
* @param string format
* @return DibiResult provides a fluent interface
* @return self
*/
final public function setFormat($type, $format)
{
@@ -582,7 +570,6 @@ class DibiResult extends DibiObject implements IDataSource
}
/**
* Returns data format.
* @return string
@@ -593,27 +580,24 @@ class DibiResult extends DibiObject implements IDataSource
}
/********************* meta info ****************d*g**/
/**
* Returns a meta information about the current result set.
* @return DibiResultInfo
* @return Reflection\Result
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new DibiResultInfo($this->getResultDriver());
$this->meta = new Reflection\Result($this->getResultDriver());
}
return $this->meta;
}
/**
* @deprecated
* @return Reflection\Column[]
*/
final public function getColumns()
{
@@ -621,53 +605,16 @@ class DibiResult extends DibiObject implements IDataSource
}
/** @deprecated */
public function getColumnNames($fullNames = FALSE)
{
trigger_error(__METHOD__ . '() is deprecated; use $res->getInfo()->getColumnNames() instead.', E_USER_WARNING);
return $this->getInfo()->getColumnNames($fullNames);
}
/********************* misc tools ****************d*g**/
/**
* Displays complete result set as HTML table for debug purposes.
* Displays complete result set as HTML or text table for debug purposes.
* @return void
*/
final public function dump()
{
$i = 0;
$this->seek(0);
while ($row = $this->fetch()) {
if ($i === 0) {
echo "\n<table class=\"dump\">\n<thead>\n\t<tr>\n\t\t<th>#row</th>\n";
foreach ($row as $col => $foo) {
echo "\t\t<th>" . htmlSpecialChars($col) . "</th>\n";
}
echo "\t</tr>\n</thead>\n<tbody>\n";
}
echo "\t<tr>\n\t\t<th>", $i, "</th>\n";
foreach ($row as $col) {
//if (is_object($col)) $col = $col->__toString();
echo "\t\t<td>", htmlSpecialChars($col), "</td>\n";
}
echo "\t</tr>\n";
$i++;
}
if ($i === 0) {
echo '<p><em>empty result set</em></p>';
} else {
echo "</tbody>\n</table>\n";
}
echo Helpers::dump($this);
}
}

View File

@@ -2,19 +2,16 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* External result set iterator.
*
* This can be returned by DibiResult::getIterator() method or using foreach
* This can be returned by Result::getIterator() method or using foreach
* <code>
* $result = dibi::query('SELECT * FROM table');
* foreach ($result as $row) {
@@ -22,13 +19,12 @@
* }
* unset($result);
* </code>
*
* @author David Grudl
* @package dibi
*/
class DibiResultIterator implements Iterator, Countable
class ResultIterator implements \Iterator, \Countable
{
/** @var DibiResult */
use Strict;
/** @var Result */
private $result;
/** @var int */
@@ -39,15 +35,14 @@ class DibiResultIterator implements Iterator, Countable
/**
* @param DibiResult
* @param Result
*/
public function __construct(DibiResult $result)
public function __construct(Result $result)
{
$this->result = $result;
}
/**
* Rewinds the iterator to the first element.
* @return void
@@ -60,7 +55,6 @@ class DibiResultIterator implements Iterator, Countable
}
/**
* Returns the key of the current element.
* @return mixed
@@ -71,7 +65,6 @@ class DibiResultIterator implements Iterator, Countable
}
/**
* Returns the current element.
* @return mixed
@@ -82,7 +75,6 @@ class DibiResultIterator implements Iterator, Countable
}
/**
* Moves forward to next element.
* @return void
@@ -94,7 +86,6 @@ class DibiResultIterator implements Iterator, Countable
}
/**
* Checks if there is a current element after calls to rewind() or next().
* @return bool
@@ -105,7 +96,6 @@ class DibiResultIterator implements Iterator, Countable
}
/**
* Required by the Countable interface.
* @return int

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

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

@@ -0,0 +1,608 @@
<?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->identifiers->$v);
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]};
}
}
return implode(', ', $vx);
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->identifiers->{$pair[0]} . '='
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return implode(', ', $vx);
case 'in':// replaces scalar %in modifier!
case 'l': // (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . (($vx || $modifier === 'l') ? implode(', ', $vx) : 'NULL') . ')';
case 'v': // (key, key, ...) VALUES (val, val, ...)
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->identifiers->{$pair[0]};
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
}
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
foreach ($value as $k => $v) {
if (is_array($v)) {
if (isset($proto)) {
if ($proto !== array_keys($v)) {
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': // identifier name
return $this->identifiers->$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.1',
REVISION = 'released on 2015-12-16';
/** 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);
}
}

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

@@ -0,0 +1,159 @@
<?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
{
use Strict;
/** @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
{
use Strict;
public function __construct($message = '%msg.')
{
static $messages = [
PREG_INTERNAL_ERROR => 'Internal error',
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
5 => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point', // PREG_BAD_UTF8_OFFSET_ERROR
];
$code = preg_last_error();
parent::__construct(str_replace('%msg', isset($messages[$code]) ? $messages[$code] : 'Unknown error', $message), $code);
}
}
class NotImplementedException extends Exception
{
}
class NotSupportedException extends Exception
{
}
/**
* Database procedure exception.
*/
class ProcedureException extends Exception
{
/** @var string */
protected $severity;
/**
* Construct the exception.
* @param string Message describing the exception
* @param int Some code
* @param string SQL command
*/
public function __construct($message = NULL, $code = 0, $severity = NULL, $sql = NULL)
{
parent::__construct($message, (int) $code, $sql);
$this->severity = $severity;
}
/**
* Gets the exception severity.
* @return string
*/
public function getSeverity()
{
$this->severity;
}
}
/**
* Base class for all constraint violation related exceptions.
*/
class ConstraintViolationException extends DriverException
{
}
/**
* Exception for a foreign key constraint violation.
*/
class ForeignKeyConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a NOT NULL constraint violation.
*/
class NotNullConstraintViolationException extends ConstraintViolationException
{
}
/**
* Exception for a unique constraint violation.
*/
class UniqueConstraintViolationException extends ConstraintViolationException
{
}

View File

@@ -2,54 +2,48 @@
/**
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
* Copyright (c) 2005 David Grudl (https://davidgrudl.com)
*/
namespace Dibi;
/**
* Provides an interface between a dataset and data-aware components.
* @package dibi
*/
interface IDataSource extends Countable, IteratorAggregate
interface IDataSource extends \Countable, \IteratorAggregate
{
//function IteratorAggregate::getIterator();
//function Countable::count();
//function \IteratorAggregate::getIterator();
//function \Countable::count();
}
/**
* dibi driver interface.
* @package dibi
*/
interface IDibiDriver
interface Driver
{
/**
* Connects to a database.
* @param array
* @return void
* @throws DibiException
* @throws Exception
*/
function connect(array &$config);
function connect(array & $config);
/**
* Disconnects from a database.
* @return void
* @throws DibiException
* @throws Exception
*/
function disconnect();
/**
* Internal: Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
* @return ResultDriver|NULL
* @throws DriverException
*/
function query($sql);
@@ -69,7 +63,7 @@ interface IDibiDriver
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function begin($savepoint = NULL);
@@ -77,7 +71,7 @@ interface IDibiDriver
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function commit($savepoint = NULL);
@@ -85,7 +79,7 @@ interface IDibiDriver
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
* @throws DriverException
*/
function rollback($savepoint = NULL);
@@ -97,18 +91,26 @@ interface IDibiDriver
/**
* Returns the connection reflector.
* @return IDibiReflector
* @return Reflector
*/
function getReflector();
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @param mixed value
* @return string encoded value
* @throws InvalidArgumentException
*/
function escape($value, $type);
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.
@@ -120,24 +122,17 @@ interface IDibiDriver
/**
* Injects LIMIT/OFFSET to the SQL query.
* @param string &$sql The SQL query that will be modified.
* @param int $limit
* @param int $offset
* @return void
*/
function applyLimit(&$sql, $limit, $offset);
function applyLimit(& $sql, $limit, $offset);
}
/**
* dibi result set driver interface.
* @package dibi
*/
interface IDibiResultDriver
interface ResultDriver
{
/**
@@ -150,7 +145,7 @@ interface IDibiResultDriver
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
* @throws Exception
*/
function seek($row);
@@ -183,26 +178,18 @@ interface IDibiResultDriver
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* @param string
* @return string
*/
function unescape($value, $type);
function unescapeBinary($value);
}
/**
* dibi driver reflection.
*
* @author David Grudl
* @package dibi
*/
interface IDibiReflector
interface Reflector
{
/**

140
src/loader.php Normal file
View File

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

4
tests/.gitignore vendored Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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