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

Compare commits

...

235 Commits
v1.1 ... v2.0.0

Author SHA1 Message Date
David Grudl
7c47f57e30 released 2.0 stable 2012-02-03 13:48:14 +01:00
David Grudl
0327573085 updated Nette Debugger 2012-02-03 13:48:14 +01:00
David Grudl
cf2e08c20f DibiNettePanel: fixed doubled alias 2012-02-01 21:50:35 +01:00
David Grudl
9b7a80e894 DibiConnection ensures class dibi is loaded 2012-02-01 19:18:24 +01:00
Miloslav Hůla
2ae6535899 Consider value of %n array as identifier 2012-02-01 19:18:23 +01:00
Miloslav Hůla
0b2cb3c6e4 Detection of PostgreSQL array types 2012-01-21 16:58:57 +01:00
David Grudl
0723e8ffc8 typos 2012-01-21 16:58:57 +01:00
David Grudl
5b68657842 DibiResult: row-class can be disabled and fetch() returns array 2012-01-21 16:58:57 +01:00
David Grudl
3cde2c7815 DibiTranslator: supported key%~like~ modifier in array keys 2012-01-21 16:58:57 +01:00
David Grudl
5aed9e7e4f MSSQL2005: used native functions for transaction 2012-01-21 16:58:56 +01:00
David Grudl
c2c63615fd DibiFluent: added setupResult() 2012-01-21 16:58:56 +01:00
David Grudl
e4ed284e77 DibiConnection: added config option "result|formatDate" 2012-01-21 16:58:56 +01:00
David Grudl
0f21b6ca5b DibiEvent: (SELECT ...) UNION (SELECT ...) is classified as SELECT [Closes #35] 2012-01-21 16:58:56 +01:00
David Grudl
b87d3cab81 DibiConnection::test() shows complete error message 2012-01-21 16:58:55 +01:00
David Grudl
3f2e383b47 __toString() do not throw an exception [Closes #19] 2012-01-21 16:58:55 +01:00
David Grudl
bf49cec002 DibiNettePanel: Oracle explains using EXPLAIN PLAN 2012-01-21 16:58:55 +01:00
David Grudl
70fd2368aa DibiNettePanel: explain SQL command can be altered 2012-01-21 16:57:33 +01:00
David Grudl
5d41128752 calling getResultResource() disables auto-free in __destruct() 2012-01-19 04:23:00 +01:00
David Grudl
5efb11d780 DibiMysqlDriver & DibiPostgreSql: escape() checks if resource is alive [Closes #46] 2012-01-19 04:21:09 +01:00
David Grudl
660a9db522 drivers: getResource() and getResultResource() checks if resource is alive 2012-01-19 04:20:57 +01:00
David Grudl
c7b9a91fc2 DibiResult: added getResultDriver() 2012-01-19 00:14:07 +01:00
David Grudl
ea0d6d02ba DibiResult: added setFormat() 2012-01-18 21:15:22 +01:00
David Grudl
b964887f9d released 1.5-rc2 2012-01-12 15:25:19 +01:00
David Grudl
a8f5e09b69 DibiDateTime: added __toString(), modify(), modifyClone() 2012-01-12 15:25:04 +01:00
David Grudl
8c481bc128 DibiRow: asBool(), asDate() and asTimestamp() deprecated 2012-01-12 01:19:15 +01:00
David Grudl
4c85d1d55c DibiNettePanel: works with disabled bar [Closes #43] 2012-01-12 01:06:15 +01:00
David Grudl
2a5934c385 removed IDibiVariable & DibiVariable, replaced with DibiLiteral 2012-01-12 00:46:50 +01:00
David Grudl
a8672bb6ff DibiFluent passed as argument to DibiFluent is converted to string [Closes #44] 2012-01-12 00:46:49 +01:00
Steven Bredenberg
9bc53109a0 Adding reflector code for MSSQL. 2012-01-12 00:16:22 +01:00
Radek Dostál
3288b38b6c Implemented escapeLike() for Oracle driver 2012-01-12 00:06:39 +01:00
David Grudl
8c8b3c3dc1 dibi: new keywords in dump() 2012-01-12 00:03:33 +01:00
David Grudl
2c1950eacf DibiDatabaseInfo::detectTypes: adjusted YEAR, BIGINT 2012-01-12 00:03:33 +01:00
David Grudl
baf08d3c71 DibiConnection: types detection + normalization is always TRUE 2012-01-12 00:03:32 +01:00
David Grudl
e60dc2d2d0 Merge pull request #48 from mil0/master
Implemented getForeignKeys() for PostgreSQL driver
2012-01-11 13:31:15 -08:00
David Grudl
eee105f607 class DibiNettePanel is included in dibi.php [Closes #49] 2012-01-11 00:18:37 +01:00
Miloslav Hůla
c65294075e Merge remote-tracking branch 'upstream/master' 2012-01-08 20:03:30 +01:00
David Grudl
1d63af75f9 Nette: getContainer() renamed to getContainerBuilder() 2012-01-06 04:02:43 +01:00
David Grudl
9be3bd7a53 fix: DibiNettePanel is called only if required interface is available 2012-01-04 18:50:42 +01:00
Miloslav Hůla
96d7236a4a Implemented getForeignKeys() for PostgreSQL driver 2012-01-03 13:57:37 +01:00
David Grudl
9ba0cf62d1 All exceptions are DibiException descendants 2012-01-03 05:46:59 +01:00
David Grudl
d1a63ce757 FireBird driver: implemented getResultColumns() 2012-01-03 05:34:56 +01:00
David Grudl
0a17cc50ae updated Nette\Debugger 2012-01-03 05:15:44 +01:00
David Grudl
74a139974c DibiTranslator: fixed usage of modifier ? 2012-01-03 05:14:44 +01:00
David Grudl
dbb72b769b updated phpDoc @package 2012-01-03 04:50:11 +01:00
David Grudl
dd07d50434 added DibiNetteExtension 2012-01-03 04:34:01 +01:00
David Grudl
53c2ef55d8 changed profiler API; IDibiProfiler replaced with DibiConnection::$onEvent; DibiProfiler split to DibiFileLogger, DibiFirePhpLogger and DibiNettePanel (BC break!) 2012-01-03 04:34:01 +01:00
David Grudl
1d4e86fe2b Updated copyright notices for 2012 2012-01-03 04:17:30 +01:00
David Grudl
183a215fc5 updated DibiProfiler CSS 2011-07-01 08:19:16 +02:00
David Grudl
9c59fee929 updated examples & Debugger 2011-07-01 08:19:15 +02:00
David Grudl
ac1f45b397 updated for Nette 2.0 beta: exceptions 2011-07-01 08:19:15 +02:00
David Grudl
508e8638e8 Merge pull request #34 from mil0/master.
implemented escapeLike() for PostgreSQL driver
2011-05-02 15:35:20 -07:00
David Grudl
0de947883b updated for Nette 2.0 beta 2011-04-21 14:53:22 +02:00
Miloslav Hůla
b979295baa implemented escapeLike() for PostgreSQL driver 2011-03-10 14:33:34 +01:00
David Grudl
8a899c7ddb DibiProfiler logs source files and lines 2011-02-23 02:16:07 +01:00
Lukáš Gavenda
1a4ea39a60 DibiProfiler: iterator fix 2011-02-23 02:12:55 +01:00
David Grudl
acda14ca64 DibiProfiler shows source files and lines 2011-02-17 22:19:06 +01:00
David Grudl
f09e4b9b3e updated Nette\Debug 2011-02-17 22:18:30 +01:00
David Grudl
1103714b9f typos 2011-02-16 19:38:05 +01:00
David Grudl
e37af6f99d added IDibiVariable 2011-02-16 18:01:22 +01:00
David Grudl
b148291ca2 DibiDataSource: removed keyword 'AS' 2011-02-16 17:48:47 +01:00
David Grudl
713918ae3c typos 2011-02-06 15:48:44 +01:00
David Grudl
617cb42e8e fixed PHP bug #53915 2011-02-03 04:13:01 +01:00
David Grudl
4ea5ddae8e DibiResult::convert() correctly handles too big INT [Closes #18] 2011-02-02 22:49:52 +01:00
David Grudl
60b62c50fe MySQLi driver: added support for persistent connection [Closes #27] 2011-02-02 22:39:52 +01:00
David Grudl
568ec80994 DibiProfiler: EXPLAIN is executed at shutdown [Closes #17] 2011-02-02 22:34:13 +01:00
David Grudl
23e3ba6db4 PDO driver: getReflector() is implemented for MySQL and SQLite 2011-02-02 20:36:54 +01:00
David Grudl
bdb5b217c7 License changed to the New BSD License or the GNU General Public License (GPL) version 2 or 3. 2011-02-02 01:20:30 +01:00
David Grudl
faf444567b released 1.5-rc1 2011-01-25 18:26:51 +01:00
David Grudl
018b9da06d used NotSupportedException at appropriate places 2011-01-25 18:23:30 +01:00
David Grudl
9c52b8ea9d renamed DibiLazyStorage to DibiHashMap 2011-01-25 18:00:29 +01:00
David Grudl
1ada6fab97 removed DibiConnection::sql() 2011-01-25 17:50:50 +01:00
David Grudl
0dc9db1d77 some stuff is deprecated and throws E_USER_WARNING: dibi::datetime(), dibi::date(), dibi::addSubst(), dibi::removeSubst(), dibi::setSubstFallback(), DibiResult::rowCount(), DibiResult::getColumnNames(), DibiVariable 2011-01-25 17:49:37 +01:00
David Grudl
08099816d5 DibiDatabaseInfo: removed support for substitutions 2011-01-25 17:42:13 +01:00
David Grudl
26de1aebc0 Substitution moved from class dibi to DibiConnection 2011-01-25 17:41:44 +01:00
David Grudl
88cccc0543 DibiConnection refactoring 2011-01-25 17:37:49 +01:00
David Grudl
0b1547818d DibiDateTime::__construct accepts unix timestamp 2011-01-25 17:37:35 +01:00
David Grudl
e0d110962e MSSQL2005 driver: added alias charset -> options|CharacterSet 2011-01-25 17:37:35 +01:00
David Grudl
ae00fed718 fixed regexp for profiler 2011-01-24 15:20:50 +01:00
Filip Procházka
af715a8044 fixed sql injection vulnerability through conditions and comments [closes #30] 2011-01-24 22:01:51 +08:00
David Grudl
af6352d0af removed czech license 2010-11-10 00:55:22 +01:00
David Grudl
14fee47d54 DibiProfiler: added HTML title for Debug bar 2010-11-07 23:06:21 +01:00
David Grudl
ec82eda864 DibiResult destructor moved to individual IDibiResultDriver drivers 2010-11-02 14:42:15 +01:00
David Grudl
087734fb23 drivers implementing IDibiDriver and IDibiResultDriver together do not use $resultSet in IDibiDriver part 2010-11-02 14:42:14 +01:00
David Grudl
32baabdeac added low-level methods createResultDriver() and DibiConnection::createResultSet() 2010-11-02 13:59:05 +01:00
David Grudl
33ef22b488 fixed compatibility with Nette / RobotLoader 2010-11-02 03:29:19 +01:00
David Grudl
36fe9d42c1 PcreException renamed to DibiPcreException 2010-10-25 16:16:38 +02:00
David Grudl
0c4f343238 Firebird driver: added missing getReflector() 2010-10-06 16:17:29 +02:00
David Grudl
8d6639fa3c DateTime53 renamed to DibiDateTime 2010-10-06 16:16:09 +02:00
David Grudl
90592929ec fixed Nette compatibility [Closes #23] 2010-09-28 17:29:02 +02:00
David Grudl
c052582670 DibiProfiler: the file can be specified in a configuration 2010-09-15 13:25:46 +02:00
David Grudl
e6e7babe22 simplified phpDoc comments 2010-09-14 19:09:56 +02:00
David Grudl
056a680cff DibiProfiler & Debug: compatible with XHTML 2010-09-07 04:05:41 +02:00
David Grudl
6d94afdddd added dibi::setConnection() 2010-09-07 00:16:45 +02:00
David Grudl
b1156e54d8 drivers meta function refactoring 2010-08-27 02:18:07 +02:00
David Grudl
39add9b8a3 dibi::dump() fixed in CLI mode 2010-08-27 01:06:05 +02:00
David Grudl
b8e518f44b implemented escapeLike() and modifiers %~like, %like~, %~like~ 2010-08-27 01:00:53 +02:00
David Grudl
46a3b8a42c now compatible with Debug / NDebug / Nette\Debug 2010-08-25 07:21:49 +02:00
David Grudl
310d43f404 DibiTranslator: temporary removed delimite() cache [Closes #21] 2010-08-25 01:27:05 +02:00
David Grudl
a64c2270d8 Revert "DibiResult: removed destructor"
This reverts commit 6d353c0b5d.
2010-08-25 01:27:05 +02:00
David Grudl
16dd0c7230 dibi::$substs & fall-back provided via DibiLazyStorage 2010-08-25 01:27:04 +02:00
David Grudl
79735e96d1 DibiLazyStorage: allows empty string as property name for reading 2010-08-25 01:27:04 +02:00
David Grudl
325305326b missing substitutions no longer throw exception 2010-08-25 01:27:04 +02:00
David Grudl
ff3ca38504 typos 2010-08-25 01:27:04 +02:00
David Grudl
f651d9f473 Revert "empty substitutions are going to be deprecated" commit e50b1a0b5a. 2010-08-25 01:27:04 +02:00
David Grudl
3568e041c5 MSSQL: delimited name in mssql_select_db 2010-08-25 01:27:04 +02:00
David Grudl
6430573d61 MySQL & MySQLi, SQLite & SQLite3 reflectors moved do external classes DibiMySqlReflector and DibiSqliteReflector 2010-08-25 01:27:04 +02:00
David Grudl
65b5e2eecd added IDibiDriver::getReflector() 2010-08-05 21:03:08 +02:00
David Grudl
9f982cb310 MySQL, MySQLi & PostgreSql drivers: default character set is 'utf8' (BC break) 2010-08-04 15:36:10 +02:00
David Grudl
76783b3872 DibiDatabaseInfo: better IDibiReflector vs. IDibiResultDriver handling 2010-08-04 15:34:49 +02:00
David Grudl
de85d3814e IDibiDriver splitted into IDibiDriver & IDibiResultDriver 2010-08-04 15:34:42 +02:00
David Grudl
5cce595518 DibiConnection::sql() renamed to verb translate() 2010-08-04 12:10:29 +02:00
David Grudl
746a553419 IDibiDriver::getColumnsMeta() renamed to getResultColumns() (BC break!) 2010-08-03 23:33:12 +02:00
David Grudl
0d7b9c32c9 updated phpDoc 2010-08-03 23:27:53 +02:00
David Grudl
9a4f1e6e36 DibiFluent: allowed multiple ->from('table') 2010-08-03 21:54:01 +02:00
David Grudl
204c4cdbd7 DibiConnection: fixed processing of profiler configuration 2010-08-03 21:29:12 +02:00
David Grudl
6b166afffb SQLite & SQLite3: improved primary key detection for ROWID 2010-08-03 21:28:06 +02:00
David Grudl
f3c2c27818 examples: added new 2010-08-03 21:06:56 +02:00
David Grudl
dc62e87629 PostgreSQL: implemented inTransaction() 2010-08-03 20:20:37 +02:00
David Grudl
66e709e846 MySQL & MySQLi drivers: configuration items 'options' renamed to 'flags' (old name is alias); added array 'options' for MySQLi 2010-08-03 19:49:22 +02:00
David Grudl
dc3b1ff399 DibiConnection::getDriver() automatically connects lazy connection; connect() and disconnect() are imperative now! 2010-08-03 18:11:21 +02:00
David Grudl
6bb2bc489d DibiTranslator: removed getDriver() (it is private) 2010-08-03 18:11:21 +02:00
David Grudl
019f6864cf DibiResult::getIterator() - removed optional $offset and $limit parameters (BC break!) 2010-08-03 18:11:20 +02:00
David Grudl
792a25d57b added tests 2010-08-03 17:18:11 +02:00
David Grudl
759b8d4615 removed directory "icon" 2010-08-03 17:18:10 +02:00
David Grudl
3f594756e0 examples: removed unnecessary delimiters [] 2010-08-03 17:18:10 +02:00
David Grudl
beee4ee4b7 examples: files renamed 2010-08-03 17:18:10 +02:00
David Grudl
8f9435b518 examples: better headings and comments 2010-08-03 17:18:02 +02:00
David Grudl
65dba03652 examples: added CSS style 2010-08-03 13:41:06 +02:00
David Grudl
6e35a783c7 examples: data files moved to directory "data" 2010-08-03 13:41:05 +02:00
David Grudl
914f7d3c26 DibiLazyStorage: empty string as property name throws exception; fixed in DibiDatabaseInfo too 2010-08-03 13:41:05 +02:00
David Grudl
e50b1a0b5a empty substitutions are going to be deprecated 2010-08-03 08:20:50 +02:00
David Grudl
8f358ca73d dibi::dump() turns off HTML tags in CLI mode 2010-08-03 08:19:58 +02:00
David Grudl
9d27c7cc4e DibiFluent: speed optimizations 2010-08-03 08:19:57 +02:00
David Grudl
bcee7bba91 DibiFluent::__class() small refactoring 2010-08-03 08:19:57 +02:00
David Grudl
8c5dc153f2 DibiDatabaseInfo: uses DibiLazyStorage for column type detecting 2010-08-03 08:19:34 +02:00
David Grudl
c93137340e DibiTranslator: uses DibiLazyStorage as caching layer over delimite() 2010-08-03 08:19:00 +02:00
David Grudl
84e0f0ecc1 added DibiLazyStorage, simple caching layer 2010-08-03 08:18:42 +02:00
David Grudl
3b87d71a68 MySQLi: fixed columns types detection [Closes #6] 2010-08-03 04:04:59 +02:00
David Grudl
df02cf1e3d dibi::dump highlights keyword OFFSET [Closes #15] 2010-08-03 04:04:58 +02:00
PetrP
e8990c9be6 DibiProfiler: explain query doesn't overwrite dibi::$sql 2010-08-03 04:04:58 +02:00
PetrP
e8de6f21c9 Mysql & Mysqli drivers: fixed bug in detection unsigned columns 2010-08-03 04:04:58 +02:00
PetrP
8082489143 DibiConnection: connect() is public 2010-08-03 04:04:57 +02:00
David Grudl
63163de18b DibiDatabaseInfo: BIGINT is treated as string [Closes #18] 2010-08-03 04:04:53 +02:00
David Grudl
1b623855b7 typos 2010-08-03 04:04:47 +02:00
David Grudl
6288dc8cba DibiConnection: removed old deprecated methods 2010-08-03 01:35:23 +02:00
David Grudl
20d0163316 DibiTranslator: speed optimizations 2010-08-03 01:32:00 +02:00
David Grudl
97da612604 DibiConnection: driver name is case insensitive 2010-08-03 00:45:52 +02:00
David Grudl
2ed67c1944 DibiConnection: uses single DibiTranslator object (per-connection) 2010-08-03 00:44:59 +02:00
David Grudl
a0a12701e9 DibiPdoDriver: speed optimization 2010-08-03 00:42:50 +02:00
David Grudl
739994dac6 MySQLi driver: sets mysqli_report(MYSQLI_REPORT_OFF) 2010-07-21 00:15:16 +02:00
David Grudl
8c99f0c04d Driver's reflection capabilities moved to IDibiReflector 2010-05-26 16:26:21 +02:00
David Grudl
7bac2ef3b3 typos, changed nettephp.com -> nette.org 2010-05-25 02:48:51 +02:00
David Grudl
651c0f8c4a DibiProfiler is configurable via DibiConnection $config 2010-05-19 20:48:35 +02:00
David Grudl
88b1a45e42 DibiResult is configured via items 'detectTypes' and 'formatDateTime' in 'result' subarray; removed RESULT_DETECT_TYPES & RESULT_DATE_TIME 2010-05-19 20:25:17 +02:00
David Grudl
9d803869fa updated Nette\Debug 2010-05-19 19:42:04 +02:00
David Grudl
d19afd5790 Removed inTransaction definitely (BC break!) 2010-05-19 18:33:28 +02:00
David Grudl
8b4cd4e689 DibiTranslator: %f modifier converts value to number 2010-05-19 17:14:12 +02:00
David Grudl
286cd7bacd DibiFluent: implemented clause auto-switch for 'JOIN', 'INNER JOIN', 'LEFT JOIN' 2010-05-19 16:38:51 +02:00
David Grudl
1285d9b30a DibiMsSqlDriver: exception message received from mssql_get_last_message() 2010-05-19 15:54:49 +02:00
David Grudl
0748c693ff DibiMsSql2005Driver: added config aliases 'username', 'password', 'database' 2010-05-19 15:40:32 +02:00
David Grudl
550c477797 DibiConnection::alias() refactoring 2010-05-19 15:33:37 +02:00
David Grudl
27d58bff40 DibiDatabaseInfo: provides substitutions 2010-05-19 15:03:48 +02:00
David Grudl
bec559448c rewritten dibi::IDENTIFIER escaping; added support for [table.*] 2010-05-19 14:59:03 +02:00
David Grudl
27930611de REGEXP optimizations 2010-05-16 22:45:55 +02:00
David Grudl
553f7da5f9 implemented PCRE error checking and PcreException 2010-05-16 22:12:19 +02:00
David Grudl
555e825bb2 DibiResult: fixed compatibility with new DibiRow 2010-04-26 21:20:10 +02:00
David Grudl
5d95f0ba0d added DibiRow::toArray() & Countable 2010-04-26 21:18:46 +02:00
David Grudl
26384626ba ArrayObject -> Traversable & iterator_to_array 2010-04-26 20:43:04 +02:00
David Grudl
f048cc4086 added @method phpDoc 2010-04-23 14:55:29 +02:00
David Grudl
784153e98c DibiRow is not longer ArrayObject descendant 2010-04-22 23:19:33 +02:00
David Grudl
a4c5f327de DibiConnection: $config can be Traversable 2010-04-22 22:14:46 +02:00
David Grudl
90d61002fb added @property phpDoc 2010-04-22 12:12:11 +02:00
Roman Sklenar
ea5e1f052f SQLite driver: implemented database reflection 2010-04-22 17:41:28 +08:00
Roman Sklenar
7412f8bf65 DibiPdoDriver::getColumnsMeta PHP < 5.2.3 compatibility 2010-04-22 17:41:28 +08:00
Jakub Vrana
35b8a4f000 Fix MySQL implementation of inTransaction() 2010-04-22 17:41:04 +08:00
Ondřej Mirtes
e8517b43dc PostgreSQL driver now allows 'database' key in config array instead of 'dbname' 2010-04-22 17:32:36 +08:00
paranoiq
7504a51451 enabled 'unsigned' option on mysql and mysqli column info 2010-04-22 17:32:24 +08:00
David Grudl
550be3b10a Debug Bar fix 2010-04-06 12:58:54 +02:00
David Grudl
52e5d43416 DibiProfiler: added [explain] info 2010-04-04 05:58:57 +02:00
David Grudl
57b68131e6 DibiProfiler: $tickets is now static, profiler sets dibi static variables 2010-04-04 00:08:14 +02:00
David Grudl
52f8128a2f profiler fix 2010-04-03 23:55:16 +02:00
David Grudl
3f6a075251 updated for new Nette Debug Bar 2010-04-01 05:30:32 +02:00
David Grudl
22d41ca3c1 DibiProfiler: json_encode in native in PHP 5.2 2010-03-29 15:46:05 +02:00
David Grudl
0b9562497f typos 2010-02-24 07:50:50 +01:00
David Grudl
dbe79ae57b DibiResult: convert() is protected 2010-02-24 06:51:21 +01:00
David Grudl
92ea9f2c5a DibiResult: default type for datetime changed from timestamp -> object DateTime (BC break) 2010-02-24 06:49:05 +01:00
David Grudl
c01bfd792d added new option 'resultDateTime' - replaces parameter $format in DibiResult::setType() 2010-02-24 06:43:08 +01:00
David Grudl
527863fcce added new option 'resultDetectTypes' - calls automatically detectTypes() 2010-02-24 06:23:55 +01:00
David Grudl
4054bdc231 DibiResult: removed setWithTables & 'resultWithTables' configuration option (BC break!) 2010-02-24 04:59:00 +01:00
David Grudl
a2323271d4 DibiResult: small refactoring 2010-02-24 02:43:15 +01:00
David Grudl
576397319a DibiDataSource: better table name detection in __construct 2010-02-16 19:19:32 +01:00
David Grudl
ec1a1ec5b9 DibiRow::asDateTime() added $format parameter 2010-02-15 21:20:19 +01:00
David Grudl
55cd98e9c1 DibiFluent: fixed support for cloning 2010-01-26 19:55:00 +01:00
David Grudl
a8e83ce93d DibiRow: added asTimestamp() & asDateTime(), therefore asDate() is deprecated 2010-01-26 01:38:47 +01:00
David Grudl
e232bf470c DibiFluent: clause() argument can be in upper-case 2010-01-24 18:58:05 +01:00
David Grudl
65965b0943 DibiFluent: added support for cloning 2010-01-24 18:57:04 +01:00
David Grudl
e035c13437 DibiPostgreDriver: getTables() returns always array 2010-01-23 08:00:52 +01:00
David Grudl
b8600ee704 added DibiFluent::REMOVE const 2010-01-23 06:28:23 +01:00
David Grudl
2395b83b03 DibiTranslator: added modifiers %sN & %iN, replacements for %sn & %in 2010-01-23 06:02:03 +01:00
David Grudl
cb1ed71669 DibiPdoDriver: added rowCount() 2010-01-23 05:39:28 +01:00
David Grudl
fa0b146f67 DibiTranslator: empty arrays DO NOT generate NULL (BC break!) & added array modifier %in 2010-01-23 05:25:17 +01:00
David Grudl
bb40e28eb8 DibiTranslator: %by supports inner arrays 2010-01-23 04:57:49 +01:00
David Grudl
1ecc4ab134 DibiMySqlDriver & MySQLi: sets time_zone in connect() 2010-01-23 04:45:59 +01:00
David Grudl
76c02a65da DibiFluent: added removeClause() 2010-01-23 04:43:36 +01:00
David Grudl
b9ec3047b0 small code refactoring 2010-01-15 22:27:06 +01:00
David Grudl
e177436e38 added DateTime53, fix for crappy DateTime in PHP 5.2 2010-01-14 23:41:37 +01:00
David Grudl
9d4a887e7a typos 2010-01-11 17:03:57 +01:00
David Grudl
d61bd79982 DibiConnection: inTransaction() is back, implemented in drivers 2010-01-11 16:58:37 +01:00
David Grudl
c37475838f - year 2009 -> 2010 2010-01-03 15:32:26 +01:00
David Grudl
7688dd81f9 DibSQLite3Driver: improved by Roman Sklenar 2009-12-18 01:46:51 +01:00
Marek Jelen
686b60fb04 DibiOracleDriver: implemented getInsertId() 2009-12-18 01:44:35 +01:00
David Grudl
ccd20b6f17 added SQLite3 driver 2009-12-18 01:43:50 +01:00
David Grudl
20d22dd81b DibiConnection: deprecated inTransaction (BC break!) 2009-12-17 23:53:45 +01:00
David Grudl
f6d4107116 DibiResult: fixed bug in new-syntax fetchAssoc() 2009-12-09 02:34:58 +01:00
David Grudl
75ede18f94 removed PHP 5.1 support; removed DibiVariable & IDibiVariable 2009-11-26 05:38:20 +01:00
David Grudl
8586eb8e29 changed empty-date detection 2009-11-16 02:00:49 +01:00
David Grudl
5e7774404b code smoothing 2009-11-16 02:00:49 +01:00
Jan Vlcek
4b2a329127 DibiDataSource: removed the final keyword of the __toString() method. 2009-11-03 02:03:05 +08:00
Jan Vlcek
22b62ee9a5 MSSQL driver: Added subselect alias in applyLimit. 2009-11-03 02:03:04 +08:00
David Grudl
b00257c6b0 Updated DibiFirebirdDriver & DibiMsSql2005Driver 2009-10-13 17:02:26 +02:00
David Grudl
4235b3564f DibiResult: rewritten associate descriptor syntax 2009-10-06 18:09:13 +02:00
David Grudl
0337cbdba9 DibiResult: fetchAssoc refactoring 2009-10-06 16:51:27 +02:00
David Grudl
d68e526381 updated version number 2009-09-30 22:50:23 +02:00
David Grudl
3b2ca19d42 Released version 1.2 2009-09-18 07:26:02 +02:00
David Grudl
16f6d537e0 All setters provide a fluent interface now (i.e. return $this) 2009-09-18 03:38:10 +02:00
David Grudl
6d353c0b5d DibiResult: removed destructor 2009-09-09 17:04:20 +02:00
David Grudl
698c12eadc DibiColumnInfo: getType() is implemented lazy 2009-09-09 17:03:40 +02:00
David Grudl
70ba2f065c added DibiResultInfo, available via DibiResult::getInfo() 2009-09-09 17:02:46 +02:00
David Grudl
7e539f8f4f typo changes 2009-09-09 17:01:30 +02:00
David Grudl
532ed3a316 MySQL drivers: getColumns() obtains comments 2009-09-08 21:27:57 +02:00
David Grudl
08e70fda61 DibiTranslator: fixed bug in DateTime object usage 2009-08-26 00:09:05 +02:00
David Grudl
217de42d20 DibiPostgreDriver: added user => username alias (thanks to Milan Matejcek) 2009-08-21 13:48:16 +02:00
David Grudl
fa6d771813 dibi internally uses DateTime object in PHP 5.2 2009-08-21 01:34:07 +02:00
David Grudl
3777bacc02 DibiOracleDriver: supports configuration options 'fmtDate' & 'fmtDateTime' 2009-08-20 22:19:01 +02:00
David Grudl
9850a2f78b opened 1.2-dev 2009-08-17 15:58:08 +02:00
93 changed files with 7323 additions and 4230 deletions

View File

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

@@ -0,0 +1,166 @@
<?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.
*/
if (interface_exists('Nette\Diagnostics\IBarPanel')) {
class_alias('Nette\Diagnostics\IBarPanel', 'IBarPanel');
}
/**
* Dibi panel for Nette\Diagnostics.
*
* @author David Grudl
* @package dibi\nette
*/
class DibiNettePanel extends DibiObject implements IBarPanel
{
/** @var int maximum SQL length */
static public $maxLength = 1000;
/** @var bool explain queries? */
public $explain;
/** @var int */
public $filter;
/** @var array */
private $events = array();
public function __construct($explain = TRUE, $filter = NULL)
{
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
$this->explain = $explain;
}
public function register(DibiConnection $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');
}
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$this->events[] = $event;
}
/**
* Returns blue-screen custom tab.
* @return mixed
*/
public function renderException($e)
{
if ($e instanceof DibiException && $e->getSql()) {
return array(
'tab' => 'SQL',
'panel' => dibi::dump($e->getSql(), TRUE),
);
}
}
/**
* Returns HTML code for custom tab. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getTab()
{
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' : '')
. '</span>';
}
/**
* Returns HTML code for custom panel. (Nette\Diagnostics\IBarPanel)
* @return mixed
*/
public function getPanel()
{
$s = NULL;
$h = 'htmlSpecialChars';
foreach ($this->events as $event) {
$explain = NULL; // EXPLAIN is called here to work SELECT FOUND_ROWS()
if ($this->explain && $event->type === DibiEvent::SELECT) {
try {
$backup = array($event->connection->onEvent, dibi::$numOfQueries, dibi::$totalTime);
$event->connection->onEvent = NULL;
$cmd = is_string($this->explain) ? $this->explain : ($event->connection->getConfig('driver') === 'oracle' ? 'EXPLAIN PLAN' : 'EXPLAIN');
$explain = dibi::dump($event->connection->nativeQuery("$cmd $event->sql"), TRUE);
} catch (DibiException $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 .= '</td><td class="nette-DibiProfiler-sql">' . dibi::dump(strlen($event->sql) > self::$maxLength ? substr($event->sql, 0, self::$maxLength) . '...' : $event->sql, TRUE);
if ($explain) {
$s .= "<div id='nette-debug-DibiProfiler-row-$counter' class='nette-collapsed'>{$explain}</div>";
}
if ($event->source) {
$helpers = 'Nette\Diagnostics\Helpers';
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 .= "</td><td>{$event->count}</td><td>{$h($event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name'))}</td></tr>";
}
return empty($this->events) ? '' :
'<style> #nette-debug td.nette-DibiProfiler-sql { background: white !important }
#nette-debug .nette-DibiProfiler-source { color: #999 !important }
#nette-debug nette-DibiProfiler tr table { margin: 8px 0; max-height: 150px; overflow:auto } </style>
<h1>Queries: ' . dibi::$numOfQueries . (dibi::$totalTime === NULL ? '' : ', time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms') . '</h1>
<div class="nette-inner nette-DibiProfiler">
<table>
<tr><th>Time&nbsp;ms</th><th>SQL Statement</th><th>Rows</th><th>Connection</th></tr>' . $s . '
</table>
</div>';
}
}

View File

@@ -1,40 +0,0 @@
<?php
/**
* Nette Framework
*
* Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "Nette license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://nettephp.com
*
* @copyright Copyright (c) 2004, 2009 David Grudl
* @license http://nettephp.com/license Nette license
* @link http://nettephp.com
* @category Nette
* @package Nette
*/
/*namespace Nette;*/
/**
* Custom output for Nette\Debug.
*
* @author David Grudl
* @copyright Copyright (c) 2004, 2009 David Grudl
* @package Nette
*/
interface IDebuggable
{
/**
* Returns custom panels.
* @return array
*/
function getPanels();
}

View File

@@ -1,79 +1,46 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* dibi - smart database abstraction layer (http://dibiphp.com)
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005, 2012 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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.1.0', '<')) {
throw new Exception('dibi needs PHP 5.1.0 or newer.');
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 @
/**
* Compatibility with Nette
*/
if (!class_exists('NotImplementedException', FALSE)) {
class NotImplementedException extends LogicException {}
}
if (!class_exists('NotSupportedException', FALSE)) {
class NotSupportedException extends LogicException {}
}
if (!class_exists('MemberAccessException', FALSE)) {
class MemberAccessException extends LogicException {}
}
if (!class_exists('InvalidStateException', FALSE)) {
class InvalidStateException extends RuntimeException {}
}
if (!class_exists('IOException', FALSE)) {
class IOException extends RuntimeException {}
}
if (!class_exists('FileNotFoundException', FALSE)) {
class FileNotFoundException extends IOException {}
}
if (!interface_exists(/*Nette\*/'IDebuggable', FALSE)) {
require_once dirname(__FILE__) . '/Nette/IDebuggable.php';
}
// dibi libraries
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/DibiVariable.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/DibiProfiler.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';
}
@@ -86,52 +53,39 @@ require_once dirname(__FILE__) . '/libs/DibiProfiler.php';
* store connections info.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class dibi
{
/**#@+
* dibi data type
*/
const TEXT = 's'; // as 'string'
const BINARY = 'bin';
const BOOL = 'b';
const INTEGER = 'i';
const FLOAT = 'f';
const DATE = 'd';
const DATETIME = 't';
const TIME = 't';
/** 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 column types
*/
const FIELD_TEXT = self::TEXT;
const FIELD_BINARY = self::BINARY;
const FIELD_BOOL = self::BOOL;
const FIELD_INTEGER = self::INTEGER;
const FIELD_FLOAT = self::FLOAT;
const FIELD_DATE = self::DATE;
const FIELD_DATETIME = self::DATETIME;
const FIELD_TIME = self::TIME;
/**#@-*/
/** @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;
/**#@+
* dibi version
*/
const VERSION = '1.1';
const REVISION = '$WCREV$ released on $WCDATE$';
/**#@-*/
/** version */
const VERSION = '2.0',
REVISION = '$WCREV$ released on $WCDATE$';
/**#@+
* Configuration options
*/
const RESULT_WITH_TABLES = 'resultWithTables'; // for MySQL
const ROW_CLASS = 'rowClass';
const ASC = 'ASC', DESC = 'DESC';
/**#@-*/
/** sorting order */
const ASC = 'ASC',
DESC = 'DESC';
/** @var DibiConnection[] Connection registry storage for DibiConnection objects */
private static $registry = array();
@@ -139,12 +93,6 @@ class dibi
/** @var DibiConnection Current connection */
private static $connection;
/** @var array Substitutions for identifiers */
public static $substs = array();
/** @var callback Substitution fallback */
public static $substFallBack = array(__CLASS__, 'defaultSubstFallback');
/** @var array @see addHandler */
private static $handlers = array();
@@ -181,8 +129,8 @@ class dibi
/**
* Creates a new DibiConnection object and connects it to specified database.
* @param array|string|ArrayObject connection parameters
* @param string connection name
* @param mixed connection parameters
* @param string connection name
* @return DibiConnection
* @throws DibiException
*/
@@ -240,6 +188,18 @@ class dibi
/**
* Sets connection.
* @param DibiConnection
* @return DibiConnection
*/
public static function setConnection(DibiConnection $connection)
{
return self::$connection = $connection;
}
/**
* Change active connection.
* @param string connection registy name
@@ -253,18 +213,6 @@ class dibi
/**
* Retrieve active connection profiler.
* @return IDibiProfiler
* @throws DibiException
*/
public static function getProfiler()
{
return self::getConnection()->getProfiler();
}
/********************* monostate for active connection ****************d*g**/
@@ -570,39 +518,23 @@ class dibi
/**
* Pseudotype for timestamp representation.
* @param mixed datetime
* @return DibiVariable
* @return DibiDateTime
*/
public static function datetime($time = NULL)
{
if ($time === NULL) {
$time = time(); // current time
} elseif (is_numeric($time)) {
$time = (int) $time; // timestamp
} elseif ($time instanceof DateTime) {
$time = $time->format('U');
} else {
$time = strtotime($time); // try convert to timestamp
}
return new DibiVariable($time, dibi::DATETIME);
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING);
return new DibiDateTime($time);
}
/**
* Pseudotype for date representation.
* @param mixed date
* @return DibiVariable
* @deprecated
*/
public static function date($date = NULL)
{
$var = self::datetime($date);
$var->modifier = dibi::DATE;
return $var;
trigger_error(__METHOD__ . '() is deprecated; create DibiDateTime object instead.', E_USER_WARNING);
return new DibiDateTime($date);
}
@@ -612,59 +544,46 @@ class dibi
/**
* Create a new substitution pair for indentifiers.
* @param string from
* @param string to
* @return void
* Returns substitution hashmap - Monostate for DibiConnection::getSubstitutes().
* @return DibiHashMap
*/
public static function getSubstitutes()
{
return self::getConnection()->getSubstitutes();
}
/** @deprecated */
public static function addSubst($expr, $subst)
{
self::$substs[$expr] = $subst;
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->expr = val; instead.', E_USER_WARNING);
self::getSubstitutes()->$expr = $subst;
}
/**
* Remove substitution pair.
* @param mixed from or TRUE
* @return void
*/
/** @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) {
self::$substs = array();
foreach ($substitutes as $expr => $foo) {
unset($substitutes->$expr);
}
} else {
unset(self::$substs[':'.$expr.':']);
unset($substitutes->$expr);
}
}
/**
* Sets substitution fallback handler.
* @param callback
* @return void
*/
/** @deprecated */
public static function setSubstFallback($callback)
{
if (!is_callable($callback)) {
$able = is_callable($callback, TRUE, $textual);
throw new InvalidArgumentException("Handler '$textual' is not " . ($able ? 'callable.' : 'valid PHP callback.'));
}
self::$substFallBack = $callback;
}
/**
* Default substitution fallback handler.
* @param string
* @return mixed
*/
public static function defaultSubstFallback($expr)
{
throw new InvalidStateException("Missing substitution for '$expr' expression.");
trigger_error(__METHOD__ . '() is deprecated; use dibi::getSubstitutes()->setCallback() instead.', E_USER_WARNING);
self::getSubstitutes()->setCallback($callback);
}
@@ -688,8 +607,8 @@ class dibi
} else {
if ($sql === NULL) $sql = self::$sql;
static $keywords1 = 'SELECT|UPDATE|INSERT(?:\s+INTO)?|REPLACE(?:\s+INTO)?|DELETE|FROM|WHERE|HAVING|GROUP\s+BY|ORDER\s+BY|LIMIT|SET|VALUES|LEFT\s+JOIN|INNER\s+JOIN|TRUNCATE';
static $keywords2 = 'ALL|DISTINCT|DISTINCTROW|AS|USING|ON|AND|OR|IN|IS|NOT|NULL|LIKE|TRUE|FALSE';
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 ";
@@ -699,13 +618,16 @@ class dibi
$sql = preg_replace('#[ \t]{2,}#', " ", $sql);
$sql = wordwrap($sql, 100);
$sql = htmlSpecialChars($sql);
$sql = preg_replace("#\n{2,}#", "\n", $sql);
$sql = preg_replace("#([ \t]*\r?\n){2,}#", "\n", $sql);
// syntax highlight
$sql = preg_replace_callback("#(/\\*.+?\\*/)|(\\*\\*.+?\\*\\*)|(?<=[\\s,(])($keywords1)(?=[\\s,)])|(?<=[\\s,(=])($keywords2)(?=[\\s,)=])#is", array('dibi', 'highlightCallback'), $sql);
$sql = trim($sql);
echo '<pre class="dump">', $sql, "</pre>\n";
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) {
@@ -732,23 +654,4 @@ class dibi
return '<strong style="color:green">' . $matches[4] . '</strong>';
}
/**
* Returns brief descriptions.
* @return string
* @return array
*/
public static function getColophon($sender = NULL)
{
$arr = array(
'Number of SQL queries: ' . dibi::$numOfQueries
. (dibi::$totalTime === NULL ? '' : ', elapsed time: ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms'),
);
if ($sender === 'bluescreen') {
$arr[] = 'dibi ' . dibi::VERSION . ' (revision ' . dibi::REVISION . ')';
}
return $arr;
}
}

View File

@@ -1,40 +1,31 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* The dibi driver for Firebird/InterBase database.
*
* Connection options:
* - 'database' - the path to database file (server:/path/database.fdb)
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'charset' - character encoding to set
* - 'buffers' - buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - database => the path to database file (server:/path/database.fdb)
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - buffers (int) => buffers is the number of database buffers to allocate for the server-side cache. If 0 or omitted, server chooses its own default.
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author Tomáš Kraina, Roman Sklenář
* @copyright Copyright (c) 2009
* @package dibi
* @package dibi\drivers
*/
class DibiFirebirdDriver extends DibiObject implements IDibiDriver
class DibiFirebirdDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
const ERROR_EXCEPTION_THROWN = -836;
@@ -44,6 +35,9 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var resource Resultset resource */
private $transaction;
@@ -52,12 +46,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('interbase')) {
throw new DibiDriverException("PHP extension 'interbase' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'interbase' is not loaded.");
}
}
@@ -116,14 +110,14 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException|DibiException
*/
public function query($sql)
{
DibiDriverException::tryError();
$resource = $this->inTransaction ? $this->transaction : $this->connection;
$this->resultSet = ibase_query($resource, $sql);
$res = ibase_query($resource, $sql);
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
@@ -135,11 +129,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
}
}
if ($this->resultSet === FALSE) {
if ($res === FALSE) {
throw new DibiDriverException(ibase_errmsg(), ibase_errcode(), $sql);
}
return is_resource($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
@@ -163,7 +158,6 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function getInsertId($sequence)
{
return ibase_gen_id($sequence, 0, $this->connection);
//throw new NotSupportedException('Firebird/InterBase does not support autoincrementing.');
}
@@ -177,7 +171,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function begin($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiDriverException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
$this->transaction = ibase_trans($this->resource);
$this->inTransaction = TRUE;
@@ -194,11 +188,11 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function commit($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiDriverException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_commit($this->transaction)) {
DibiDriverException('Unable to handle operation - failure when commiting transaction.');
throw new DibiDriverException('Unable to handle operation - failure when commiting transaction.');
}
$this->inTransaction = FALSE;
@@ -215,11 +209,11 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function rollback($savepoint = NULL)
{
if ($savepoint !== NULL) {
throw new DibiDriverException('Savepoints are not supported in Firebird/Interbase.');
throw new DibiNotSupportedException('Savepoints are not supported in Firebird/Interbase.');
}
if (!ibase_rollback($this->transaction)) {
DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
throw new DibiDriverException('Unable to handle operation - failure when rolbacking transaction.');
}
$this->inTransaction = FALSE;
@@ -227,13 +221,49 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return $this->inTransaction;
}
/**
* Returns the connection resource.
* @return resource
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -244,7 +274,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -263,10 +293,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
return $value ? 1 : 0;
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -275,6 +305,19 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -313,6 +356,17 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -328,12 +382,11 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
DibiDriverException::tryError();
$result = $assoc ? ibase_fetch_assoc($this->resultSet) : ibase_fetch_row($this->resultSet); // intentionally @
$result = $assoc ? ibase_fetch_assoc($this->resultSet, IBASE_TEXT) : ibase_fetch_row($this->resultSet, IBASE_TEXT); // intentionally @
if (DibiDriverException::catchError($msg)) {
if (ibase_errcode() == self::ERROR_EXCEPTION_THROWN) {
@@ -358,7 +411,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function seek($row)
{
throw new DibiDriverException("Firebird/Interbase do not support seek in result set.");
throw new DibiNotSupportedException("Firebird/Interbase do not support seek in result set.");
}
@@ -381,7 +434,8 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
@@ -390,14 +444,25 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
throw new NotImplementedException;
$count = ibase_num_fields($this->resultSet);
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) ibase_field_info($this->resultSet, $i);
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['name'],
'table' => $row['relation'],
'nativetype' => $row['type'],
);
}
return $columns;
}
/********************* reflection ********************/
/********************* IDibiReflector ********************/
@@ -407,21 +472,20 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getTables()
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$RELATION_NAME),
CASE RDB\$VIEW_BLR WHEN NULL THEN 'TRUE' ELSE 'FALSE' END
FROM RDB\$RELATIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = array(
$tables = array();
while ($row = $res->fetch(FALSE)) {
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'TRUE',
);
}
$this->free();
return $res;
return $tables;
}
@@ -434,7 +498,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function getColumns($table)
{
$table = strtoupper($table);
$this->query("
$res = $this->query("
SELECT TRIM(r.RDB\$FIELD_NAME) AS FIELD_NAME,
CASE f.RDB\$FIELD_TYPE
WHEN 261 THEN 'BLOB'
@@ -464,10 +528,10 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
ORDER BY r.RDB\$FIELD_POSITION;"
);
$res = array();
while ($row = $this->fetch(TRUE)) {
$columns = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['FIELD_NAME'];
$res[$key] = array(
$columns[$key] = array(
'name' => $key,
'table' => $table,
'nativetype' => trim($row['FIELD_TYPE']),
@@ -477,8 +541,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
'autoincrement' => FALSE,
);
}
$this->free();
return $res;
return $columns;
}
@@ -491,7 +554,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function getIndexes($table)
{
$table = strtoupper($table);
$this->query("
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
i.RDB\$UNIQUE_FLAG AS UNIQUE_FLAG,
@@ -504,17 +567,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
WHERE UPPER(i.RDB\$RELATION_NAME) = '$table'
ORDER BY s.RDB\$FIELD_POSITION"
);
$res = array();
while ($row = $this->fetch(TRUE)) {
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$res[$key]['name'] = $key;
$res[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
$res[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$res[$key]['table'] = $table;
$res[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
$indexes[$key]['name'] = $key;
$indexes[$key]['unique'] = $row['UNIQUE_FLAG'] === 1;
$indexes[$key]['primary'] = $row['CONSTRAINT_TYPE'] === 'PRIMARY KEY';
$indexes[$key]['table'] = $table;
$indexes[$key]['columns'][$row['FIELD_POSITION']] = $row['FIELD_NAME'];
}
$this->free();
return $res;
return $indexes;
}
@@ -527,7 +589,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
public function getForeignKeys($table)
{
$table = strtoupper($table);
$this->query("
$res = $this->query("
SELECT TRIM(s.RDB\$INDEX_NAME) AS INDEX_NAME,
TRIM(s.RDB\$FIELD_NAME) AS FIELD_NAME,
FROM RDB\$INDEX_SEGMENTS s
@@ -536,17 +598,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
AND r.RDB\$CONSTRAINT_TYPE = 'FOREIGN KEY'
ORDER BY s.RDB\$FIELD_POSITION"
);
$res = array();
while ($row = $this->fetch(TRUE)) {
$keys = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['INDEX_NAME'];
$res[$key] = array(
$keys[$key] = array(
'name' => $key,
'column' => $row['FIELD_NAME'],
'table' => $table,
);
}
$this->free();
return $res;
return $keys;
}
@@ -558,19 +619,18 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getIndices($table)
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
AND RDB\$UNIQUE_FLAG IS NULL
AND RDB\$FOREIGN_KEY IS NULL;"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$indices = array();
while ($row = $res->fetch(FALSE)) {
$indices[] = $row[0];
}
$this->free();
return $res;
return $indices;
}
@@ -582,7 +642,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getConstraints($table)
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$INDEX_NAME)
FROM RDB\$INDICES
WHERE RDB\$RELATION_NAME = UPPER('$table')
@@ -591,12 +651,11 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
OR RDB\$FOREIGN_KEY IS NOT NULL
);"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$constraints = array();
while ($row = $res->fetch(FALSE)) {
$constraints[] = $row[0];
}
$this->free();
return $res;
return $constraints;
}
@@ -610,7 +669,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getTriggersMeta($table = NULL)
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$TRIGGER_NAME) AS TRIGGER_NAME,
TRIM(RDB\$RELATION_NAME) AS TABLE_NAME,
CASE RDB\$TRIGGER_TYPE
@@ -636,9 +695,9 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
WHERE RDB\$SYSTEM_FLAG = 0"
. ($table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table');")
);
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['TRIGGER_NAME']] = array(
$triggers = array();
while ($row = $res->fetch(TRUE)) {
$triggers[$row['TRIGGER_NAME']] = array(
'name' => $row['TRIGGER_NAME'],
'table' => $row['TABLE_NAME'],
'type' => trim($row['TRIGGER_TYPE']),
@@ -646,8 +705,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
'enabled' => trim($row['TRIGGER_ENABLED']) === 'TRUE',
);
}
$this->free();
return $res;
return $triggers;
}
@@ -665,13 +723,12 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
WHERE RDB\$SYSTEM_FLAG = 0";
$q .= $table === NULL ? ";" : " AND RDB\$RELATION_NAME = UPPER('$table')";
$this->query($q);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$res = $this->query($q);
$triggers = array();
while ($row = $res->fetch(FALSE)) {
$triggers[] = $row[0];
}
$this->free();
return $res;
return $triggers;
}
@@ -683,7 +740,7 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getProceduresMeta()
{
$this->query("
$res = $this->query("
SELECT
TRIM(p.RDB\$PARAMETER_NAME) AS PARAMETER_NAME,
TRIM(p.RDB\$PROCEDURE_NAME) AS PROCEDURE_NAME,
@@ -715,18 +772,17 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
LEFT JOIN RDB\$FIELDS f ON f.RDB\$FIELD_NAME = p.RDB\$FIELD_SOURCE
ORDER BY p.RDB\$PARAMETER_TYPE, p.RDB\$PARAMETER_NUMBER;"
);
$res = array();
while ($row = $this->fetch(TRUE)) {
$procedures = array();
while ($row = $res->fetch(TRUE)) {
$key = $row['PROCEDURE_NAME'];
$io = trim($row['PARAMETER_TYPE']);
$num = $row['PARAMETER_NUMBER'];
$res[$key]['name'] = $row['PROCEDURE_NAME'];
$res[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME'];
$res[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
$res[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
$procedures[$key]['name'] = $row['PROCEDURE_NAME'];
$procedures[$key]['params'][$io][$num]['name'] = $row['PARAMETER_NAME'];
$procedures[$key]['params'][$io][$num]['type'] = trim($row['FIELD_TYPE']);
$procedures[$key]['params'][$io][$num]['size'] = $row['FIELD_LENGTH'];
}
$this->free();
return $res;
return $procedures;
}
@@ -737,16 +793,15 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getProcedures()
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$PROCEDURE_NAME)
FROM RDB\$PROCEDURES;"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$procedures = array();
while ($row = $res->fetch(FALSE)) {
$procedures[] = $row[0];
}
$this->free();
return $res;
return $procedures;
}
@@ -757,17 +812,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getGenerators()
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$GENERATOR_NAME)
FROM RDB\$GENERATORS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$generators = array();
while ($row = $res->fetch(FALSE)) {
$generators[] = $row[0];
}
$this->free();
return $res;
return $generators;
}
@@ -778,17 +832,16 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
*/
public function getFunctions()
{
$this->query("
$res = $this->query("
SELECT TRIM(RDB\$FUNCTION_NAME)
FROM RDB\$FUNCTIONS
WHERE RDB\$SYSTEM_FLAG = 0;"
);
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = $row[0];
$functions = array();
while ($row = $res->fetch(FALSE)) {
$functions[] = $row[0];
}
$this->free();
return $res;
return $functions;
}
}
@@ -800,8 +853,8 @@ class DibiFirebirdDriver extends DibiObject implements IDibiDriver
* Database procedure exception.
*
* @author Roman Sklenář
* @copyright Copyright (c) 2009
* @package dibi
* @copyright Copyright (c) 2010
* @package dibi\drivers
*/
class DibiProcedureException extends DibiException
{
@@ -822,6 +875,7 @@ class DibiProcedureException extends DibiException
}
/**
* Gets the exception severity.
* @return string

View File

@@ -1,40 +1,32 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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.
*
* Connection options:
* - 'host' - the MS SQL server host name. It can also include a port number (hostname:port)
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'persistent' - try to find a persistent link?
* - 'database' - the database name to select
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* 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
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiMsSqlDriver extends DibiObject implements IDibiDriver
class DibiMsSqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
@@ -42,15 +34,18 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mssql')) {
throw new DibiDriverException("PHP extension 'mssql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mssql' is not loaded.");
}
}
@@ -75,7 +70,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
throw new DibiDriverException("Can't connect to DB.");
}
if (isset($config['database']) && !@mssql_select_db($config['database'], $this->connection)) { // intentionally @
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]'.");
}
}
@@ -96,18 +91,19 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->resultSet = @mssql_query($sql, $this->connection); // intentionally @
$res = @mssql_query($sql, $this->connection); // intentionally @
if ($this->resultSet === FALSE) {
throw new DibiDriverException('Query error', 0, $sql);
if ($res === FALSE) {
throw new DibiDriverException(mssql_get_last_message(), 0, $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
return is_resource($this->resultSet) ? clone $this : NULL;
}
@@ -184,7 +180,32 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
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;
}
@@ -195,7 +216,7 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -209,17 +230,16 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
$value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -228,6 +248,20 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -256,11 +290,11 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
{
// offset support is missing
if ($limit >= 0) {
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ') t';
}
if ($offset) {
throw new NotImplementedException('Offset is not implemented.');
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -270,6 +304,17 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -285,7 +330,6 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -322,20 +366,20 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = mssql_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mssql_fetch_field($this->resultSet, $i);
$res[] = array(
$columns[] = array(
'name' => $row['name'],
'fullname' => $row['column_source'] ? $row['column_source'] . '.' . $row['name'] : $row['name'],
'table' => $row['column_source'],
'nativetype' => $row['type'],
);
}
return $res;
return $columns;
}
@@ -346,58 +390,9 @@ class DibiMsSqlDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
throw new NotImplementedException;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
}
}

231
dibi/drivers/mssql.reflector.php Executable file
View File

@@ -0,0 +1,231 @@
<?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,38 +1,32 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* The dibi driver for MS SQL Driver 2005 database.
*
* Connection options:
* - 'host' - the MS SQL server host name. It can also include a port number (hostname:port)
* - 'options' - connection info array {@link http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx}
* - 'lazy' - if TRUE, connection will be established only when required
* - 'charset' - character encoding to set (default is UTF-8)
* - 'resource' - connection resource (optional)
* 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}
* - charset => character encoding to set (default is UTF-8)
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
class DibiMsSql2005Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
@@ -40,18 +34,21 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var string character encoding */
private $charset;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlsrv')) {
throw new DibiDriverException("PHP extension 'sqlsrv' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'sqlsrv' is not loaded.");
}
}
@@ -64,20 +61,22 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
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');
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} elseif (isset($config['options'])) {
$this->connection = sqlsrv_connect($config['host'], $config['options']);
} else {
$this->connection = sqlsrv_connect($config['host']);
$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']);
}
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
}
@@ -96,20 +95,22 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$sql = iconv($this->charset, 'UTF-16LE', $sql);
$this->resultSet = sqlsrv_query($this->connection, $sql);
$this->affectedRows = FALSE;
$res = sqlsrv_query($this->connection, $sql);
if ($this->resultSet === FALSE) {
if ($res === FALSE) {
$info = sqlsrv_errors();
throw new DibiDriverException($info[0]['message'], $info[0]['code'], $sql);
}
return is_resource($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
$this->affectedRows = sqlsrv_rows_affected($res);
return $this->createResultDriver($res);
}
}
@@ -120,7 +121,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function getAffectedRows()
{
return sqlsrv_rows_affected($this->resultSet);
return $this->affectedRows;
}
@@ -149,7 +150,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function begin($savepoint = NULL)
{
$this->query('BEGIN TRANSACTION');
sqlsrv_begin_transaction($this->connection);
}
@@ -162,7 +163,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function commit($savepoint = NULL)
{
$this->query('COMMIT');
sqlsrv_commit($this->connection);
}
@@ -175,7 +176,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function rollback($savepoint = NULL)
{
$this->query('ROLLBACK');
sqlsrv_rollback($this->connection);
}
@@ -186,7 +187,32 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
throw new DibiNotSupportedException;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -197,7 +223,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -211,17 +237,16 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
case dibi::IDENTIFIER:
// @see http://msdn.microsoft.com/en-us/library/ms176027.aspx
$value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -230,6 +255,20 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
/**
* 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
@@ -262,7 +301,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
}
if ($offset) {
throw new NotImplementedException('Offset is not implemented.');
throw new DibiNotImplementedException('Offset is not implemented.');
}
}
@@ -272,13 +311,24 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -287,17 +337,10 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
$row = sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
foreach ($row as $k => $v) {
if (is_string($v)) {
$row[$k] = iconv('UTF-16LE', $this->charset, $v);
}
}
return $row;
return sqlsrv_fetch_array($this->resultSet, $assoc ? SQLSRV_FETCH_ASSOC : SQLSRV_FETCH_NUMERIC);
}
@@ -309,7 +352,7 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function seek($row)
{
throw new NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -330,19 +373,19 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = sqlsrv_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) sqlsrv_field_metadata($this->resultSet, $i);
$res[] = array(
$columns[] = array(
'name' => $row['Name'],
'fullname' => $row['Name'],
'nativetype' => $row['Type'],
);
}
return $res;
return $columns;
}
@@ -353,58 +396,8 @@ class DibiMsSql2005Driver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
}
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
throw new NotImplementedException;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -1,46 +1,40 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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';
/**
* The dibi driver for MySQL database.
*
* Connection options:
* - 'host' - the MySQL server host name
* - 'port' - the port number to attempt to connect to the MySQL server
* - 'socket' - the socket or named pipe
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'persistent' - try to find a persistent link?
* - 'database' - the database name to select
* - 'charset' - character encoding to set
* - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - 'options' - driver specific constants (MYSQL_*)
* - 'sqlmode' - see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - host => the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - flags (int) => driver specific constants (MYSQL_CLIENT_*)
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiMySqlDriver extends DibiObject implements IDibiDriver
class DibiMySqlDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
@@ -52,18 +46,21 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysql')) {
throw new DibiDriverException("PHP extension 'mysql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mysql' is not loaded.");
}
}
@@ -76,13 +73,13 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
*/
public function connect(array &$config)
{
DibiConnection::alias($config, 'options');
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');
if (!isset($config['host'])) {
@@ -103,9 +100,9 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
}
if (empty($config['persistent'])) {
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['options']); // intentionally @
$this->connection = @mysql_connect($host, $config['username'], $config['password'], TRUE, $config['flags']); // intentionally @
} else {
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['options']); // intentionally @
$this->connection = @mysql_pconnect($host, $config['username'], $config['password'], $config['flags']); // intentionally @
}
}
@@ -120,10 +117,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
$ok = @mysql_set_charset($config['charset'], $this->connection); // intentionally @
}
if (!$ok) {
$ok = @mysql_query("SET NAMES '$config[charset]'", $this->connection); // intentionally @
if (!$ok) {
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
$this->query("SET NAMES '$config[charset]'");
}
}
@@ -134,11 +128,11 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
}
if (isset($config['sqlmode'])) {
if (!@mysql_query("SET sql_mode='$config[sqlmode]'", $this->connection)) { // intentionally @
throw new DibiDriverException(mysql_error($this->connection), mysql_errno($this->connection));
}
$this->query("SET sql_mode='$config[sqlmode]'");
}
$this->query("SET time_zone='" . date('P') . "'");
$this->buffered = empty($config['unbuffered']);
}
@@ -158,22 +152,23 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
if ($this->buffered) {
$this->resultSet = @mysql_query($sql, $this->connection); // intentionally @
$res = @mysql_query($sql, $this->connection); // intentionally @
} else {
$this->resultSet = @mysql_unbuffered_query($sql, $this->connection); // intentionally @
$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);
}
return is_resource($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
@@ -186,6 +181,8 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
{
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysql_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) throw new DibiPcreException;
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
@@ -261,7 +258,32 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -272,7 +294,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -281,24 +303,29 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
{
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
$value = str_replace('`', '``', $value);
return '`' . str_replace('.', '`.`', $value) . '`';
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -307,6 +334,20 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
@@ -346,6 +387,17 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -353,7 +405,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiDriverException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return mysql_num_rows($this->resultSet);
}
@@ -364,7 +416,6 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -382,7 +433,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
public function seek($row)
{
if (!$this->buffered) {
throw new DibiDriverException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return mysql_data_seek($this->resultSet, $row);
@@ -406,13 +457,13 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = mysql_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysql_fetch_field($this->resultSet, $i);
$res[] = array(
$columns[] = array(
'name' => $row['name'],
'table' => $row['table'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
@@ -420,7 +471,7 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
'vendor' => $row,
);
}
return $res;
return $columns;
}
@@ -431,91 +482,8 @@ class DibiMySqlDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
}
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$this->query("SHOW FULL TABLES");
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$this->query("SHOW COLUMNS FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$res[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
);
}
$this->free();
return $res;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$this->query("SHOW INDEX FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['Key_name']]['name'] = $row['Key_name'];
$res[$row['Key_name']]['unique'] = !$row['Non_unique'];
$res[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$res[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
$this->free();
return array_values($res);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
}

View File

@@ -0,0 +1,129 @@
<?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,46 +1,41 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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';
/**
* The dibi driver for MySQL database via improved extension.
*
* Connection options:
* - 'host' - the MySQL server host name
* - 'port' - the port number to attempt to connect to the MySQL server
* - 'socket' - the socket or named pipe
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'persistent' - try to find a persistent link?
* - 'database' - the database name to select
* - 'charset' - character encoding to set
* - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - 'options' - driver specific constants (MYSQLI_*)
* - 'sqlmode' - see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - host => the MySQL server host name
* - port (int) => the port number to attempt to connect to the MySQL server
* - socket => the socket or named pipe
* - username (or user)
* - password (or pass)
* - database => the database name to select
* - options (array) => array of driver specific constants (MYSQLI_*) and values {@see mysqli_options}
* - flags (int) => driver specific constants (MYSQLI_CLIENT_*) {@see mysqli_real_connect}
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - unbuffered (bool) => sends query without fetching and buffering the result rows automatically?
* - sqlmode => see http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html
* - resource (mysqli) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiMySqliDriver extends DibiObject implements IDibiDriver
class DibiMySqliDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
const ERROR_ACCESS_DENIED = 1045;
const ERROR_DUPLICATE_ENTRY = 1062;
@@ -52,18 +47,21 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
/** @var mysqli_result Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool Is buffered (seekable and countable)? */
private $buffered;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('mysqli')) {
throw new DibiDriverException("PHP extension 'mysqli' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'mysqli' is not loaded.");
}
}
@@ -76,14 +74,13 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
*/
public function connect(array &$config)
{
DibiConnection::alias($config, 'options');
DibiConnection::alias($config, 'database');
mysqli_report(MYSQLI_REPORT_OFF);
if (isset($config['resource'])) {
$this->connection = $config['resource'];
} 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');
@@ -99,8 +96,21 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
}
}
$foo = & $config['flags'];
$foo = & $config['database'];
$this->connection = mysqli_init();
@mysqli_real_connect($this->connection, $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['options']); // intentionally @
if (isset($config['options'])) {
if (is_scalar($config['options'])) {
$config['flags'] = $config['options']; // back compatibility
trigger_error(__CLASS__ . ": configuration item 'options' must be array; for constants MYSQLI_CLIENT_* use 'flags'.", E_USER_NOTICE);
} else {
foreach ((array) $config['options'] as $key => $value) {
mysqli_options($this->connection, $key, $value);
}
}
}
@mysqli_real_connect($this->connection, (empty($config['persistent']) ? '' : 'p:') . $config['host'], $config['username'], $config['password'], $config['database'], $config['port'], $config['socket'], $config['flags']); // intentionally @
if ($errno = mysqli_connect_errno()) {
throw new DibiDriverException(mysqli_connect_error(), $errno);
@@ -114,19 +124,16 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
$ok = @mysqli_set_charset($this->connection, $config['charset']); // intentionally @
}
if (!$ok) {
$ok = @mysqli_query($this->connection, "SET NAMES '$config[charset]'"); // intentionally @
if (!$ok) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection));
}
$this->query("SET NAMES '$config[charset]'");
}
}
if (isset($config['sqlmode'])) {
if (!@mysqli_query($this->connection, "SET sql_mode='$config[sqlmode]'")) { // intentionally @
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection));
}
$this->query("SET sql_mode='$config[sqlmode]'");
}
$this->query("SET time_zone='" . date('P') . "'");
$this->buffered = empty($config['unbuffered']);
}
@@ -146,18 +153,19 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->resultSet = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
$res = @mysqli_query($this->connection, $sql, $this->buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); // intentionally @
if (mysqli_errno($this->connection)) {
throw new DibiDriverException(mysqli_error($this->connection), mysqli_errno($this->connection), $sql);
}
return is_object($this->resultSet) ? clone $this : NULL;
} elseif (is_object($res)) {
return $this->createResultDriver($res);
}
}
@@ -170,6 +178,8 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
{
$res = array();
preg_match_all('#(.+?): +(\d+) *#', mysqli_info($this->connection), $matches, PREG_SET_ORDER);
if (preg_last_error()) throw new DibiPcreException;
foreach ($matches as $m) {
$res[$m[1]] = (int) $m[2];
}
@@ -245,7 +255,32 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
return @$this->connection->thread_id ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiMySqlReflector($this);
}
/**
* Result set driver factory.
* @param mysqli_result
* @return IDibiResultDriver
*/
public function createResultDriver(mysqli_result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -256,7 +291,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -271,17 +306,16 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
return "_binary'" . mysqli_real_escape_string($this->connection, $value) . "'";
case dibi::IDENTIFIER:
$value = str_replace('`', '``', $value);
return '`' . str_replace('.', '`.`', $value) . '`';
return '`' . str_replace('`', '``', $value) . '`';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -290,6 +324,20 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\n\r\\'%_");
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
@@ -329,6 +377,17 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -336,7 +395,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiDriverException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return mysqli_num_rows($this->resultSet);
}
@@ -347,7 +406,6 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -365,7 +423,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
public function seek($row)
{
if (!$this->buffered) {
throw new DibiDriverException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return mysqli_data_seek($this->resultSet, $row);
}
@@ -388,7 +446,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
static $types;
if (empty($types)) {
@@ -398,13 +456,14 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
$types[$value] = substr($key, 12);
}
}
$types[MYSQLI_TYPE_TINY] = $types[MYSQLI_TYPE_SHORT] = $types[MYSQLI_TYPE_LONG] = 'INT';
}
$count = mysqli_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = (array) mysqli_fetch_field_direct($this->resultSet, $i);
$res[] = array(
$columns[] = array(
'name' => $row['name'],
'table' => $row['orgtable'],
'fullname' => $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'],
@@ -412,7 +471,7 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
'vendor' => $row,
);
}
return $res;
return $columns;
}
@@ -423,109 +482,8 @@ class DibiMySqliDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
}
/********************* reflection ****************d*g**/
/**
* 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()
");*/
$this->query("SHOW FULL TABLES");
$res = array();
while ($row = $this->fetch(FALSE)) {
$res[] = array(
'name' => $row[0],
'view' => isset($row[1]) && $row[1] === 'VIEW',
);
}
$this->free();
return $res;
}
/**
* 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()
");*/
$this->query("SHOW COLUMNS FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$type = explode('(', $row['Type']);
$res[] = array(
'name' => $row['Field'],
'table' => $table,
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['Null'] === 'YES',
'default' => $row['Default'],
'autoincrement' => $row['Extra'] === 'auto_increment',
);
}
$this->free();
return $res;
}
/**
* 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
");*/
$this->query("SHOW INDEX FROM `$table`");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['Key_name']]['name'] = $row['Key_name'];
$res[$row['Key_name']]['unique'] = !$row['Non_unique'];
$res[$row['Key_name']]['primary'] = $row['Key_name'] === 'PRIMARY';
$res[$row['Key_name']]['columns'][$row['Seq_in_index'] - 1] = $row['Column_name'];
}
$this->free();
return array_values($res);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
$this->autoFree = FALSE;
return @$this->resultSet->type === NULL ? NULL : $this->resultSet;
}
}

View File

@@ -1,39 +1,30 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* The dibi driver interacting with databases via ODBC connections.
*
* Connection options:
* - 'dsn' - driver specific DSN
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'persistent' - try to find a persistent link?
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - dsn => driver specific DSN
* - username (or user)
* - password (or pass)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiOdbcDriver extends DibiObject implements IDibiDriver
class DibiOdbcDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
/** @var resource Connection resource */
private $connection;
@@ -41,18 +32,24 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var int Cursor */
private $row = 0;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('odbc')) {
throw new DibiDriverException("PHP extension 'odbc' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'odbc' is not loaded.");
}
}
@@ -101,18 +98,21 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->resultSet = @odbc_exec($this->connection, $sql); // intentionally @
$this->affectedRows = FALSE;
$res = @odbc_exec($this->connection, $sql); // intentionally @
if ($this->resultSet === FALSE) {
if ($res === FALSE) {
throw new DibiDriverException(odbc_errormsg($this->connection) . ' ' . odbc_error($this->connection), 0, $sql);
}
return is_resource($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
$this->affectedRows = odbc_num_rows($res);
return $this->createResultDriver($res);
}
}
@@ -123,7 +123,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getAffectedRows()
{
return odbc_num_rows($this->resultSet);
return $this->affectedRows;
}
@@ -134,7 +134,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getInsertId($sequence)
{
throw new NotSupportedException('ODBC does not support autoincrementing.');
throw new DibiNotSupportedException('ODBC does not support autoincrementing.');
}
@@ -186,13 +186,49 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !odbc_autocommit($this->connection);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -203,7 +239,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -216,17 +252,16 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
return "'" . str_replace("'", "''", $value) . "'";
case dibi::IDENTIFIER:
$value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("#m/d/Y#", $value);
return $value instanceof DateTime ? $value->format("#m/d/Y#") : date("#m/d/Y#", $value);
case dibi::DATETIME:
return date("#m/d/Y H:i:s#", $value);
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.');
@@ -235,6 +270,20 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -266,7 +315,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
$sql = 'SELECT TOP ' . (int) $limit . ' * FROM (' . $sql . ')';
}
if ($offset) throw new InvalidArgumentException('Offset is not implemented in driver odbc.');
if ($offset) throw new DibiNotSupportedException('Offset is not implemented in driver odbc.');
}
@@ -275,6 +324,17 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -291,7 +351,6 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -338,19 +397,19 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = odbc_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$res[] = array(
$columns[] = array(
'name' => odbc_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => odbc_field_name($this->resultSet, $i),
'nativetype'=> odbc_field_type($this->resultSet, $i),
);
}
return $res;
return $columns;
}
@@ -361,12 +420,13 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* reflection ****************d*g**/
/********************* IDibiReflector ****************d*g**/
@@ -376,18 +436,18 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getTables()
{
$result = odbc_tables($this->connection);
$res = array();
while ($row = odbc_fetch_array($result)) {
$res = odbc_tables($this->connection);
$tables = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_TYPE'] === 'TABLE' || $row['TABLE_TYPE'] === 'VIEW') {
$res[] = array(
$tables[] = array(
'name' => $row['TABLE_NAME'],
'view' => $row['TABLE_TYPE'] === 'VIEW',
);
}
}
odbc_free_result($result);
return $res;
odbc_free_result($res);
return $tables;
}
@@ -399,11 +459,11 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getColumns($table)
{
$result = odbc_columns($this->connection);
$res = array();
while ($row = odbc_fetch_array($result)) {
$res = odbc_columns($this->connection);
$columns = array();
while ($row = odbc_fetch_array($res)) {
if ($row['TABLE_NAME'] === $table) {
$res[] = array(
$columns[] = array(
'name' => $row['COLUMN_NAME'],
'table' => $table,
'nativetype' => $row['TYPE_NAME'],
@@ -413,8 +473,8 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
);
}
}
odbc_free_result($result);
return $res;
odbc_free_result($res);
return $columns;
}
@@ -426,7 +486,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getIndexes($table)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -438,7 +498,7 @@ class DibiOdbcDriver extends DibiObject implements IDibiDriver
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -1,39 +1,32 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* The dibi driver for Oracle database.
*
* Connection options:
* - 'database' (or 'db') - the name of the local Oracle instance or the name of the entry in tnsnames.ora
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'charset' - character encoding to set
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - database => the name of the local Oracle instance or the name of the entry in tnsnames.ora
* - username (or user)
* - password (or pass)
* - charset => character encoding to set
* - 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
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiOracleDriver extends DibiObject implements IDibiDriver
class DibiOracleDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
/** @var resource Connection resource */
private $connection;
@@ -41,18 +34,24 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var bool */
private $autocommit = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('oci8')) {
throw new DibiDriverException("PHP extension 'oci8' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'oci8' is not loaded.");
}
}
@@ -65,7 +64,9 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function connect(array &$config)
{
DibiConnection::alias($config, 'charset');
$foo = & $config['charset'];
$this->fmtDate = isset($config['formatDate']) ? $config['formatDate'] : 'U';
$this->fmtDateTime = isset($config['formatDateTime']) ? $config['formatDateTime'] : 'U';
if (isset($config['resource'])) {
$this->connection = $config['resource'];
@@ -95,25 +96,25 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->resultSet = oci_parse($this->connection, $sql);
if ($this->resultSet) {
oci_execute($this->resultSet, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($this->resultSet);
$res = oci_parse($this->connection, $sql);
if ($res) {
oci_execute($res, $this->autocommit ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT);
$err = oci_error($res);
if ($err) {
throw new DibiDriverException($err['message'], $err['code'], $sql);
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
} else {
$err = oci_error($this->connection);
throw new DibiDriverException($err['message'], $err['code'], $sql);
}
return is_resource($this->resultSet) ? clone $this : NULL;
}
@@ -124,7 +125,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getAffectedRows()
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -135,7 +136,8 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getInsertId($sequence)
{
throw new NotSupportedException('Oracle does not support autoincrementing.');
$row = $this->query("SELECT $sequence.CURRVAL AS ID FROM DUAL")->fetch(TRUE);
return isset($row['ID']) ? (int) $row['ID'] : FALSE;
}
@@ -192,7 +194,32 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -203,7 +230,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -217,17 +244,16 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
case dibi::IDENTIFIER:
// @see http://download.oracle.com/docs/cd/B10500_01/server.920/a96540/sql_elements9a.htm
$value = str_replace('"', '""', $value);
return '"' . str_replace('.', '"."', $value) . '"';
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date("U", $value);
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return date("U", $value);
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
@@ -236,6 +262,21 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes(str_replace('\\', '\\\\', $value), "\x00\\%_");
$value = str_replace("'", "''", $value);
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
@@ -277,13 +318,24 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->getResultResource() && $this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
public function getRowCount()
{
throw new NotSupportedException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
@@ -292,7 +344,6 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -308,7 +359,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function seek($row)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -329,19 +380,19 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = oci_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 1; $i <= $count; $i++) {
$res[] = array(
'name' => oci_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => oci_field_name($this->resultSet, $i),
$columns[] = array(
'name' => oci_field_name($this->resultSet, $i),
'table' => NULL,
'fullname' => oci_field_name($this->resultSet, $i),
'nativetype'=> oci_field_type($this->resultSet, $i),
);
}
return $res;
return $columns;
}
@@ -352,12 +403,13 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* reflection ****************d*g**/
/********************* IDibiReflector ****************d*g**/
@@ -367,18 +419,17 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getTables()
{
$this->query('SELECT * FROM cat');
$res = array();
while ($row = $this->fetch(FALSE)) {
$res = $this->query('SELECT * FROM cat');
$tables = array();
while ($row = $res->fetch(FALSE)) {
if ($row[1] === 'TABLE' || $row[1] === 'VIEW') {
$res[] = array(
$tables[] = array(
'name' => $row[0],
'view' => $row[1] === 'VIEW',
);
}
}
$this->free();
return $res;
return $tables;
}
@@ -390,7 +441,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getColumns($table)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -402,7 +453,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getIndexes($table)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
@@ -414,7 +465,7 @@ class DibiOracleDriver extends DibiObject implements IDibiDriver
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
}

View File

@@ -1,39 +1,34 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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.
*
* Connection options:
* - 'dsn' - driver specific DSN
* - 'username' (or 'user')
* - 'password' (or 'pass')
* - 'options' - driver specific options array
* - 'resource' - PDO object (optional)
* - 'lazy' - if TRUE, connection will be established only when required
* 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
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiPdoDriver extends DibiObject implements IDibiDriver
class DibiPdoDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var PDO Connection resource */
private $connection;
@@ -44,15 +39,18 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var string */
private $driverName;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pdo')) {
throw new DibiDriverException("PHP extension 'pdo' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'pdo' is not loaded.");
}
}
@@ -65,9 +63,9 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
*/
public function connect(array &$config)
{
DibiConnection::alias($config, 'dsn');
$foo = & $config['dsn'];
$foo = & $config['options'];
DibiConnection::alias($config, 'resource', 'pdo');
DibiConnection::alias($config, 'options');
if ($config['resource'] instanceof PDO) {
$this->connection = $config['resource'];
@@ -82,6 +80,8 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
if (!$this->connection) {
throw new DibiDriverException('Connecting error.');
}
$this->driverName = $this->connection->getAttribute(PDO::ATTR_DRIVER_NAME);
}
@@ -100,17 +100,17 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @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));
$list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
static $list = array('UPDATE'=>1, 'DELETE'=>1, 'INSERT'=>1, 'REPLAC'=>1);
$this->affectedRows = FALSE;
if (isset($list[$cmd])) {
$this->resultSet = NULL;
$this->affectedRows = $this->connection->exec($sql);
if ($this->affectedRows === FALSE) {
@@ -118,18 +118,15 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
}
return NULL;
} else {
$this->resultSet = $this->connection->query($sql);
$this->affectedRows = FALSE;
$res = $this->connection->query($sql);
if ($this->resultSet === FALSE) {
if ($res === FALSE) {
$err = $this->connection->errorInfo();
throw new DibiDriverException("SQLSTATE[$err[0]]: $err[2]", $err[1], $sql);
} else {
return $this->createResultDriver($res);
}
return clone $this;
}
}
@@ -216,13 +213,48 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
/**
* 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 string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -237,27 +269,21 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
return $this->connection->quote($value, PDO::PARAM_LOB);
case dibi::IDENTIFIER:
switch ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME)) {
switch ($this->driverName) {
case 'mysql':
$value = str_replace('`', '``', $value);
return '`' . str_replace('.', '`.`', $value) . '`';
return '`' . str_replace('`', '``', $value) . '`';
case 'pgsql':
$a = strrpos($value, '.');
if ($a === FALSE) {
return '"' . str_replace('"', '""', $value) . '"';
} else {
return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"';
}
return '"' . str_replace('"', '""', $value) . '"';
case 'sqlite':
case 'sqlite2':
$value = strtr($value, '[]', ' ');
return '[' . strtr($value, '[]', ' ') . ']';
case 'odbc':
case 'oci': // TODO: not tested
case 'mssql':
$value = str_replace(array('[', ']'), array('[[', ']]'), $value);
return '[' . str_replace('.', '].[', $value) . ']';
return '[' . str_replace(array('[', ']'), array('[[', ']]'), $value) . ']';
default:
return $value;
@@ -267,10 +293,10 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
return $this->connection->quote($value, PDO::PARAM_BOOL);
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -279,6 +305,19 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -307,7 +346,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
{
if ($limit < 0 && $offset < 1) return;
switch ($this->connection->getAttribute(PDO::ATTR_DRIVER_NAME)) {
switch ($this->driverName) {
case 'mysql':
$sql .= ' LIMIT ' . ($limit < 0 ? '18446744073709551615' : (int) $limit)
. ($offset > 0 ? ' OFFSET ' . (int) $offset : '');
@@ -340,7 +379,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
// intentionally break omitted
default:
throw new NotSupportedException('PDO or driver does not support applying limit or offset.');
throw new DibiNotSupportedException('PDO or driver does not support applying limit or offset.');
}
}
@@ -356,7 +395,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
*/
public function getRowCount()
{
throw new NotSupportedException('Row count is not available for unbuffered queries.');
return $this->resultSet->rowCount();
}
@@ -365,7 +404,6 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -381,7 +419,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
*/
public function seek($row)
{
throw new NotSupportedException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
@@ -402,16 +440,20 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
* @return array
* @throws DibiException
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = $this->resultSet->columnCount();
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = @$this->resultSet->getColumnMeta($i); // intentionally @
if ($row === FALSE) {
throw new DibiDriverException('Driver does not support meta data.');
throw new DibiNotSupportedException('Driver does not support meta data.');
}
$res[] = array(
// 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'],
@@ -419,7 +461,7 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
'vendor' => $row,
);
}
return $res;
return $columns;
}
@@ -433,55 +475,4 @@ class DibiPdoDriver extends DibiObject implements IDibiDriver
return $this->resultSet;
}
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
throw new NotImplementedException;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
}
}

View File

@@ -1,40 +1,31 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* The dibi driver for PostgreSQL database.
*
* Connection options:
* - 'host','hostaddr','port','dbname','user','password','connect_timeout','options','sslmode','service' - see PostgreSQL API
* - 'string' - or use connection string
* - 'persistent' - try to find a persistent link?
* - 'charset' - character encoding to set
* - 'schema' - the schema search path
* - 'lazy' - if TRUE, connection will be established only when required
* - 'resource' - connection resource (optional)
* Driver options:
* - host, hostaddr, port, dbname, user, password, connect_timeout, options, sslmode, service => see PostgreSQL API
* - string => or use connection string
* - schema => the schema search path
* - charset => character encoding to set (default is utf8)
* - persistent (bool) => try to find a persistent link?
* - resource (resource) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiPostgreDriver extends DibiObject implements IDibiDriver
class DibiPostgreDriver extends DibiObject implements IDibiDriver, IDibiResultDriver, IDibiReflector
{
/** @var resource Connection resource */
private $connection;
@@ -42,18 +33,24 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/** @var resource Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var int|FALSE Affected rows */
private $affectedRows = FALSE;
/** @var bool Escape method */
private $escMethod = FALSE;
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('pgsql')) {
throw new DibiDriverException("PHP extension 'pgsql' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'pgsql' is not loaded.");
}
}
@@ -70,10 +67,13 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
$this->connection = $config['resource'];
} else {
if (!isset($config['charset'])) $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] . ' ';
}
@@ -125,19 +125,23 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @param bool update affected rows?
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
$this->resultSet = @pg_query($this->connection, $sql); // intentionally @
$this->affectedRows = FALSE;
$res = @pg_query($this->connection, $sql); // intentionally @
if ($this->resultSet === FALSE) {
if ($res === FALSE) {
throw new DibiDriverException(pg_last_error($this->connection), 0, $sql);
}
return is_resource($this->resultSet) && pg_num_fields($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
$this->affectedRows = pg_affected_rows($res);
if (pg_num_fields($res)) {
return $this->createResultDriver($res);
}
}
}
@@ -148,7 +152,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
*/
public function getAffectedRows()
{
return pg_affected_rows($this->resultSet);
return $this->affectedRows;
}
@@ -161,15 +165,14 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
{
if ($sequence === NULL) {
// PostgreSQL 8.1 is needed
$has = $this->query("SELECT LASTVAL()");
$res = $this->query("SELECT LASTVAL()");
} else {
$has = $this->query("SELECT CURRVAL('$sequence')");
$res = $this->query("SELECT CURRVAL('$sequence')");
}
if (!$has) return FALSE;
if (!$res) return FALSE;
$row = $this->fetch(FALSE);
$this->free();
$row = $res->fetch(FALSE);
return is_array($row) ? $row[0] : FALSE;
}
@@ -214,13 +217,49 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/**
* Is in transaction?
* @return bool
*/
public function inTransaction()
{
return !in_array(pg_transaction_status($this->connection), array(PGSQL_TRANSACTION_UNKNOWN, PGSQL_TRANSACTION_IDLE), TRUE);
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return $this->connection;
return is_resource($this->connection) ? $this->connection : NULL;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return $this;
}
/**
* Result set driver factory.
* @param resource
* @return IDibiResultDriver
*/
public function createResultDriver($resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
@@ -231,7 +270,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -241,6 +280,9 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
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) . "'";
@@ -248,6 +290,9 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
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) . "'";
@@ -255,22 +300,16 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
case dibi::IDENTIFIER:
// @see http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
$a = strrpos($value, '.');
if ($a === FALSE) {
return '"' . str_replace('"', '""', $value) . '"';
} else {
// table.col delimite as table."col"
return substr($value, 0, $a) . '."' . str_replace('"', '""', substr($value, $a + 1)) . '"';
}
return '"' . str_replace('"', '""', $value) . '"';
case dibi::BOOL:
return $value ? 'TRUE' : 'FALSE';
case dibi::DATE:
return date("'Y-m-d'", $value);
return $value instanceof DateTime ? $value->format("'Y-m-d'") : date("'Y-m-d'", $value);
case dibi::DATETIME:
return date("'Y-m-d H:i:s'", $value);
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.');
@@ -279,6 +318,26 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
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( '%' => '\\\\%', '_' => '\\\\_'));
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'");
}
/**
* Decodes data from result set.
* @param string value
@@ -318,6 +377,17 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -333,7 +403,6 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -370,11 +439,11 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$hasTable = version_compare(PHP_VERSION , '5.2.0', '>=');
$count = pg_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$row = array(
'name' => pg_field_name($this->resultSet, $i),
@@ -382,9 +451,9 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
'nativetype'=> pg_field_type($this->resultSet, $i),
);
$row['fullname'] = $row['table'] ? $row['table'] . '.' . $row['name'] : $row['name'];
$res[] = $row;
$columns[] = $row;
}
return $res;
return $columns;
}
@@ -395,12 +464,13 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
$this->autoFree = FALSE;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}
/********************* reflection ****************d*g**/
/********************* IDibiReflector ****************d*g**/
@@ -410,19 +480,25 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
*/
public function getTables()
{
$version = pg_version($this->connection);
if ($version['server'] < 8) {
throw new DibiDriverException('Reflection requires PostgreSQL 8.');
$version = pg_parameter_status($this->resource, 'server_version');
if ($version < 7.4) {
throw new DibiDriverException('Reflection requires PostgreSQL 7.4 and newer.');
}
$this->query("
SELECT table_name as name, CAST(table_type = 'VIEW' AS INTEGER) as view
FROM information_schema.tables
WHERE table_schema = current_schema()
$res = $this->query("
SELECT
table_name AS name,
CASE table_type
WHEN 'VIEW' THEN 1
ELSE 0
END AS view
FROM
information_schema.tables
WHERE
table_schema = current_schema()
");
$res = pg_fetch_all($this->resultSet);
$this->free();
return $res;
$tables = pg_fetch_all($res->resultSet);
return $tables ? $tables : array();
}
@@ -435,24 +511,24 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
public function getColumns($table)
{
$_table = $this->escape($table, dibi::TEXT);
$this->query("
$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
");
$primary = (int) pg_fetch_object($this->resultSet)->indkey;
$primary = (int) pg_fetch_object($res->resultSet)->indkey;
$this->query("
$res = $this->query("
SELECT *
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
ORDER BY ordinal_position
");
$res = array();
while ($row = $this->fetch(TRUE)) {
$columns = array();
while ($row = $res->fetch(TRUE)) {
$size = (int) max($row['character_maximum_length'], $row['numeric_precision']);
$res[] = array(
$columns[] = array(
'name' => $row['column_name'],
'table' => $table,
'nativetype' => strtoupper($row['udt_name']),
@@ -463,8 +539,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
'vendor' => $row,
);
}
$this->free();
return $res;
return $columns;
}
@@ -477,7 +552,7 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
public function getIndexes($table)
{
$_table = $this->escape($table, dibi::TEXT);
$this->query("
$res = $this->query("
SELECT ordinal_position, column_name
FROM information_schema.columns
WHERE table_name = $_table AND table_schema = current_schema()
@@ -485,11 +560,11 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
");
$columns = array();
while ($row = $this->fetch(TRUE)) {
while ($row = $res->fetch(TRUE)) {
$columns[$row['ordinal_position']] = $row['column_name'];
}
$this->query("
$res = $this->query("
SELECT pg_class2.relname, indisunique, indisprimary, indkey
FROM pg_class
LEFT JOIN pg_index on pg_class.oid = pg_index.indrelid
@@ -497,17 +572,16 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
WHERE pg_class.relname = $_table
");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[$row['relname']]['name'] = $row['relname'];
$res[$row['relname']]['unique'] = $row['indisunique'] === 't';
$res[$row['relname']]['primary'] = $row['indisprimary'] === 't';
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['relname']]['name'] = $row['relname'];
$indexes[$row['relname']]['unique'] = $row['indisunique'] === 't';
$indexes[$row['relname']]['primary'] = $row['indisprimary'] === 't';
foreach (explode(' ', $row['indkey']) as $index) {
$res[$row['relname']]['columns'][] = $columns[$index];
$indexes[$row['relname']]['columns'][] = $columns[$index];
}
}
$this->free();
return array_values($res);
return array_values($indexes);
}
@@ -519,7 +593,72 @@ class DibiPostgreDriver extends DibiObject implements IDibiDriver
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
$_table = $this->escape($table, dibi::TEXT);
$res = $this->query("
SELECT
c.conname AS name,
lt.attname AS local,
c.confrelid::regclass AS table,
ft.attname AS foreign,
CASE c.confupdtype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onUpdate\",
CASE c.confdeltype
WHEN 'a' THEN 'NO ACTION'
WHEN 'r' THEN 'RESTRICT'
WHEN 'c' THEN 'CASCADE'
WHEN 'n' THEN 'SET NULL'
WHEN 'd' THEN 'SET DEFAULT'
ELSE 'UNKNOWN'
END AS \"onDelete\",
c.conkey,
lt.attnum AS lnum,
c.confkey,
ft.attnum AS fnum
FROM
pg_constraint c
JOIN pg_attribute lt ON c.conrelid = lt.attrelid AND lt.attnum = ANY (c.conkey)
JOIN pg_attribute ft ON c.confrelid = ft.attrelid AND ft.attnum = ANY (c.confkey)
WHERE
c.contype = 'f'
AND
c.conrelid = $_table::regclass
");
$fKeys = $references = array();
while ($row = $res->fetch(TRUE)) {
if (!isset($fKeys[$row['name']])) {
$fKeys[$row['name']] = array(
'name' => $row['name'],
'table' => $row['table'],
'local' => array(),
'foreign' => array(),
'onUpdate' => $row['onUpdate'],
'onDelete' => $row['onDelete'],
);
$l = explode(',', trim($row['conkey'], '{}'));
$f = explode(',', trim($row['confkey'], '{}'));
$references[$row['name']] = array_combine($l, $f);
}
if (isset($references[$row['name']][$row['lnum']]) && $references[$row['name']][$row['lnum']] === $row['fnum']) {
$fKeys[$row['name']]['local'][] = $row['local'];
$fKeys[$row['name']]['foreign'][] = $row['foreign'];
}
}
return $fKeys;
}
}

View File

@@ -1,42 +1,36 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* 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.
*
* Connection options:
* - 'database' (or 'file') - the filename of the SQLite database
* - 'persistent' - try to find a persistent link?
* - 'unbuffered' - sends query without fetching and buffering the result rows automatically?
* - 'lazy' - if TRUE, connection will be established only when required
* - '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' - connection resource (optional)
* 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
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\drivers
*/
class DibiSqliteDriver extends DibiObject implements IDibiDriver
class DibiSqliteDriver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var resource Connection resource */
private $connection;
@@ -56,12 +50,12 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
/**
* @throws DibiException
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite')) {
throw new DibiDriverException("PHP extension 'sqlite' is not loaded.");
throw new DibiNotSupportedException("PHP extension 'sqlite' is not loaded.");
}
}
@@ -116,7 +110,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
@@ -127,15 +121,16 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
DibiDriverException::tryError();
if ($this->buffered) {
$this->resultSet = sqlite_query($this->connection, $sql);
$res = sqlite_query($this->connection, $sql);
} else {
$this->resultSet = sqlite_unbuffered_query($this->connection, $sql);
$res = sqlite_unbuffered_query($this->connection, $sql);
}
if (DibiDriverException::catchError($msg)) {
throw new DibiDriverException($msg, sqlite_last_error($this->connection), $sql);
}
return is_resource($this->resultSet) ? clone $this : NULL;
} elseif (is_resource($res)) {
return $this->createResultDriver($res);
}
}
@@ -207,7 +202,32 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
*/
public function getResource()
{
return $this->connection;
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;
}
@@ -218,7 +238,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
/**
* Encodes data for use in a SQL statement.
* @param string value
* @param mixed value
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string encoded value
* @throws InvalidArgumentException
@@ -230,20 +250,17 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
case dibi::BINARY:
return "'" . sqlite_escape_string($value) . "'";
/*case dibi::BINARY: // SQLite 3
return "X'" . bin2hex((string) $value) . "'";*/
case dibi::IDENTIFIER:
return '[' . str_replace('.', '].[', strtr($value, '[]', ' ')) . ']';
return '[' . strtr($value, '[]', ' ') . ']';
case dibi::BOOL:
return $value ? 1 : 0;
case dibi::DATE:
return date($this->fmtDate, $value);
return $value instanceof DateTime ? $value->format($this->fmtDate) : date($this->fmtDate, $value);
case dibi::DATETIME:
return date($this->fmtDateTime, $value);
return $value instanceof DateTime ? $value->format($this->fmtDateTime) : date($this->fmtDateTime, $value);
default:
throw new InvalidArgumentException('Unsupported type.');
@@ -252,6 +269,19 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
/**
* 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
@@ -295,7 +325,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
public function getRowCount()
{
if (!$this->buffered) {
throw new DibiDriverException('Row count is not available for unbuffered queries.');
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
return sqlite_num_rows($this->resultSet);
}
@@ -306,7 +336,6 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
* @internal
*/
public function fetch($assoc)
{
@@ -336,7 +365,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
public function seek($row)
{
if (!$this->buffered) {
throw new DibiDriverException('Cannot seek an unbuffered result set.');
throw new DibiNotSupportedException('Cannot seek an unbuffered result set.');
}
return sqlite_seek($this->resultSet, $row);
}
@@ -358,21 +387,21 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
* Returns metadata for all columns in a result set.
* @return array
*/
public function getColumnsMeta()
public function getResultColumns()
{
$count = sqlite_num_fields($this->resultSet);
$res = array();
$columns = array();
for ($i = 0; $i < $count; $i++) {
$name = str_replace(array('[', ']'), '', sqlite_field_name($this->resultSet, $i));
$pair = explode('.', $name);
$res[] = array(
$columns[] = array(
'name' => isset($pair[1]) ? $pair[1] : $pair[0],
'table' => isset($pair[1]) ? $pair[0] : NULL,
'fullname' => $name,
'nativetype' => NULL,
);
}
return $res;
return $columns;
}
@@ -383,69 +412,7 @@ class DibiSqliteDriver extends DibiObject implements IDibiDriver
*/
public function getResultResource()
{
return $this->resultSet;
}
/********************* reflection ****************d*g**/
/**
* Returns list of tables.
* @return array
*/
public function getTables()
{
$this->query("
SELECT name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
UNION ALL
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$res = array();
while ($row = $this->fetch(TRUE)) {
$res[] = $row;
}
$this->free();
return $res;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
throw new NotImplementedException;
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
public function getForeignKeys($table)
{
throw new NotImplementedException;
return is_resource($this->resultSet) ? $this->resultSet : NULL;
}

View File

@@ -0,0 +1,170 @@
<?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 SQLite database.
*
* @author David Grudl
* @package dibi\drivers
* @internal
*/
class DibiSqliteReflector 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 name, type = 'view' as view FROM sqlite_master WHERE type IN ('table', 'view')
UNION ALL
SELECT name, type = 'view' as view FROM sqlite_temp_master WHERE type IN ('table', 'view')
ORDER BY name
");
$tables = array();
while ($row = $res->fetch(TRUE)) {
$tables[] = $row;
}
return $tables;
}
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
*/
public function getColumns($table)
{
$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();
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(
'name' => $column,
'table' => $table,
'fullname' => "$table.$column",
'nativetype' => strtoupper($type[0]),
'size' => isset($type[1]) ? (int) $type[1] : NULL,
'nullable' => $row['notnull'] == '0',
'default' => $row['dflt_value'],
'autoincrement' => (bool) preg_match($pattern, $meta['sql']),
'vendor' => $row,
);
}
return $columns;
}
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
*/
public function getIndexes($table)
{
$res = $this->driver->query("PRAGMA index_list([$table])");
$indexes = array();
while ($row = $res->fetch(TRUE)) {
$indexes[$row['name']]['name'] = $row['name'];
$indexes[$row['name']]['unique'] = (bool) $row['unique'];
}
foreach ($indexes as $index => $values) {
$res = $this->driver->query("PRAGMA index_info([$index])");
while ($row = $res->fetch(TRUE)) {
$indexes[$index]['columns'][$row['seqno']] = $row['name'];
}
}
$columns = $this->getColumns($table);
foreach ($indexes as $index => $values) {
$column = $indexes[$index]['columns'][0];
$primary = FALSE;
foreach ($columns as $info) {
if ($column == $info['name']) {
$primary = $info['vendor']['pk'];
break;
}
}
$indexes[$index]['primary'] = (bool) $primary;
}
if (!$indexes) { // @see http://www.sqlite.org/lang_createtable.html#rowid
foreach ($columns as $column) {
if ($column['vendor']['pk']) {
$indexes[] = array(
'name' => 'ROWID',
'unique' => TRUE,
'primary' => TRUE,
'columns' => array($column['name']),
);
break;
}
}
}
return array_values($indexes);
}
/**
* Returns metadata for all foreign keys in a table.
* @param string
* @return array
*/
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();
while ($row = $res->fetch(TRUE)) {
$keys[$row['id']]['name'] = $row['id']; // foreign key name
$keys[$row['id']]['local'][$row['seq']] = $row['from']; // local columns
$keys[$row['id']]['table'] = $row['table']; // referenced table
$keys[$row['id']]['foreign'][$row['seq']] = $row['to']; // referenced columns
$keys[$row['id']]['onDelete'] = $row['on_delete'];
$keys[$row['id']]['onUpdate'] = $row['on_update'];
if ($keys[$row['id']]['foreign'][0] == NULL) {
$keys[$row['id']]['foreign'] = NULL;
}
}
return array_values($keys);
}
}

454
dibi/drivers/sqlite3.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 (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 SQLite3 database.
*
* Driver options:
* - database (or file) => the filename of the SQLite3 database
* - formatDate => how to format date in SQL (@see date)
* - formatDateTime => how to format datetime in SQL (@see date)
* - dbcharset => database character encoding (will be converted to 'charset')
* - charset => character encoding to set (default is UTF-8)
* - resource (SQLite3) => existing connection resource
* - lazy, profiler, result, substitutes, ... => see DibiConnection options
*
* @author David Grudl
* @package dibi\drivers
*/
class DibiSqlite3Driver extends DibiObject implements IDibiDriver, IDibiResultDriver
{
/** @var SQLite3 Connection resource */
private $connection;
/** @var SQLite3Result Resultset resource */
private $resultSet;
/** @var bool */
private $autoFree = TRUE;
/** @var string Date and datetime format */
private $fmtDate, $fmtDateTime;
/** @var string character encoding */
private $dbcharset, $charset;
/**
* @throws DibiNotSupportedException
*/
public function __construct()
{
if (!extension_loaded('sqlite3')) {
throw new DibiNotSupportedException("PHP extension 'sqlite3' 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';
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());
}
$this->dbcharset = empty($config['dbcharset']) ? 'UTF-8' : $config['dbcharset'];
$this->charset = empty($config['charset']) ? 'UTF-8' : $config['charset'];
if (strcasecmp($this->dbcharset, $this->charset) === 0) {
$this->dbcharset = $this->charset = NULL;
}
// enable foreign keys support (defaultly disabled; if disabled then foreign key constraints are not enforced)
$version = SQLite3::version();
if ($version['versionNumber'] >= '3006019') {
$this->query("PRAGMA foreign_keys = ON");
}
}
/**
* Disconnects from a database.
* @return void
*/
public function disconnect()
{
$this->connection->close();
}
/**
* Executes the SQL query.
* @param string SQL statement.
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
public function query($sql)
{
if ($this->dbcharset !== NULL) {
$sql = iconv($this->charset, $this->dbcharset . '//IGNORE', $sql);
}
$res = @$this->connection->query($sql); // intentionally @
if ($this->connection->lastErrorCode()) {
throw new DibiDriverException($this->connection->lastErrorMsg(), $this->connection->lastErrorCode(), $sql);
} elseif ($res instanceof SQLite3Result) {
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->connection->changes();
}
/**
* Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.
* @return int|FALSE int on success or FALSE on failure
*/
public function getInsertId($sequence)
{
return $this->connection->lastInsertRowID();
}
/**
* Begins a transaction (if supported).
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function begin($savepoint = NULL)
{
$this->query($savepoint ? "SAVEPOINT $savepoint" : 'BEGIN');
}
/**
* Commits statements in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function commit($savepoint = NULL)
{
$this->query($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
}
/**
* Rollback changes in a transaction.
* @param string optional savepoint name
* @return void
* @throws DibiDriverException
*/
public function rollback($savepoint = NULL)
{
$this->query($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
}
/**
* Returns the connection resource.
* @return mixed
*/
public function getResource()
{
return $this->connection;
}
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
public function getReflector()
{
return new DibiSqliteReflector($this);
}
/**
* Result set driver factory.
* @param SQLite3Result
* @return IDibiResultDriver
*/
public function createResultDriver(SQLite3Result $resource)
{
$res = clone $this;
$res->resultSet = $resource;
return $res;
}
/********************* SQL ****************d*g**/
/**
* Encodes data for use in a SQL statement.
* @param mixed value
* @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->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.');
}
}
/**
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
public function escapeLike($value, $pos)
{
$value = addcslashes($this->connection->escapeString($value), '%_\\');
return ($pos <= 0 ? "'%" : "'") . $value . ($pos >= 0 ? "%'" : "'") . " ESCAPE '\\'";
}
/**
* Decodes data from result set.
* @param string 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**/
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
$this->autoFree && $this->resultSet && @$this->free();
}
/**
* Returns the number of rows in a result set.
* @return int
* @throws DibiNotSupportedException
*/
public function getRowCount()
{
throw new DibiNotSupportedException('Row count is not available for unbuffered queries.');
}
/**
* Fetches the row at current position and moves the internal cursor to the next position.
* @param bool TRUE for associative array, FALSE for numeric
* @return array array on success, nonarray if no next record
*/
public function fetch($assoc)
{
$row = $this->resultSet->fetchArray($assoc ? SQLITE3_ASSOC : SQLITE3_NUM);
$charset = $this->charset === NULL ? NULL : $this->charset . '//TRANSLIT';
if ($row && ($assoc || $charset)) {
$tmp = 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 DibiNotSupportedException
*/
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->finalize();
$this->resultSet = NULL;
}
/**
* Returns metadata for all columns in a result set.
* @return array
*/
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');
for ($i = 0; $i < $count; $i++) {
$columns[] = array(
'name' => $this->resultSet->columnName($i),
'table' => NULL,
'fullname' => $this->resultSet->columnName($i),
'nativetype' => $types[$this->resultSet->columnType($i)],
);
}
return $columns;
}
/**
* Returns the result set resource.
* @return mixed
*/
public function getResultResource()
{
$this->autoFree = FALSE;
return $this->resultSet;
}
/********************* user defined functions ****************d*g**/
/**
* Registers an user defined function for use in SQL statements.
* @param string function name
* @param mixed callback
* @param int num of arguments
* @return void
*/
public function registerFunction($name, $callback, $numArgs = -1)
{
$this->connection->createFunction($name, $callback, $numArgs);
}
/**
* Registers an aggregating user defined function for use in SQL statements.
* @param string function name
* @param mixed callback called for each row of the result set
* @param mixed callback called to aggregate the "stepped" data from each row
* @param int num of arguments
* @return void
*/
public function registerAggregateFunction($name, $rowCallback, $agrCallback, $numArgs = -1)
{
$this->connection->createAggregate($name, $rowCallback, $agrCallback, $numArgs);
}
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,60 +15,81 @@
* dibi connection.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 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
{
/** @var array of function(DibiEvent $event); Occurs after query is executed */
public $onEvent;
/** @var array Current connection configuration */
private $config;
/** @var IDibiDriver Driver */
/** @var IDibiDriver */
private $driver;
/** @var IDibiProfiler Profiler */
private $profiler;
/** @var DibiTranslator */
private $translator;
/** @var bool Is connected? */
private $connected = FALSE;
/** @var bool Is in transaction? */
private $inTxn = FALSE;
/** @var DibiHashMap Substitutes for identifiers */
private $substitutes;
/**
* Creates object and (optionally) connects to a database.
* @param array|string|ArrayObject connection parameters
* @param string connection name
* Connection options: (see driver-specific options too)
* - lazy (bool) => if TRUE, connection will be established only when required
* - result (array) => result set options
* - formatDateTime => date-time format (if empty, DateTime objects will be returned)
* - profiler (array or bool)
* - run (bool) => enable profiler?
* - file => file to log
* - substitutes (array) => map of driver specific substitutes (under development)
* @param mixed connection parameters
* @param string connection name
* @throws DibiException
*/
public function __construct($config, $name = NULL)
{
if (class_exists(/*Nette\*/'Debug', FALSE)) {
/*Nette\*/Debug::addColophon(array('dibi', 'getColophon'));
}
class_exists('dibi'); // ensure class dibi is loaded
// DSN string
if (is_string($config)) {
parse_str($config, $config);
} elseif ($config instanceof ArrayObject) {
$config = (array) $config;
} elseif ($config instanceof Traversable) {
$tmp = array();
foreach ($config as $key => $val) {
$tmp[$key] = $val instanceof Traversable ? iterator_to_array($val) : $val;
}
$config = $tmp;
} elseif (!is_array($config)) {
throw new InvalidArgumentException('Configuration must be array, string or ArrayObject.');
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');
if (!isset($config['driver'])) {
$config['driver'] = dibi::$defaultDriver;
}
$driver = preg_replace('#[^a-z0-9_]#', '_', $config['driver']);
$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";
@@ -89,21 +102,34 @@ class DibiConnection extends DibiObject
$config['name'] = $name;
$this->config = $config;
$this->driver = new $class;
$this->translator = new DibiTranslator($this);
if (!empty($config['profiler'])) {
$class = $config['profiler'];
if (is_numeric($class) || is_bool($class)) {
$class = 'DibiProfiler';
// profiler
$profilerCfg = & $config['profiler'];
if (is_scalar($profilerCfg)) {
$profilerCfg = array('run' => (bool) $profilerCfg);
}
if (!empty($profilerCfg['run'])) {
$filter = isset($profilerCfg['filter']) ? $profilerCfg['filter'] : DibiEvent::QUERY;
if (isset($profilerCfg['file'])) {
$this->onEvent[] = array(new DibiFileLogger($profilerCfg['file'], $filter), 'logEvent');
}
if (!class_exists($class)) {
throw new DibiException("Unable to create instance of dibi profiler '$class'.");
if (DibiFirePhpLogger::isAvailable()) {
$this->onEvent[] = array(new DibiFirePhpLogger($filter), 'logEvent');
}
if (class_exists('DibiNettePanel', FALSE)) {
$panel = new DibiNettePanel(isset($profilerCfg['explain']) ? $profilerCfg['explain'] : TRUE, $filter);
$panel->register($this);
}
$this->setProfiler(new $class);
}
$this->substitutes = new DibiHashMap(create_function('$expr', 'return ":$expr:";'));
if (!empty($config['substitutes'])) {
foreach ($config['substitutes'] as $key => $value) {
dibi::addSubst($key, $value);
$this->substitutes->$key = $value;
}
}
@@ -121,7 +147,7 @@ class DibiConnection extends DibiObject
public function __destruct()
{
// disconnects and rolls back transaction - do not rely on auto-disconnect and rollback!
$this->disconnect();
$this->connected && $this->driver->getResource() && $this->disconnect();
}
@@ -130,17 +156,17 @@ class DibiConnection extends DibiObject
* Connects to a database.
* @return void
*/
final protected function connect()
final public function connect()
{
if (!$this->connected) {
if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::CONNECT);
}
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::CONNECT) : NULL;
try {
$this->driver->connect($this->config);
$this->connected = TRUE;
if (isset($ticket)) {
$this->profiler->after($ticket);
}
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
@@ -152,13 +178,8 @@ class DibiConnection extends DibiObject
*/
final public function disconnect()
{
if ($this->connected) {
if ($this->inTxn) {
$this->rollback();
}
$this->driver->disconnect();
$this->connected = FALSE;
}
$this->driver->disconnect();
$this->connected = FALSE;
}
@@ -203,44 +224,31 @@ class DibiConnection extends DibiObject
* @param string alias key
* @return void
*/
public static function alias(&$config, $key, $alias=NULL)
public static function alias(&$config, $key, $alias)
{
if (isset($config[$key])) return;
$foo = & $config;
foreach (explode('|', $key) as $key) $foo = & $foo[$key];
if ($alias !== NULL && isset($config[$alias])) {
$config[$key] = $config[$alias];
if (!isset($foo) && isset($config[$alias])) {
$foo = $config[$alias];
unset($config[$alias]);
} else {
$config[$key] = NULL;
}
}
/**
* Returns the connection resource.
* Returns the driver and connects to a database in lazy mode.
* @return IDibiDriver
*/
final public function getDriver()
{
$this->connected || $this->connect();
return $this->driver;
}
/**
* Returns the connection resource.
* @return resource
* @deprecated use getDriver()->getResource()
*/
final public function getResource()
{
trigger_error('Deprecated: use getDriver()->getResource(...) instead.', E_USER_WARNING);
return $this->driver->getResource();
}
/**
* Generates (translates) and executes SQL query.
* @param array|mixed one or more arguments
@@ -250,25 +258,21 @@ class DibiConnection extends DibiObject
final public function query($args)
{
$args = func_get_args();
$this->connect();
$translator = new DibiTranslator($this->driver);
return $this->nativeQuery($translator->translate($args));
return $this->nativeQuery($this->translateArgs($args));
}
/**
* Generates and returns SQL query.
* Generates SQL query.
* @param array|mixed one or more arguments
* @return string
* @throws DibiException
*/
final public function sql($args)
final public function translate($args)
{
$args = func_get_args();
$this->connect();
$translator = new DibiTranslator($this->driver);
return $translator->translate($args);
return $this->translateArgs($args);
}
@@ -281,14 +285,16 @@ class DibiConnection extends DibiObject
final public function test($args)
{
$args = func_get_args();
$this->connect();
try {
$translator = new DibiTranslator($this->driver);
dibi::dump($translator->translate($args));
dibi::dump($this->translateArgs($args));
return TRUE;
} catch (DibiException $e) {
dibi::dump($e->getSql());
if ($e->getSql()) {
dibi::dump($e->getSql());
} else {
echo get_class($e) . ': ' . $e->getMessage() . (PHP_SAPI === 'cli' ? "\n" : '<br>');
}
return FALSE;
}
}
@@ -304,9 +310,20 @@ class DibiConnection extends DibiObject
final public function dataSource($args)
{
$args = func_get_args();
$this->connect();
$translator = new DibiTranslator($this->driver);
return new DibiDataSource($translator->translate($args), $this);
return new DibiDataSource($this->translateArgs($args), $this);
}
/**
* Generates SQL query.
* @param array
* @return string
*/
private function translateArgs($args)
{
$this->connected || $this->connect();
return $this->translator->translate($args);
}
@@ -319,38 +336,26 @@ class DibiConnection extends DibiObject
*/
final public function nativeQuery($sql)
{
$this->connect();
$this->connected || $this->connect();
if ($this->profiler !== NULL) {
$event = IDibiProfiler::QUERY;
if (preg_match('#\s*(SELECT|UPDATE|INSERT|DELETE)#i', $sql, $matches)) {
static $events = array(
'SELECT' => IDibiProfiler::SELECT, 'UPDATE' => IDibiProfiler::UPDATE,
'INSERT' => IDibiProfiler::INSERT, 'DELETE' => IDibiProfiler::DELETE,
);
$event = $events[strtoupper($matches[1])];
}
$ticket = $this->profiler->before($this, $event, $sql);
}
// TODO: move to profiler?
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::QUERY, $sql) : NULL;
dibi::$numOfQueries++;
dibi::$sql = $sql;
dibi::$elapsedTime = FALSE;
$time = -microtime(TRUE);
try {
$res = $this->driver->query($sql);
if ($res = $this->driver->query($sql)) { // intentionally =
$res = new DibiResult($res, $this->config);
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
if ($res) {
$res = $this->createResultSet($res);
} else {
$res = $this->driver->getAffectedRows();
}
$time += microtime(TRUE);
dibi::$elapsedTime = $time;
dibi::$totalTime += $time;
if (isset($ticket)) {
$this->profiler->after($ticket, $res);
}
$event && $this->onEvent($event->done($res));
return $res;
}
@@ -363,6 +368,7 @@ class DibiConnection extends DibiObject
*/
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.');
return $rows;
@@ -390,6 +396,7 @@ class DibiConnection extends DibiObject
*/
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.');
return (int) $id;
@@ -417,21 +424,15 @@ class DibiConnection extends DibiObject
*/
public function begin($savepoint = NULL)
{
$this->connect();
if (!$savepoint && $this->inTxn) {
throw new DibiException('There is already an active transaction.');
}
if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::BEGIN, $savepoint);
}
if ($savepoint && !$this->inTxn) {
$this->driver->begin();
}
$this->driver->begin($savepoint);
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::BEGIN, $savepoint) : NULL;
try {
$this->driver->begin($savepoint);
$event && $this->onEvent($event->done());
$this->inTxn = TRUE;
if (isset($ticket)) {
$this->profiler->after($ticket);
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
@@ -444,16 +445,15 @@ class DibiConnection extends DibiObject
*/
public function commit($savepoint = NULL)
{
if (!$this->inTxn) {
throw new DibiException('There is no active transaction.');
}
if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::COMMIT, $savepoint);
}
$this->driver->commit($savepoint);
$this->inTxn = (bool) $savepoint;
if (isset($ticket)) {
$this->profiler->after($ticket);
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::COMMIT, $savepoint) : NULL;
try {
$this->driver->commit($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
@@ -466,89 +466,30 @@ class DibiConnection extends DibiObject
*/
public function rollback($savepoint = NULL)
{
if (!$this->inTxn) {
throw new DibiException('There is no active transaction.');
}
if ($this->profiler !== NULL) {
$ticket = $this->profiler->before($this, IDibiProfiler::ROLLBACK, $savepoint);
}
$this->driver->rollback($savepoint);
$this->inTxn = (bool) $savepoint;
if (isset($ticket)) {
$this->profiler->after($ticket);
$this->connected || $this->connect();
$event = $this->onEvent ? new DibiEvent($this, DibiEvent::ROLLBACK, $savepoint) : NULL;
try {
$this->driver->rollback($savepoint);
$event && $this->onEvent($event->done());
} catch (DibiException $e) {
$event && $this->onEvent($event->done($e));
throw $e;
}
}
/**
* Is in transaction?
* @return bool
* Result set factory.
* @param IDibiResultDriver
* @return DibiResult
*/
public function inTransaction()
public function createResultSet(IDibiResultDriver $resultDriver)
{
return $this->inTxn;
}
/**
* Encodes data for use in a SQL statement.
* @param string unescaped string
* @param string type (dibi::TEXT, dibi::BOOL, ...)
* @return string escaped and quoted string
* @deprecated
*/
public function escape($value, $type = dibi::TEXT)
{
trigger_error('Deprecated: use getDriver()->escape(...) instead.', E_USER_WARNING);
$this->connect(); // MySQL & PDO require connection
return $this->driver->escape($value, $type);
}
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @deprecated
*/
public function unescape($value, $type = dibi::BINARY)
{
trigger_error('Deprecated: use getDriver()->unescape(...) instead.', E_USER_WARNING);
return $this->driver->unescape($value, $type);
}
/**
* Delimites identifier (table's or column's name, etc.).
* @param string identifier
* @return string delimited identifier
* @deprecated
*/
public function delimite($value)
{
trigger_error('Deprecated: use getDriver()->escape(...) instead.', E_USER_WARNING);
return $this->driver->escape($value, dibi::IDENTIFIER);
}
/**
* 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
* @deprecated
*/
public function applyLimit(&$sql, $limit, $offset)
{
trigger_error('Deprecated: use getDriver()->applyLimit(...) instead.', E_USER_WARNING);
$this->driver->applyLimit($sql, $limit, $offset);
$res = new DibiResult($resultDriver);
return $res->setFormat(dibi::DATE, $this->config['result']['formatDate'])
->setFormat(dibi::DATETIME, $this->config['result']['formatDateTime']);
}
@@ -586,8 +527,8 @@ class DibiConnection extends DibiObject
*/
public function update($table, $args)
{
if (!(is_array($args) || $args instanceof ArrayObject)) {
throw new InvalidArgumentException('Arguments must be array or ArrayObject.');
if (!(is_array($args) || $args instanceof Traversable)) {
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->update('%n', $table)->set($args);
}
@@ -601,10 +542,10 @@ class DibiConnection extends DibiObject
*/
public function insert($table, $args)
{
if ($args instanceof ArrayObject) {
$args = (array) $args;
if ($args instanceof Traversable) {
$args = iterator_to_array($args);
} elseif (!is_array($args)) {
throw new InvalidArgumentException('Arguments must be array or ArrayObject.');
throw new InvalidArgumentException('Arguments must be array or Traversable.');
}
return $this->command()->insert()
->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args);
@@ -623,27 +564,38 @@ class DibiConnection extends DibiObject
/********************* profiler ****************d*g**/
/********************* substitutions ****************d*g**/
/**
* @param IDibiProfiler
* @return void
* Returns substitution hashmap.
* @return DibiHashMap
*/
public function setProfiler(IDibiProfiler $profiler = NULL)
public function getSubstitutes()
{
$this->profiler = $profiler;
return $this->substitutes;
}
/**
* @return IDibiProfiler
* Provides substitution.
* @return string
*/
public function getProfiler()
public function substitute($value)
{
return $this->profiler;
return strpos($value, ':') === FALSE ? $value : preg_replace_callback('#:([^:\s]*):#', array($this, 'subCb'), $value);
}
/**
* Substitution callback.
*/
private function subCb($m)
{
return $this->substitutes->{$m[1]};
}
@@ -719,13 +671,12 @@ class DibiConnection extends DibiObject
*/
public function loadFile($file)
{
$this->connect();
$this->connected || $this->connect();
@set_time_limit(0); // intentionally @
$handle = @fopen($file, 'r'); // intentionally @
if (!$handle) {
throw new FileNotFoundException("Cannot open file '$file'.");
throw new RuntimeException("Cannot open file '$file'.");
}
$count = 0;
@@ -751,8 +702,8 @@ class DibiConnection extends DibiObject
*/
public function getDatabaseInfo()
{
$this->connect();
return new DibiDatabaseInfo($this->driver, isset($this->config['database']) ? $this->config['database'] : NULL);
$this->connected || $this->connect();
return new DibiDatabaseInfo($this->driver->getReflector(), isset($this->config['database']) ? $this->config['database'] : NULL);
}
@@ -762,7 +713,7 @@ class DibiConnection extends DibiObject
*/
public function __wakeup()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
@@ -772,7 +723,7 @@ class DibiConnection extends DibiObject
*/
public function __sleep()
{
throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
throw new DibiNotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.');
}
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,8 +15,12 @@
* Default implementation of IDataSource for dibi.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 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
{
@@ -66,7 +62,7 @@ class DibiDataSource extends DibiObject implements IDataSource
*/
public function __construct($sql, DibiConnection $connection)
{
if (strpos($sql, ' ') === FALSE) {
if (strpbrk($sql, " \t\r\n") === FALSE) {
$this->sql = $connection->getDriver()->escape($sql, dibi::IDENTIFIER); // table name
} else {
$this->sql = '(' . $sql . ') t'; // SQL command
@@ -267,7 +263,7 @@ class DibiDataSource extends DibiObject implements IDataSource
*/
public function toFluent()
{
return $this->connection->select('*')->from('(%SQL) AS t', $this->__toString());
return $this->connection->select('*')->from('(%SQL) t', $this->__toString());
}
@@ -287,15 +283,19 @@ class DibiDataSource extends DibiObject implements IDataSource
* Returns SQL query.
* @return string
*/
final public function __toString()
public function __toString()
{
return $this->connection->sql('
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, '
%ofs %lmt', $this->offset, $this->limit
);
try {
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, '
%ofs %lmt', $this->offset, $this->limit
);
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
@@ -313,7 +313,7 @@ class DibiDataSource extends DibiObject implements IDataSource
if ($this->count === NULL) {
$this->count = $this->conds || $this->offset || $this->limit
? (int) $this->connection->nativeQuery(
'SELECT COUNT(*) FROM (' . $this->__toString() . ') AS t'
'SELECT COUNT(*) FROM (' . $this->__toString() . ') t'
)->fetchSingle()
: $this->getTotalCount();
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,13 +15,16 @@
* Reflection metadata class for a database.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $tables
* @property-read array $tableNames
*/
class DibiDatabaseInfo extends DibiObject
{
/** @var IDibiDriver */
private $driver;
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
@@ -39,9 +34,9 @@ class DibiDatabaseInfo extends DibiObject
public function __construct(IDibiDriver $driver, $name)
public function __construct(IDibiReflector $reflector, $name)
{
$this->driver = $driver;
$this->reflector = $reflector;
$this->name = $name;
}
@@ -120,8 +115,8 @@ class DibiDatabaseInfo extends DibiObject
{
if ($this->tables === NULL) {
$this->tables = array();
foreach ($this->driver->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new DibiTableInfo($this->driver, $info);
foreach ($this->reflector->getTables() as $info) {
$this->tables[strtolower($info['name'])] = new DibiTableInfo($this->reflector, $info);
}
}
}
@@ -135,13 +130,20 @@ class DibiDatabaseInfo extends DibiObject
* Reflection metadata class for a database table.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @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 IDibiDriver */
private $driver;
/** @var IDibiReflector */
private $reflector;
/** @var string */
private $name;
@@ -163,9 +165,9 @@ class DibiTableInfo extends DibiObject
public function __construct(IDibiDriver $driver, array $info)
public function __construct(IDibiReflector $reflector, array $info)
{
$this->driver = $driver;
$this->reflector = $reflector;
$this->name = $info['name'];
$this->view = !empty($info['view']);
}
@@ -288,8 +290,8 @@ class DibiTableInfo extends DibiObject
{
if ($this->columns === NULL) {
$this->columns = array();
foreach ($this->driver->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->driver, $info);
foreach ($this->reflector->getColumns($this->name) as $info) {
$this->columns[strtolower($info['name'])] = new DibiColumnInfo($this->reflector, $info);
}
}
}
@@ -304,7 +306,7 @@ class DibiTableInfo extends DibiObject
if ($this->indexes === NULL) {
$this->initColumns();
$this->indexes = array();
foreach ($this->driver->getIndexes($this->name) as $info) {
foreach ($this->reflector->getIndexes($this->name) as $info) {
foreach ($info['columns'] as $key => $name) {
$info['columns'][$key] = $this->columns[strtolower($name)];
}
@@ -323,7 +325,7 @@ class DibiTableInfo extends DibiObject
*/
protected function initForeignKeys()
{
throw new NotImplementedException;
throw new DibiNotImplementedException;
}
}
@@ -332,33 +334,144 @@ class DibiTableInfo extends DibiObject
/**
* Reflection metadata class for a table column.
* Reflection metadata class for a result set.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @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 IDibiDriver */
private $driver;
/** @var IDibiReflector|NULL when created by DibiResultInfo */
private $reflector;
/** @var array (name, nativetype, [table], [fullname], [size], [nullable], [default], [autoincrement], [vendor]) */
private $info;
/** @var string */
private $type;
public function __construct(IDibiDriver $driver, array $info)
public function __construct(IDibiReflector $reflector = NULL, array $info)
{
$this->driver = $driver;
$this->reflector = $reflector;
$this->info = $info;
$this->type = self::detectType($this->info['nativetype']);
}
@@ -373,6 +486,16 @@ class DibiColumnInfo extends DibiObject
/**
* @return string
*/
public function getFullName()
{
return isset($this->info['fullname']) ? $this->info['fullname'] : NULL;
}
/**
* @return bool
*/
@@ -388,10 +511,20 @@ class DibiColumnInfo extends DibiObject
*/
public function getTable()
{
if (empty($this->info['table'])) {
throw new DibiException("Table name is unknown.");
if (empty($this->info['table']) || !$this->reflector) {
throw new DibiException("Table is unknown or not available.");
}
return new DibiTableInfo($this->driver, array('name' => $this->info['table']));
return new DibiTableInfo($this->reflector, array('name' => $this->info['table']));
}
/**
* @return string
*/
public function getTableName()
{
return isset($this->info['table']) ? $this->info['table'] : NULL;
}
@@ -401,7 +534,7 @@ class DibiColumnInfo extends DibiObject
*/
public function getType()
{
return $this->type;
return self::getTypeCache()->{$this->info['nativetype']};
}
@@ -426,6 +559,16 @@ class DibiColumnInfo extends DibiObject
/**
* @return bool
*/
public function isUnsigned()
{
return isset($this->info['unsigned']) ? (bool) $this->info['unsigned'] : NULL;
}
/**
* @return bool
*/
@@ -471,29 +614,41 @@ class DibiColumnInfo extends DibiObject
* 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,
'BYTE|COUNTER|SERIAL|INT|LONG' => dibi::INTEGER,
'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
'YEAR|DATE' => dibi::DATE,
'DATE' => dibi::DATE,
'BOOL|BIT' => dibi::BOOL,
);
if (!isset(self::$types[$type])) {
self::$types[$type] = dibi::TEXT;
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return self::$types[$type] = $val;
}
foreach ($patterns as $s => $val) {
if (preg_match("#$s#i", $type)) {
return $val;
}
}
return self::$types[$type];
return dibi::TEXT;
}
/**
* @internal
*/
public static function getTypeCache()
{
if (self::$types === NULL) {
self::$types = new DibiHashMap(array(__CLASS__, 'detectType'));
}
return self::$types;
}
}
@@ -505,9 +660,11 @@ class DibiColumnInfo extends DibiObject
* Reflection metadata class for a foreign key.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\reflection
* @todo
*
* @property-read string $name
* @property-read array $references
*/
class DibiForeignKeyInfo extends DibiObject
{
@@ -554,8 +711,12 @@ class DibiForeignKeyInfo extends DibiObject
* Reflection metadata class for a index or primary key.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
* @package dibi\reflection
*
* @property-read string $name
* @property-read array $columns
* @property-read bool $unique
* @property-read bool $primary
*/
class DibiIndexInfo extends DibiObject
{

View File

@@ -0,0 +1,88 @@
<?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');
}
}

102
dibi/libs/DibiEvent.php Normal file
View File

@@ -0,0 +1,102 @@
<?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.
*/
/**
* Profiler & logger event.
*
* @author David Grudl
* @package dibi
*/
class DibiEvent
{
/** event type */
const CONNECT = 1,
SELECT = 4,
INSERT = 8,
DELETE = 16,
UPDATE = 32,
QUERY = 60, // SELECT | INSERT | DELETE | UPDATE
BEGIN = 64,
COMMIT = 128,
ROLLBACK = 256,
TRANSACTION = 448, // BEGIN | COMMIT | ROLLBACK
ALL = 1023;
/** @var DibiConnection */
public $connection;
/** @var int */
public $type;
/** @var string */
public $sql;
/** @var DibiResult|DibiDriverException|NULL */
public $result;
/** @var float */
public $time;
/** @var int */
public $count;
/** @var array */
public $source;
public function __construct(DibiConnection $connection, $type, $sql = NULL)
{
$this->connection = $connection;
$this->type = $type;
$this->sql = trim($sql);
$this->time = -microtime(TRUE);
if ($type === self::QUERY && preg_match('#\(?\s*(SELECT|UPDATE|INSERT|DELETE)#iA', $this->sql, $matches)) {
static $types = array(
'SELECT' => self::SELECT, 'UPDATE' => self::UPDATE,
'INSERT' => self::INSERT, 'DELETE' => self::DELETE,
);
$this->type = $types[strtoupper($matches[1])];
}
$rc = new ReflectionClass('dibi');
$dibiDir = dirname($rc->getFileName()) . DIRECTORY_SEPARATOR;
foreach (debug_backtrace(FALSE) as $row) {
if (isset($row['file']) && is_file($row['file']) && strpos($row['file'], $dibiDir) !== 0) {
$this->source = array($row['file'], (int) $row['line']);
break;
}
}
dibi::$elapsedTime = FALSE;
}
public function done($result = NULL)
{
$this->result = $result;
try {
$this->count = $result instanceof DibiResult ? count($result) : NULL;
} catch (DibiException $e) {
$this->count = NULL;
}
$this->time += microtime(TRUE);
dibi::$elapsedTime = $this->time;
dibi::$totalTime += $this->time;
return $this;
}
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,10 +15,9 @@
* dibi common exception.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class DibiException extends Exception implements /*Nette\*/IDebuggable
class DibiException extends Exception
{
/** @var string */
private $sql;
@@ -42,7 +33,6 @@ class DibiException extends Exception implements /*Nette\*/IDebuggable
{
parent::__construct($message, (int) $code);
$this->sql = $sql;
// TODO: add $profiler->exception($this);
}
@@ -65,27 +55,6 @@ class DibiException extends Exception implements /*Nette\*/IDebuggable
return parent::__toString() . ($this->sql ? "\nSQL: " . $this->sql : '');
}
/********************* interface Nette\IDebuggable ****************d*g**/
/**
* Returns custom panels.
* @return array
*/
public function getPanels()
{
$panels = array();
if ($this->sql !== NULL) {
$panels['SQL'] = array(
'expanded' => TRUE,
'content' => dibi::dump($this->sql, TRUE),
);
}
return $panels;
}
}
@@ -95,7 +64,6 @@ class DibiException extends Exception implements /*Nette\*/IDebuggable
* database server exception.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class DibiDriverException extends DibiException
@@ -153,4 +121,45 @@ class DibiDriverException extends DibiException
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

@@ -0,0 +1,78 @@
<?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 file logger.
*
* @author David Grudl
* @package dibi
*/
class DibiFileLogger extends DibiObject
{
/** @var string Name of the file where SQL errors should be logged */
public $file;
/** @var int */
public $filter;
public function __construct($file, $filter = NULL)
{
$this->file = $file;
$this->filter = $filter ? (int) $filter : DibiEvent::QUERY;
}
/**
* After event notification.
* @return void
*/
public function logEvent(DibiEvent $event)
{
if (($event->type & $this->filter) === 0) {
return;
}
$handle = fopen($this->file, 'a');
if (!$handle) return; // or throw exception?
flock($handle, LOCK_EX);
if ($event->result instanceof Exception) {
$message = $event->result->getMessage();
if ($code = $event->result->getCode()) {
$message = "[$code] $message";
}
fwrite($handle,
"ERROR: $message"
. "\n-- SQL: " . $event->sql
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. ";\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
} else {
fwrite($handle,
"OK: " . $event->sql
. ($event->count ? ";\n-- rows: " . $event->count : '')
. "\n-- takes: " . sprintf('%0.3f', $event->time * 1000) . ' ms'
. "\n-- source: " . implode(':', $event->source)
. "\n-- driver: " . $event->connection->getConfig('driver') . '/' . $event->connection->getConfig('name')
. "\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
}
fclose($handle);
}
}

View File

@@ -0,0 +1,88 @@
<?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,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,11 +15,25 @@
* dibi SQL builder via fluent interfaces. EXPERIMENTAL!
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 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)
*/
class DibiFluent extends DibiObject implements IDataSource
{
const REMOVE = FALSE;
/** @var array */
public static $masks = array(
'SELECT' => array('SELECT', 'DISTINCT', 'FROM', 'WHERE', 'GROUP BY',
@@ -41,7 +47,7 @@ class DibiFluent extends DibiObject implements IDataSource
public static $modifiers = array(
'SELECT' => '%n',
'FROM' => '%n',
'IN' => '%l',
'IN' => '%in',
'VALUES' => '%l',
'SET' => '%a',
'WHERE' => '%and',
@@ -53,7 +59,7 @@ class DibiFluent extends DibiObject implements IDataSource
/** @var array clauses separators */
public static $separators = array(
'SELECT' => ',',
'FROM' => FALSE,
'FROM' => ',',
'WHERE' => 'AND',
'GROUP BY' => ',',
'HAVING' => 'AND',
@@ -65,9 +71,20 @@ class DibiFluent extends DibiObject implements IDataSource
'INTO' => FALSE,
);
/** @var array clauses */
public static $clauseSwitches = array(
'JOIN' => 'FROM',
'INNER JOIN' => 'FROM',
'LEFT JOIN' => 'FROM',
'RIGHT JOIN' => 'FROM',
);
/** @var DibiConnection */
private $connection;
/** @var array */
private $setups = array();
/** @var string */
private $command;
@@ -80,6 +97,9 @@ class DibiFluent extends DibiObject implements IDataSource
/** @var array */
private $cursor;
/** @var DibiHashMap normalized clauses */
private static $normalizer;
/**
@@ -88,6 +108,10 @@ class DibiFluent extends DibiObject implements IDataSource
public function __construct(DibiConnection $connection)
{
$this->connection = $connection;
if (self::$normalizer === NULL) {
self::$normalizer = new DibiHashMap(array(__CLASS__, '_formatClause'));
}
}
@@ -100,7 +124,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function __call($clause, $args)
{
$clause = self::_formatClause($clause);
$clause = self::$normalizer->$clause;
// lazy initialization
if ($this->command === NULL) {
@@ -112,27 +136,9 @@ class DibiFluent extends DibiObject implements IDataSource
$this->command = $clause;
}
// special types or argument
if (count($args) === 1) {
$arg = $args[0];
// TODO: really ignore TRUE?
if ($arg === TRUE) { // flag
$args = array();
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*$#i', $arg)) { // identifier
$args = array('%n', $arg);
} elseif ($arg instanceof self) {
$args = array_merge(array('('), $arg->_export(), array(')'));
} elseif (is_array($arg) || $arg instanceof ArrayObject) { // any array
if (isset(self::$modifiers[$clause])) {
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
}
} // case $arg === FALSE is handled below
// auto-switch to a clause
if (isset(self::$clauseSwitches[$clause])) {
$this->cursor = & $this->clauses[self::$clauseSwitches[$clause]];
}
if (array_key_exists($clause, $this->clauses)) {
@@ -140,14 +146,14 @@ class DibiFluent extends DibiObject implements IDataSource
$this->cursor = & $this->clauses[$clause];
// TODO: really delete?
if ($args === array(FALSE)) {
if ($args === array(self::REMOVE)) {
$this->cursor = NULL;
return $this;
}
if (isset(self::$separators[$clause])) {
$sep = self::$separators[$clause];
if ($sep === FALSE) {
if ($sep === FALSE) { // means: replace
$this->cursor = array();
} elseif (!empty($this->cursor)) {
@@ -157,7 +163,7 @@ class DibiFluent extends DibiObject implements IDataSource
} else {
// append to currect flow
if ($args === array(FALSE)) {
if ($args === array(self::REMOVE)) {
return $this;
}
@@ -168,7 +174,33 @@ class DibiFluent extends DibiObject implements IDataSource
$this->cursor = array();
}
array_splice($this->cursor, count($this->cursor), 0, $args);
// special types or argument
if (count($args) === 1) {
$arg = $args[0];
// TODO: really ignore TRUE?
if ($arg === TRUE) { // flag
return $this;
} elseif (is_string($arg) && preg_match('#^[a-z:_][a-z0-9_.:]*$#i', $arg)) { // identifier
$args = array('%n', $arg);
} elseif (is_array($arg) || ($arg instanceof Traversable && !$arg instanceof self)) { // any array
if (isset(self::$modifiers[$clause])) {
$args = array(self::$modifiers[$clause], $arg);
} elseif (is_string(key($arg))) { // associative array
$args = array('%a', $arg);
}
} // case $arg === FALSE is handled above
}
foreach ($args as $arg) {
if ($arg instanceof self) {
$arg = "($arg)";
}
$this->cursor[] = $arg;
}
return $this;
}
@@ -181,9 +213,10 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function clause($clause, $remove = FALSE)
{
$this->cursor = & $this->clauses[self::_formatClause($clause)];
$this->cursor = & $this->clauses[self::$normalizer->$clause];
if ($remove) {
if ($remove) { // deprecated, use removeClause
trigger_error(__METHOD__ . '(..., TRUE) is deprecated; use removeClause() instead.', E_USER_NOTICE);
$this->cursor = NULL;
} elseif ($this->cursor === NULL) {
@@ -195,6 +228,19 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Removes a clause.
* @param string clause name
* @return DibiFluent provides a fluent interface
*/
public function removeClause($clause)
{
$this->clauses[self::$normalizer->$clause] = NULL;
return $this;
}
/**
* Change a SQL flag.
* @param string flag name
@@ -248,6 +294,20 @@ class DibiFluent extends DibiObject implements IDataSource
/**
* Adds DibiResult setup.
* @param string method
* @param mixed args
* @return DibiFluent provides a fluent interface
*/
public function setupResult($method)
{
$this->setups[] = func_get_args();
return $this;
}
/********************* executing ****************d*g**/
@@ -260,7 +320,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function execute($return = NULL)
{
$res = $this->connection->query($this->_export());
$res = $this->query($this->_export());
return $return === dibi::IDENTIFIER ? $this->connection->getInsertId() : $res;
}
@@ -273,9 +333,9 @@ class DibiFluent extends DibiObject implements IDataSource
public function fetch()
{
if ($this->command === 'SELECT') {
return $this->connection->query($this->_export(NULL, array('%lmt', 1)))->fetch();
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetch();
} else {
return $this->connection->query($this->_export())->fetch();
return $this->query($this->_export())->fetch();
}
}
@@ -288,9 +348,9 @@ class DibiFluent extends DibiObject implements IDataSource
public function fetchSingle()
{
if ($this->command === 'SELECT') {
return $this->connection->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
return $this->query($this->_export(NULL, array('%lmt', 1)))->fetchSingle();
} else {
return $this->connection->query($this->_export())->fetchSingle();
return $this->query($this->_export())->fetchSingle();
}
}
@@ -304,7 +364,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchAll($offset = NULL, $limit = NULL)
{
return $this->connection->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->fetchAll();
}
@@ -316,7 +376,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchAssoc($assoc)
{
return $this->connection->query($this->_export())->fetchAssoc($assoc);
return $this->query($this->_export())->fetchAssoc($assoc);
}
@@ -329,7 +389,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function fetchPairs($key = NULL, $value = NULL)
{
return $this->connection->query($this->_export())->fetchPairs($key, $value);
return $this->query($this->_export())->fetchPairs($key, $value);
}
@@ -342,7 +402,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function getIterator($offset = NULL, $limit = NULL)
{
return $this->connection->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
return $this->query($this->_export(NULL, array('%ofs %lmt', $offset, $limit)))->getIterator();
}
@@ -364,9 +424,23 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function count()
{
return (int) $this->connection->query(
return (int) $this->query(array(
'SELECT COUNT(*) FROM (%ex', $this->_export(), ') AS [data]'
)->fetchSingle();
))->fetchSingle();
}
/**
* @return DibiResult
*/
private function query($args)
{
$res = $this->connection->query($args);
foreach ($this->setups as $setup) {
call_user_func_array(array($res, array_shift($setup)), $setup);
}
return $res;
}
@@ -380,7 +454,7 @@ class DibiFluent extends DibiObject implements IDataSource
*/
public function toDataSource()
{
return new DibiDataSource($this->connection->sql($this->_export()), $this->connection);
return new DibiDataSource($this->connection->translate($this->_export()), $this->connection);
}
@@ -391,7 +465,11 @@ class DibiFluent extends DibiObject implements IDataSource
*/
final public function __toString()
{
return $this->connection->sql($this->_export());
try {
return $this->connection->translate($this->_export());
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_ERROR);
}
}
@@ -407,7 +485,7 @@ class DibiFluent extends DibiObject implements IDataSource
$data = $this->clauses;
} else {
$clause = self::_formatClause($clause);
$clause = self::$normalizer->$clause;
if (array_key_exists($clause, $this->clauses)) {
$data = array($clause => $this->clauses[$clause]);
} else {
@@ -418,10 +496,10 @@ class DibiFluent extends DibiObject implements IDataSource
foreach ($data as $clause => $statement) {
if ($statement !== NULL) {
$args[] = $clause;
if ($clause === $this->command) {
if ($clause === $this->command && $this->flags) {
$args[] = implode(' ', array_keys($this->flags));
}
array_splice($args, count($args), 0, $statement);
foreach ($statement as $arg) $args[] = $arg;
}
}
@@ -434,24 +512,27 @@ class DibiFluent extends DibiObject implements IDataSource
* Format camelCase clause name to UPPER CASE.
* @param string
* @return string
* @internal
*/
private static function _formatClause($s)
public static function _formatClause($s)
{
if ($s === 'order' || $s === 'group') {
$s .= 'By';
trigger_error("Did you mean '$s'?", E_USER_NOTICE);
}
return strtoupper(preg_replace('#[A-Z]#', ' $0', $s));
return strtoupper(preg_replace('#[a-z](?=[A-Z])#', '$0 ', $s));
}
}
// PHP < 5.2 compatibility
if (!function_exists('array_fill_keys')) {
function array_fill_keys($keys, $value)
public function __clone()
{
return array_combine($keys, array_fill(0, count($keys), $value));
// remove references
foreach ($this->clauses as $clause => $val) {
$this->clauses[$clause] = & $val;
unset($val);
}
$this->cursor = & $foo;
}
}

82
dibi/libs/DibiHashMap.php Normal file
View File

@@ -0,0 +1,82 @@
<?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.
*/
/**
* Lazy cached storage.
*
* @author David Grudl
* @package dibi
* @internal
*/
abstract class DibiHashMapBase
{
private $callback;
public function __construct($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 getCallback()
{
return $this->callback;
}
}
/**
* Lazy cached storage.
*
* @author David Grudl
* @internal
*/
final class DibiHashMap extends DibiHashMapBase
{
public function __set($nm, $val)
{
if ($nm == '') {
$nm = "\xFF";
}
$this->$nm = $val;
}
public function __get($nm)
{
if ($nm == '') {
$nm = "\xFF";
return isset($this->$nm) ? $this->$nm : $this->$nm = call_user_func($this->getCallback(), '');
} else {
return $this->$nm = call_user_func($this->getCallback(), $nm);
}
}
}

40
dibi/libs/DibiLiteral.php Normal file
View File

@@ -0,0 +1,40 @@
<?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.
*/
/**
* SQL literal value.
*
* @author David Grudl
*/
class DibiLiteral extends DibiObject
{
/** @var string */
private $value;
public function __construct($value)
{
$this->value = (string) $value;
}
/**
* @return string
*/
public function __toString()
{
return $this->value;
}
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -22,7 +14,7 @@
/**
* DibiObject is the ultimate ancestor of all instantiable classes.
*
* DibiObject is copy of Nette\Object from Nette Framework (http://nettephp.com).
* 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
@@ -58,7 +50,6 @@
* </code>
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
abstract class DibiObject
@@ -85,7 +76,7 @@ abstract class DibiObject
*/
final public function getReflection()
{
return new /*\*/ReflectionObject($this);
return new ReflectionObject($this);
}
@@ -95,22 +86,22 @@ abstract class DibiObject
* @param string method name
* @param array arguments
* @return mixed
* @throws \MemberAccessException
* @throws \LogicException
*/
public function __call($name, $args)
{
$class = get_class($this);
if ($name === '') {
throw new /*\*/MemberAccessException("Call to class '$class' method without 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);
$rp = new ReflectionProperty($class, $name);
if ($rp->isPublic() && !$rp->isStatic()) {
$list = $this->$name;
if (is_array($list) || $list instanceof /*\*/Traversable) {
if (is_array($list) || $list instanceof Traversable) {
foreach ($list as $handler) {
/**/if (is_object($handler)) {
call_user_func_array(array($handler, '__invoke'), $args);
@@ -130,7 +121,7 @@ abstract class DibiObject
return call_user_func_array($cb, $args);
}
throw new /*\*/MemberAccessException("Call to undefined method $class::$name().");
throw new LogicException("Call to undefined method $class::$name().");
}
@@ -140,12 +131,12 @@ abstract class DibiObject
* @param string method name (in lower case!)
* @param array arguments
* @return mixed
* @throws \MemberAccessException
* @throws \LogicException
*/
public static function __callStatic($name, $args)
{
$class = get_called_class();
throw new /*\*/MemberAccessException("Call to undefined static method $class::$name().");
throw new LogicException("Call to undefined static method $class::$name().");
}
@@ -216,14 +207,14 @@ abstract class DibiObject
* Returns property value. Do not call directly.
* @param string property name
* @return mixed property value
* @throws \MemberAccessException if the property is not defined.
* @throws \LogicException if the property is not defined.
*/
public function &__get($name)
{
$class = get_class($this);
if ($name === '') {
throw new /*\*/MemberAccessException("Cannot read a class '$class' property without name.");
throw new LogicException("Cannot read a class '$class' property without name.");
}
// property getter support
@@ -244,7 +235,7 @@ abstract class DibiObject
}
$name = func_get_arg(0);
throw new /*\*/MemberAccessException("Cannot read an undeclared property $class::\$$name.");
throw new LogicException("Cannot read an undeclared property $class::\$$name.");
}
@@ -254,14 +245,14 @@ abstract class DibiObject
* @param string property name
* @param mixed property value
* @return void
* @throws \MemberAccessException if the property is not defined or is read-only
* @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 /*\*/MemberAccessException("Cannot assign to a class '$class' property without name.");
throw new LogicException("Cannot assign to a class '$class' property without name.");
}
// property setter support
@@ -274,12 +265,12 @@ abstract class DibiObject
} else {
$name = func_get_arg(0);
throw new /*\*/MemberAccessException("Cannot assign to a read-only property $class::\$$name.");
throw new LogicException("Cannot assign to a read-only property $class::\$$name.");
}
}
$name = func_get_arg(0);
throw new /*\*/MemberAccessException("Cannot assign to an undeclared property $class::\$$name.");
throw new LogicException("Cannot assign to an undeclared property $class::\$$name.");
}
@@ -301,12 +292,12 @@ abstract class DibiObject
* Access to undeclared property.
* @param string property name
* @return void
* @throws \MemberAccessException
* @throws \LogicException
*/
public function __unset($name)
{
$class = get_class($this);
throw new /*\*/MemberAccessException("Cannot unset the property $class::\$$name.");
throw new LogicException("Cannot unset the property $class::\$$name.");
}

View File

@@ -1,242 +0,0 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
*/
/**
* dibi basic logger & profiler (experimental).
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class DibiProfiler extends DibiObject implements IDibiProfiler
{
/** maximum number of rows */
const FIREBUG_MAX_ROWS = 30;
/** maximum SQL length */
const FIREBUG_MAX_LENGTH = 500;
/** @var string Name of the file where SQL errors should be logged */
private $file;
/** @var bool log to firebug? */
public $useFirebug;
/** @var int */
private $filter = self::ALL;
/** @var array */
public $tickets = array();
/** @var array */
public static $table = array(array('Time', 'SQL Statement', 'Rows', 'Connection'));
public function __construct()
{
$this->useFirebug = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
}
/**
* @param string filename
* @return void
*/
public function setFile($file)
{
$this->file = $file;
}
/**
* @param int
* @return void
*/
public function setFilter($filter)
{
$this->filter = (int) $filter;
}
/**
* Before event notification.
* @param DibiConnection
* @param int event name
* @param string sql
* @return int
*/
public function before(DibiConnection $connection, $event, $sql = NULL)
{
$this->tickets[] = array($connection, $event, $sql);
end($this->tickets);
return key($this->tickets);
}
/**
* After event notification.
* @param int
* @param DibiResult
* @return void
*/
public function after($ticket, $res = NULL)
{
if (!isset($this->tickets[$ticket])) {
throw new InvalidArgumentException('Bad ticket number.');
}
list($connection, $event, $sql) = $this->tickets[$ticket];
$sql = trim($sql);
if (($event & $this->filter) === 0) return;
if ($event & self::QUERY) {
try {
$count = $res instanceof DibiResult ? count($res) : '-';
} catch (Exception $e) {
$count = '?';
}
if ($this->useFirebug && !headers_sent()) {
if (count(self::$table) < self::FIREBUG_MAX_ROWS) {
self::$table[] = array(
sprintf('%0.3f', dibi::$elapsedTime * 1000),
strlen($sql) > self::FIREBUG_MAX_LENGTH ? substr($sql, 0, self::FIREBUG_MAX_LENGTH) . '...' : $sql,
$count,
$connection->getConfig('driver') . '/' . $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 = array(
array(
'Type' => 'TABLE',
'Label' => 'dibi profiler (' . dibi::$numOfQueries . ' SQL queries took ' . sprintf('%0.3f', dibi::$totalTime * 1000) . ' ms)',
),
self::$table,
);
$payload = function_exists('json_encode') ? json_encode($payload) : self::json_encode($payload);
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|");
}
if ($this->file) {
$this->writeFile(
"OK: " . $sql
. ($res instanceof DibiResult ? ";\n-- rows: " . $count : '')
. "\n-- takes: " . sprintf('%0.3f', dibi::$elapsedTime * 1000) . ' ms'
. "\n-- driver: " . $connection->getConfig('driver') . '/' . $connection->getConfig('name')
. "\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
}
}
}
/**
* After exception notification.
* @param DibiDriverException
* @return void
*/
public function exception(DibiDriverException $exception)
{
if ((self::EXCEPTION & $this->filter) === 0) return;
if ($this->useFirebug) {
// TODO: implement
}
if ($this->file) {
$message = $exception->getMessage();
$code = $exception->getCode();
if ($code) {
$message = "[$code] $message";
}
$this->writeFile(
"ERROR: $message"
. "\n-- SQL: " . dibi::$sql
. "\n-- driver: " //. $connection->getConfig('driver')
. ";\n-- " . date('Y-m-d H:i:s')
. "\n\n"
);
}
}
private function writeFile($message)
{
$handle = fopen($this->file, 'a');
if (!$handle) return; // or throw exception?
flock($handle, LOCK_EX);
fwrite($handle, $message);
fclose($handle);
}
public static function json_encode($val)
{
// indexed array
if (is_array($val) && (!$val
|| array_keys($val) === range(0, count($val) - 1))) {
return '[' . implode(',', array_map(array(__CLASS__, 'json_encode'), $val)) . ']';
}
// associative array
if (is_array($val) || is_object($val)) {
$tmp = array();
foreach ($val as $k => $v) {
$tmp[] = self::json_encode((string) $k) . ':' . self::json_encode($v);
}
return '{' . implode(',', $tmp) . '}';
}
if (is_string($val)) {
$val = str_replace(array("\\", "\x00"), array("\\\\", "\\u0000"), $val); // due to bug #40915
return '"' . addcslashes($val, "\x8\x9\xA\xC\xD/\"") . '"';
}
if (is_int($val) || is_float($val)) {
return rtrim(rtrim(number_format($val, 5, '.', ''), '0'), '.');
}
if (is_bool($val)) {
return $val ? 'true' : 'false';
}
return 'null';
}
}

View File

@@ -1,26 +1,18 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* dibi result-set.
* dibi result set.
*
* <code>
* $result = dibi::query('SELECT * FROM [table]');
@@ -36,99 +28,54 @@
* </code>
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 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
{
/** @var array IDibiDriver */
/** @var array IDibiResultDriver */
private $driver;
/** @var array Translate table */
private $xlat;
private $types = array();
/** @var array Cache for $driver->getColumnsMeta() */
/** @var DibiResultInfo */
private $meta;
/** @var bool Already fetched? Used for allowance for first seek(0) */
private $fetched = FALSE;
/** @var array|FALSE Qualifiy each column name with the table name? */
private $withTables = FALSE;
/** @var string returned object class */
private $class = 'DibiRow';
private $rowClass = 'DibiRow';
/** @var array format */
private $formats = array();
/**
* @param IDibiDriver
* @param array
* @param IDibiResultDriver
*/
public function __construct($driver, $config)
public function __construct($driver)
{
$this->driver = $driver;
if (!empty($config[dibi::RESULT_WITH_TABLES])) {
$this->setWithTables(TRUE);
}
$this->detectTypes();
}
/**
* Automatically frees the resources allocated for this result set.
* @return void
*/
public function __destruct()
{
@$this->free(); // intentionally @
}
/**
* Returns the result set resource.
* @return mixed
* @deprecated
*/
final public function getResource()
{
return $this->getDriver()->getResultResource();
}
/**
* 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
*/
final public function seek($row)
{
return ($row !== 0 || $this->fetched) ? (bool) $this->getDriver()->seek($row) : TRUE;
}
/**
* Returns the number of rows in a result set.
* @return int
*/
final public function getRowCount()
{
return $this->getDriver()->getRowCount();
}
/**
* Returns the number of rows in a result set. Alias for getRowCount().
* @return int
*/
final public function rowCount()
{
return $this->getDriver()->getRowCount();
return $this->getResultDriver()->getResultResource();
}
@@ -141,52 +88,97 @@ class DibiResult extends DibiObject implements IDataSource
{
if ($this->driver !== NULL) {
$this->driver->free();
$this->driver = NULL;
$this->driver = $this->meta = NULL;
}
}
/**
* Qualifiy each column name with the table name?
* @param bool
* @return DibiResult provides a fluent interface
* Safe access to property $driver.
* @return IDibiResultDriver
* @throws RuntimeException
*/
final public function getResultDriver()
{
if ($this->driver === NULL) {
throw new RuntimeException('Result-set was released from memory.');
}
return $this->driver;
}
/********************* rows ****************d*g**/
/**
* Moves cursor position without fetching row.
* @param int the 0-based cursor pos to seek to
* @return boolean TRUE on success, FALSE if unable to seek to specified record
* @throws DibiException
*/
final public function setWithTables($val)
final public function seek($row)
{
if ($val) {
$cols = array();
foreach ($this->getMeta() as $info) {
$name = $info['fullname'];
if (isset($cols[$name])) {
$fix = 1;
while (isset($cols[$name . '#' . $fix])) $fix++;
$name .= '#' . $fix;
}
$cols[$name] = TRUE;
}
$this->withTables = array_keys($cols);
} else {
$this->withTables = FALSE;
}
return $this;
return ($row !== 0 || $this->fetched) ? (bool) $this->getResultDriver()->seek($row) : TRUE;
}
/**
* Qualifiy each key with the table name?
* @return bool
* Required by the Countable interface.
* @return int
*/
final public function getWithTables()
final public function count()
{
return (bool) $this->withTables;
return $this->getResultDriver()->getRowCount();
}
/**
* Returns the number of rows in a result set.
* @return int
*/
final public function getRowCount()
{
return $this->getResultDriver()->getRowCount();
}
/**
* 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
*/
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);
}
/********************* fetching rows ****************d*g**/
/**
* Set fetched object class. This class should extend the DibiRow class.
* @param string
@@ -194,7 +186,7 @@ class DibiResult extends DibiObject implements IDataSource
*/
public function setRowClass($class)
{
$this->class = $class;
$this->rowClass = $class;
return $this;
}
@@ -206,7 +198,7 @@ class DibiResult extends DibiObject implements IDataSource
*/
public function getRowClass()
{
return $this->class;
return $this->rowClass;
}
@@ -218,28 +210,16 @@ class DibiResult extends DibiObject implements IDataSource
*/
final public function fetch()
{
if ($this->withTables === FALSE) {
$row = $this->getDriver()->fetch(TRUE);
if (!is_array($row)) return FALSE;
} else {
$row = $this->getDriver()->fetch(FALSE);
if (!is_array($row)) return FALSE;
$row = array_combine($this->withTables, $row);
$row = $this->getResultDriver()->fetch(TRUE);
if (!is_array($row)) {
return FALSE;
}
$this->fetched = TRUE;
// types-converting?
if ($this->xlat !== NULL) {
foreach ($this->xlat as $col => $type) {
if (isset($row[$col])) {
$row[$col] = $this->convert($row[$col], $type['type'], $type['format']);
}
}
$this->normalize($row);
if ($this->rowClass) {
$row = new $this->rowClass($row);
}
return new $this->class($row);
return $row;
}
@@ -250,19 +230,13 @@ class DibiResult extends DibiObject implements IDataSource
*/
final public function fetchSingle()
{
$row = $this->getDriver()->fetch(TRUE);
if (!is_array($row)) return FALSE;
$this->fetched = TRUE;
$value = reset($row);
// types-converting?
$key = key($row);
if (isset($this->xlat[$key])) {
$type = $this->xlat[$key];
return $this->convert($value, $type['type'], $type['format']);
$row = $this->getResultDriver()->fetch(TRUE);
if (!is_array($row)) {
return FALSE;
}
return $value;
$this->fetched = TRUE;
$this->normalize($row);
return reset($row);
}
@@ -294,13 +268,87 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Fetches all records from table and returns associative tree.
* Associative descriptor: assoc1,#,assoc2,=,assoc3,@
* builds a tree: $data[assoc1][index][assoc2]['assoc3']->value = {record}
* Examples:
* - associative descriptor: col1[]col2->col3
* builds a tree: $tree[$val1][$index][$val2]->col3[$val3] = {record}
* - associative descriptor: col1|col2->col3=col4
* builds a tree: $tree[$val1][$val2]->col3[$val3] = val4
* @param string associative descriptor
* @return DibiRow
* @throws InvalidArgumentException
*/
final public function fetchAssoc($assoc)
{
if (strpos($assoc, ',') !== FALSE) {
return $this->oldFetchAssoc($assoc);
}
$this->seek(0);
$row = $this->fetch();
if (!$row) return array(); // empty result set
$data = NULL;
$assoc = preg_split('#(\[\]|->|=|\|)#', $assoc, NULL, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
// check columns
foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
if ($as !== '[]' && $as !== '=' && $as !== '->' && $as !== '|' && !property_exists($row, $as)) {
throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
}
}
if ($as === '->') { // must not be last
array_pop($assoc);
}
if (empty($assoc)) {
$assoc[] = '[]';
}
// make associative tree
do {
$x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '[]') { // indexed-array node
$x = & $x[];
} elseif ($as === '=') { // "value" node
$x = $row->{$assoc[$i+1]};
continue 2;
} elseif ($as === '->') { // "object" node
if ($x === NULL) {
$x = clone $row;
$x = & $x->{$assoc[$i+1]};
$x = NULL; // prepare child node
} else {
$x = & $x->{$assoc[$i+1]};
}
} elseif ($as !== '|') { // associative-array node
$x = & $x[$row->$as];
}
}
if ($x === NULL) { // build leaf
$x = $row;
}
} while ($row = $this->fetch());
unset($x);
return $data;
}
/**
* @deprecated
*/
private function oldFetchAssoc($assoc)
{
$this->seek(0);
$row = $this->fetch();
@@ -309,14 +357,6 @@ class DibiResult extends DibiObject implements IDataSource
$data = NULL;
$assoc = explode(',', $assoc);
// check columns
foreach ($assoc as $as) {
// offsetExists ignores NULL in PHP 5.2.1, isset() surprisingly NULL accepts
if ($as !== '#' && $as !== '=' && $as !== '@' && !isset($row[$as])) {
throw new InvalidArgumentException("Unknown column '$as' in associative descriptor.");
}
}
// strip leading = and @
$leaf = '@'; // gap
$last = count($assoc) - 1;
@@ -331,19 +371,16 @@ class DibiResult extends DibiObject implements IDataSource
}
}
// make associative tree
do {
$arr = (array) $row;
$x = & $data;
// iterative deepening
foreach ($assoc as $i => $as) {
if ($as === '#') { // indexed-array node
$x = & $x[];
} elseif ($as === '=') { // "record" node
if ($x === NULL) {
$x = $arr;
$x = $row->toArray();
$x = & $x[ $assoc[$i+1] ];
$x = NULL; // prepare child node
} else {
@@ -361,13 +398,13 @@ class DibiResult extends DibiObject implements IDataSource
} else { // associative-array node
$x = & $x[ $arr[ $as ] ];
$x = & $x[$row->$as];
}
}
if ($x === NULL) { // build leaf
if ($leaf === '=') {
$x = $arr;
$x = $row->toArray();
} else {
$x = $row;
}
@@ -402,7 +439,7 @@ class DibiResult extends DibiObject implements IDataSource
}
// autodetect
$tmp = array_keys((array) $row);
$tmp = array_keys($row->toArray());
$key = $tmp[0];
if (count($row) < 2) { // indexed-array
do {
@@ -414,7 +451,7 @@ class DibiResult extends DibiObject implements IDataSource
$value = $tmp[1];
} else {
if (!isset($row[$value])) {
if (!property_exists($row, $value)) {
throw new InvalidArgumentException("Unknown value column '$value'.");
}
@@ -425,7 +462,7 @@ class DibiResult extends DibiObject implements IDataSource
return $data;
}
if (!isset($row[$key])) {
if (!property_exists($row, $key)) {
throw new InvalidArgumentException("Unknown key column '$key'.");
}
}
@@ -439,17 +476,7 @@ class DibiResult extends DibiObject implements IDataSource
/**
* Define column type.
* @param string column
* @param string type (use constant Dibi::*)
* @param string optional format
* @return void
*/
final public function setType($col, $type, $format = NULL)
{
$this->xlat[$col] = array('type' => $type, 'format' => $format);
}
/********************* column types ****************d*g**/
@@ -457,112 +484,159 @@ class DibiResult extends DibiObject implements IDataSource
* Autodetect column types.
* @return void
*/
final public function detectTypes()
private function detectTypes()
{
foreach ($this->getMeta() as $info) {
$this->xlat[$info['name']] = array('type' => $info['type'], 'format' => NULL);
$cache = DibiColumnInfo::getTypeCache();
try {
foreach ($this->getResultDriver()->getResultColumns() as $col) {
$this->types[$col['name']] = $cache->{$col['nativetype']};
}
} catch (DibiNotSupportedException $e) {}
}
/**
* Converts values to specified type and format.
* @param array
* @return void
*/
private function normalize(array & $row)
{
foreach ($this->types as $key => $type) {
if (!isset($row[$key])) { // NULL
continue;
}
$value = $row[$key];
if ($value === FALSE || $type === dibi::TEXT) {
} elseif ($type === dibi::INTEGER) {
$row[$key] = is_float($tmp = $value * 1) ? $value : $tmp;
} elseif ($type === dibi::FLOAT) {
$row[$key] = (string) ($tmp = (float) $value) === $value ? $tmp : $value;
} elseif ($type === dibi::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);
} else {
$value = new DibiDateTime($value);
$row[$key] = $value->format($this->formats[$type]);
}
} elseif ($type === dibi::BINARY) {
$row[$key] = $this->getResultDriver()->unescape($value, $type);
}
}
}
/**
* Define multiple columns types.
* @param array
* @return void
* @internal
* Define column type.
* @param string column
* @param string type (use constant Dibi::*)
* @return DibiResult provides a fluent interface
*/
final public function setTypes(array $types)
final public function setType($col, $type)
{
$this->xlat = $types;
$this->types[$col] = $type;
return $this;
}
/**
* Returns column type.
* @return array ($type, $format)
* @return string
*/
final public function getType($col)
{
return isset($this->xlat[$col]) ? $this->xlat[$col] : NULL;
return isset($this->types[$col]) ? $this->types[$col] : NULL;
}
/**
* Converts value to specified type and format.
* @param mixed value
* @param int type
* @param string format
* @return mixed
* Sets data format.
* @param string type (use constant Dibi::*)
* @param string format
* @return DibiResult provides a fluent interface
*/
final public function convert($value, $type, $format = NULL)
final public function setFormat($type, $format)
{
if ($value === NULL || $value === FALSE) {
return $value;
}
switch ($type) {
case dibi::TEXT:
return (string) $value;
case dibi::BINARY:
return $this->getDriver()->unescape($value, $type);
case dibi::INTEGER:
return (int) $value;
case dibi::FLOAT:
return (float) $value;
case dibi::DATE:
case dibi::DATETIME:
$value = is_numeric($value) ? (int) $value : strtotime($value);
return $format === NULL ? $value : date($format, $value);
case dibi::BOOL:
return ((bool) $value) && $value !== 'f' && $value !== 'F';
default:
return $value;
}
$this->formats[$type] = $format;
return $this;
}
/**
* Gets an array of meta informations about columns.
* @return array of DibiColumnInfo
* Returns data format.
* @return string
*/
final public function getFormat($type)
{
return isset($this->formats[$type]) ? $this->formats[$type] : NULL;
}
/********************* meta info ****************d*g**/
/**
* Returns a meta information about the current result set.
* @return DibiResultInfo
*/
public function getInfo()
{
if ($this->meta === NULL) {
$this->meta = new DibiResultInfo($this->getResultDriver());
}
return $this->meta;
}
/**
* @deprecated
*/
final public function getColumns()
{
$cols = array();
foreach ($this->getMeta() as $info) {
$cols[] = new DibiColumnInfo($this->getDriver(), $info);
}
return $cols;
return $this->getInfo()->getColumns();
}
/**
* @param bool
* @return array of string
*/
public function getColumnNames($withTables = FALSE)
/** @deprecated */
public function getColumnNames($fullNames = FALSE)
{
$cols = array();
foreach ($this->getMeta() as $info) {
$cols[] = $info[$withTables ? 'fullname' : 'name'];
}
return $cols;
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 table for debug purposes.
* @return void
*/
final public function dump()
@@ -596,61 +670,4 @@ class DibiResult extends DibiObject implements IDataSource
}
}
/**
* Required by the IteratorAggregate interface.
* @param int offset
* @param int limit
* @return DibiResultIterator
*/
final public function getIterator($offset = NULL, $limit = NULL)
{
return new DibiResultIterator($this, $offset, $limit);
}
/**
* Required by the Countable interface.
* @return int
*/
final public function count()
{
return $this->getRowCount();
}
/**
* Safe access to property $driver.
* @return IDibiDriver
* @throws InvalidStateException
*/
private function getDriver()
{
if ($this->driver === NULL) {
throw new InvalidStateException('Result-set was released from memory.');
}
return $this->driver;
}
/**
* Meta lazy initialization.
* @return array
*/
private function getMeta()
{
if ($this->meta === NULL) {
$this->meta = $this->getDriver()->getColumnsMeta();
foreach ($this->meta as & $row) {
$row['type'] = DibiColumnInfo::detectType($row['nativetype']);
}
}
return $this->meta;
}
}

View File

@@ -1,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -31,15 +23,7 @@
* unset($result);
* </code>
*
* Optionally you can specify offset and limit:
* <code>
* foreach ($result->getIterator(2, 3) as $row) {
* print_r($row);
* }
* </code>
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class DibiResultIterator implements Iterator, Countable
@@ -47,12 +31,6 @@ class DibiResultIterator implements Iterator, Countable
/** @var DibiResult */
private $result;
/** @var int */
private $offset;
/** @var int */
private $limit;
/** @var int */
private $row;
@@ -62,14 +40,10 @@ class DibiResultIterator implements Iterator, Countable
/**
* @param DibiResult
* @param int offset
* @param int limit
*/
public function __construct(DibiResult $result, $offset = NULL, $limit = NULL)
public function __construct(DibiResult $result)
{
$this->result = $result;
$this->offset = (int) $offset;
$this->limit = $limit === NULL ? -1 : (int) $limit;
}
@@ -81,7 +55,7 @@ class DibiResultIterator implements Iterator, Countable
public function rewind()
{
$this->pointer = 0;
$this->result->seek($this->offset);
$this->result->seek(0);
$this->row = $this->result->fetch();
}
@@ -115,7 +89,6 @@ class DibiResultIterator implements Iterator, Countable
*/
public function next()
{
//$this->result->seek($this->offset + $this->pointer + 1);
$this->row = $this->result->fetch();
$this->pointer++;
}
@@ -128,7 +101,7 @@ class DibiResultIterator implements Iterator, Countable
*/
public function valid()
{
return !empty($this->row) && ($this->limit < 0 || $this->pointer < $this->limit);
return !empty($this->row);
}

View File

@@ -1,60 +1,71 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* Result-set single row.
* Result set single row.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
class DibiRow extends ArrayObject
class DibiRow implements ArrayAccess, IteratorAggregate, Countable
{
/**
* @param array
*/
public function __construct($arr)
{
parent::__construct($arr, 2);
foreach ($arr as $k => $v) $this->$k = $v;
}
public function toArray()
{
return (array) $this;
}
/**
* Converts value to date-time format.
* Converts value to DateTime object.
* @param string key
* @param string format
* @return mixed
* @return DateTime
*/
public function asDate($key, $format = NULL)
public function asDateTime($key, $format = NULL)
{
$value = $this[$key];
if ($value === NULL || $value === FALSE) {
return $value;
} else {
$value = is_numeric($value) ? (int) $value : strtotime($value);
return $format === NULL ? $value : date($format, $value);
$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));
}
@@ -66,24 +77,67 @@ class DibiRow extends ArrayObject
*/
public function asBool($key)
{
$value = $this[$key];
if ($value === NULL || $value === FALSE) {
return $value;
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 ((bool) $value) && $value !== 'f' && $value !== 'F';
return $this->asDateTime($key, $format === TRUE ? NULL : $format);
}
}
/**
* PHP < 5.3 workaround
* @return void
*/
public function __wakeup()
/********************* interfaces ArrayAccess, Countable & IteratorAggregate ****************d*g**/
final public function count()
{
$this->setFlags(2);
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,20 +1,12 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
@@ -23,11 +15,13 @@
* dibi SQL translator.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
final class DibiTranslator extends DibiObject
{
/** @var DibiConnection */
private $connection;
/** @var IDibiDriver */
private $driver;
@@ -55,21 +49,14 @@ final class DibiTranslator extends DibiObject
/** @var int */
private $offset;
/** @var DibiHashMap */
private $identifiers;
public function __construct(IDibiDriver $driver)
public function __construct(DibiConnection $connection)
{
$this->driver = $driver;
}
/**
* return IDibiDriver.
*/
public function getDriver()
{
return $this->driver;
$this->connection = $connection;
}
@@ -82,6 +69,15 @@ final class DibiTranslator extends DibiObject
*/
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;
@@ -90,8 +86,6 @@ final class DibiTranslator extends DibiObject
// shortcuts
$cursor = & $this->cursor;
$cursor = 0;
$this->args = array_values($args);
$args = & $this->args;
// conditional sql
$this->ifLevel = $this->ifLevelStart = 0;
@@ -100,22 +94,22 @@ final class DibiTranslator extends DibiObject
// iterate
$sql = array();
while ($cursor < count($args))
while ($cursor < count($this->args))
{
$arg = $args[$cursor];
$arg = $this->args[$cursor];
$cursor++;
// simple string means SQL
if (is_string($arg)) {
// speed-up - is regexp required?
$toSkip = strcspn($arg, '`[\'":%');
$toSkip = strcspn($arg, '`[\'":%?');
if (strlen($arg) === $toSkip) { // needn't be translated
$sql[] = $arg;
} else {
$sql[] = substr($arg, 0, $toSkip)
/*
preg_replace_callback('/
. preg_replace_callback('/
(?=[`[\'":%?]) ## speed-up
(?:
`(.+?)`| ## 1) `identifier`
@@ -123,16 +117,16 @@ final class DibiTranslator extends DibiObject
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
(")((?:""|[^"])*)"| ## 5,6) "string"
(\'|")| ## 7) lone quote
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) substitution
%([a-zA-Z]{1,4})(?![a-zA-Z]) ## 10) modifier
:(\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]{1,4})(?![a-zA-Z])|(\?))/s',
. 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;
}
@@ -142,15 +136,15 @@ final class DibiTranslator extends DibiObject
continue;
}
if ($arg instanceof ArrayObject) {
$arg = (array) $arg;
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($args[0]), 0, 6));
$commandIns = strtoupper(substr(ltrim($this->args[0]), 0, 6));
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
} else {
@@ -159,12 +153,6 @@ final class DibiTranslator extends DibiObject
}
$lastArr = $cursor;
continue;
} elseif ($cursor === 1) {
// implicit array expansion
$cursor = 0;
array_splice($args, 0, 1, $arg);
continue;
}
}
@@ -199,9 +187,13 @@ final class DibiTranslator extends DibiObject
*/
public function formatValue($value, $modifier)
{
if ($this->comment) {
return "...";
}
// array processing (with or without modifier)
if ($value instanceof ArrayObject) {
$value = (array) $value;
if ($value instanceof Traversable) {
$value = iterator_to_array($value);
}
if (is_array($value)) {
@@ -216,7 +208,7 @@ final class DibiTranslator extends DibiObject
foreach ($value as $k => $v) {
if (is_string($k)) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$k = $this->delimite($pair[0]) . ' ';
$k = $this->identifiers->{$pair[0]} . ' ';
if (!isset($pair[1])) {
$v = $this->formatValue($v, FALSE);
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
@@ -226,7 +218,16 @@ final class DibiTranslator extends DibiObject
} else {
$v = $this->formatValue($v, $pair[1]);
$vx[] = $k . ($pair[1] === 'l' ? 'IN ' : ($v === 'NULL' ? 'IS ' : '= ')) . $v;
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 {
@@ -238,10 +239,10 @@ final class DibiTranslator extends DibiObject
case 'n': // key, key, ... identifier names
foreach ($value as $k => $v) {
if (is_string($k)) {
$vx[] = $this->delimite($k) . (empty($v) ? '' : ' AS ' . $v);
$vx[] = $this->identifiers->$k . (empty($v) ? '' : ' AS ' . $this->identifiers->$v);
} else {
$pair = explode('%', $v, 2); // split into identifier & modifier
$vx[] = $this->delimite($pair[0]);
$vx[] = $this->identifiers->{$pair[0]};
}
}
return implode(', ', $vx);
@@ -250,24 +251,25 @@ final class DibiTranslator extends DibiObject
case 'a': // key=val, key=val, ...
foreach ($value as $k => $v) {
$pair = explode('%', $k, 2); // split into identifier & modifier
$vx[] = $this->delimite($pair[0]) . '='
$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 ? implode(', ', $vx) : 'NULL') . ')';
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->delimite($pair[0]);
$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) . ')';
@@ -289,50 +291,46 @@ final class DibiTranslator extends DibiObject
}
$pair = explode('%', $k, 2); // split into identifier & modifier
$kx[] = $this->delimite($pair[0]);
$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] = '(' . ($v ? implode(', ', $v) : 'NULL') . ')';
$vx[$k] = '(' . implode(', ', $v) . ')';
}
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
case 'by': // key ASC, key DESC
foreach ($value as $k => $v) {
if (is_string($k)) {
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->delimite($k) . ' ' . $v;
$vx[] = $this->identifiers->$k . ' ' . $v;
} else {
$vx[] = $this->delimite($v);
$vx[] = $this->identifiers->$v;
}
}
return implode(', ', $vx);
case 'ex':
case 'sql':
$translator = new self($this->driver);
$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 $vx ? implode(', ', $vx) : 'NULL';
return implode(', ', $vx);
}
}
// with modifier procession
if ($modifier) {
if ($value instanceof IDibiVariable) {
return $value->toSql($this, $modifier);
} elseif ($value instanceof DateTime) {
$value = $value->format('U');
} elseif ($value !== NULL && !is_scalar($value)) { // array is already processed
if ($value !== NULL && !is_scalar($value) && !($value instanceof DateTime)) { // array is already processed
$this->hasError = TRUE;
return '**Unexpected type ' . gettype($value) . '**';
}
@@ -343,17 +341,19 @@ final class DibiTranslator extends DibiObject
case 'b': // boolean
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
case 'sn': // string or NULL
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': // 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)) {
if (is_string($value) && preg_match('#[+-]?\d++(e\d+)?$#A', $value)) {
return $value;
} else {
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
@@ -364,7 +364,7 @@ final class DibiTranslator extends DibiObject
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, 5, '.', ''), '0'), '.');
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value + 0, 5, '.', ''), '0'), '.');
}
case 'd': // date
@@ -372,32 +372,47 @@ final class DibiTranslator extends DibiObject
if ($value === NULL) {
return 'NULL';
} else {
return $this->driver->escape(is_numeric($value) ? (int) $value : strtotime($value), $modifier);
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->delimite($value);
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) { // needn't be translated
return $value;
} else {
return substr($value, 0, $toSkip)
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':
@@ -414,26 +429,31 @@ final class DibiTranslator extends DibiObject
// without modifier procession
if (is_string($value))
if (is_string($value)) {
return $this->driver->escape($value, dibi::TEXT);
if (is_int($value) || is_float($value))
} elseif (is_int($value)) {
return (string) $value;
} elseif (is_float($value)) {
return rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
if (is_bool($value))
} elseif (is_bool($value)) {
return $this->driver->escape($value, dibi::BOOL);
if ($value === NULL)
} elseif ($value === NULL) {
return 'NULL';
if ($value instanceof IDibiVariable)
return $value->toSql($this, NULL);
} elseif ($value instanceof DateTime) {
return $this->driver->escape($value, dibi::DATETIME);
if ($value instanceof DateTime)
return $value = $value->format('U');
} elseif ($value instanceof DibiLiteral) {
return (string) $value;
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
} else {
$this->hasError = TRUE;
return '**Unexpected ' . gettype($value) . '**';
}
}
@@ -534,10 +554,10 @@ final class DibiTranslator extends DibiObject
if ($this->comment) return '...';
if ($matches[1]) // SQL identifiers: `ident`
return $this->delimite($matches[1]);
return $this->identifiers->{$matches[1]};
if ($matches[2]) // SQL identifiers: [ident]
return $this->delimite($matches[2]);
return $this->identifiers->{$matches[2]};
if ($matches[3]) // SQL strings: '...'
return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::TEXT);
@@ -552,7 +572,7 @@ final class DibiTranslator extends DibiObject
if ($matches[8]) { // SQL identifier substitution
$m = substr($matches[8], 0, -1);
$m = isset(dibi::$substs[$m]) ? dibi::$substs[$m] : call_user_func(dibi::$substFallBack, $m);
$m = $this->connection->getSubstitutes()->$m;
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
}
@@ -565,30 +585,16 @@ final class DibiTranslator extends DibiObject
* Apply substitutions to indentifier and delimites it.
* @param string indentifier
* @return string
* @internal
*/
private function delimite($value)
public function delimite($value)
{
if ($value === '*') {
return '*';
} elseif (strpos($value, ':') !== FALSE) { // provide substitution
$value = preg_replace_callback('#:(.*):#U', array(__CLASS__, 'subCb'), $value);
$value = $this->connection->substitute($value);
$parts = explode('.', $value);
foreach ($parts as & $v) {
if ($v !== '*') $v = $this->driver->escape($v, dibi::IDENTIFIER);
}
return $this->driver->escape($value, dibi::IDENTIFIER);
}
/**
* Substitution callback.
* @param array
* @return string
*/
private static function subCb($m)
{
$m = $m[1];
return isset(dibi::$substs[$m]) ? dibi::$substs[$m] : call_user_func(dibi::$substFallBack, $m);
return implode('.', $parts);
}
}

View File

@@ -1,48 +0,0 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
*/
/**
* Default implemenation of IDibiVariable.
* @package dibi
*/
class DibiVariable extends DibiObject implements IDibiVariable
{
/** @var mixed */
public $value;
/** @var string */
public $modifier;
public function __construct($value, $modifier)
{
$this->value = $value;
$this->modifier = $modifier;
}
public function toSql(DibiTranslator $translator, $modifier)
{
return $translator->formatValue($this->value, $this->modifier);
}
}

View File

@@ -1,46 +1,19 @@
<?php
/**
* dibi - tiny'n'smart database abstraction layer
* ----------------------------------------------
* This file is part of the "dibi" - smart database abstraction layer.
*
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
* Copyright (c) 2005 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "dibi license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://dibiphp.com
*
* @copyright Copyright (c) 2005, 2009 David Grudl
* @license http://dibiphp.com/license dibi license
* @link http://dibiphp.com
* @package dibi
* For the full copyright and license information, please view
* the file license.txt that was distributed with this source code.
*/
/**
* Interface for user variable, used for generating SQL.
* @package dibi
*/
interface IDibiVariable
{
/**
* Format for SQL.
* @param DibiTranslator
* @param string optional modifier
* @return string SQL code
*/
function toSql(DibiTranslator $translator, $modifier);
}
/**
* Provides an interface between a dataset and data-aware components.
* @package dibi
* @package dibi
*/
interface IDataSource extends Countable, IteratorAggregate
{
@@ -50,64 +23,8 @@ interface IDataSource extends Countable, IteratorAggregate
/**
* Defines method that must profiler implement.
* @package dibi
*/
interface IDibiProfiler
{
/**#@+ event type */
const CONNECT = 1;
const SELECT = 4;
const INSERT = 8;
const DELETE = 16;
const UPDATE = 32;
const QUERY = 60;
const BEGIN = 64;
const COMMIT = 128;
const ROLLBACK = 256;
const TRANSACTION = 448;
const EXCEPTION = 512;
const ALL = 1023;
/**#@-*/
/**
* Before event notification.
* @param DibiConnection
* @param int event name
* @param string sql
* @return int
*/
function before(DibiConnection $connection, $event, $sql = NULL);
/**
* After event notification.
* @param int
* @param DibiResult
* @return void
*/
function after($ticket, $result = NULL);
/**
* After exception notification.
* @param DibiDriverException
* @return void
*/
function exception(DibiDriverException $exception);
}
/**
* dibi driver interface.
*
* @author David Grudl
* @copyright Copyright (c) 2005, 2009 David Grudl
* @package dibi
*/
interface IDibiDriver
@@ -131,7 +48,7 @@ interface IDibiDriver
/**
* Internal: Executes the SQL query.
* @param string SQL statement.
* @return IDibiDriver|NULL
* @return IDibiResultDriver|NULL
* @throws DibiDriverException
*/
function query($sql);
@@ -178,11 +95,11 @@ interface IDibiDriver
*/
function getResource();
/********************* SQL ****************d*g**/
/**
* Returns the connection reflector.
* @return IDibiReflector
*/
function getReflector();
/**
* Encodes data for use in a SQL statement.
@@ -194,13 +111,12 @@ interface IDibiDriver
function escape($value, $type);
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
* Encodes string for use in a LIKE statement.
* @param string
* @param int
* @return string
*/
function unescape($value, $type);
function escapeLike($value, $pos);
/**
* Injects LIMIT/OFFSET to the SQL query.
@@ -211,12 +127,19 @@ interface IDibiDriver
*/
function applyLimit(&$sql, $limit, $offset);
}
/********************* result set ****************d*g**/
/**
* dibi result set driver interface.
* @package dibi
*/
interface IDibiResultDriver
{
/**
* Returns the number of rows in a result set.
* @return int
@@ -248,10 +171,9 @@ interface IDibiDriver
/**
* Returns metadata for all columns in a result set.
* @return array
* @throws DibiException
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
*/
function getColumnsMeta();
function getResultColumns();
/**
* Returns the result set resource.
@@ -259,29 +181,47 @@ interface IDibiDriver
*/
function getResultResource();
/**
* Decodes data from result set.
* @param string value
* @param string type (dibi::BINARY)
* @return string decoded value
* @throws InvalidArgumentException
*/
function unescape($value, $type);
}
/********************* reflection ****************d*g**/
/**
* dibi driver reflection.
*
* @author David Grudl
* @package dibi
*/
interface IDibiReflector
{
/**
* Returns list of tables.
* @return array
* @return array of {name [, (bool) view ]}
*/
function getTables();
/**
* Returns metadata for all columns in a table.
* @param string
* @return array
* @return array of {name, nativetype [, table, fullname, (int) size, (bool) nullable, (mixed) default, (bool) autoincrement, (array) vendor ]}
*/
function getColumns($table);
/**
* Returns metadata for all indexes in a table.
* @param string
* @return array
* @return array of {name, (array of names) columns [, (bool) unique, (bool) primary ]}
*/
function getIndexes($table);

View File

@@ -1,2 +0,0 @@
Disallow: /drivers
Disallow: /Nette

View File

@@ -1,917 +0,0 @@
<?php
/**
* Nette Framework
*
* Copyright (c) 2004, 2009 David Grudl (http://davidgrudl.com)
*
* This source file is subject to the "Nette license" that is bundled
* with this package in the file license.txt.
*
* For more information please see http://nettephp.com
*
* @copyright Copyright (c) 2004, 2009 David Grudl
* @license http://nettephp.com/license Nette license
* @link http://nettephp.com
* @category Nette
* @package Nette
*/
if(version_compare(PHP_VERSION,'5.2.0','<')){throw
new
Exception('Nette Framework requires PHP 5.2.0 or newer.');}@set_magic_quotes_runtime(FALSE);if(version_compare(PHP_VERSION,'5.2.2','<')){function
fixCallback(&$callback){if(is_object($callback)){$callback=array($callback,'__invoke');return;}if(is_string($callback)&&strpos($callback,':')){$callback=explode('::',$callback);}if(is_array($callback)&&is_string($callback[0])&&$a=strrpos($callback[0],'\\')){$callback[0]=substr($callback[0],$a+1);}}}else{function
fixCallback(&$callback){if(is_object($callback)){$callback=array($callback,'__invoke');}elseif(is_string($callback)&&$a=strrpos($callback,'\\')){$callback=substr($callback,$a+1);}elseif(is_array($callback)&&is_string($callback[0])&&$a=strrpos($callback[0],'\\')){$callback[0]=substr($callback[0],$a+1);}}}function
fixNamespace(&$class){if($a=strrpos($class,'\\')){$class=substr($class,$a+1);}}class
ArgumentOutOfRangeException
extends
InvalidArgumentException{}class
InvalidStateException
extends
RuntimeException{function
__construct($message='',$code=0,Exception$previous=NULL){if(version_compare(PHP_VERSION,'5.3','<')){$this->previous=$previous;parent::__construct($message,$code);}else{parent::__construct($message,$code,$previous);}}}class
NotImplementedException
extends
LogicException{}class
NotSupportedException
extends
LogicException{}class
DeprecatedException
extends
NotSupportedException{}class
MemberAccessException
extends
LogicException{}class
IOException
extends
RuntimeException{}class
FileNotFoundException
extends
IOException{}class
DirectoryNotFoundException
extends
IOException{}class
FatalErrorException
extends
Exception{private$severity;public
function
__construct($message,$code,$severity,$file,$line,$context){parent::__construct($message,$code);$this->severity=$severity;$this->file=$file;$this->line=$line;$this->context=$context;}public
function
getSeverity(){return$this->severity;}}final
class
Framework{const
NAME='Nette Framework';const
VERSION='0.9';const
REVISION='424 released on 2009/07/15 12:03:47';const
PACKAGE='PHP 5.2';final
public
function
__construct(){throw
new
LogicException("Cannot instantiate static class ".get_class($this));}public
static
function
compareVersion($version){return
version_compare($version,self::VERSION);}public
static
function
promo($xhtml=TRUE){echo'<a href="http://nettephp.com/" title="Nette Framework - The Most Innovative PHP Framework"><img ','src="http://nettephp.com/images/nette-powered.gif" alt="Powered by Nette Framework" width="80" height="15"',($xhtml?' />':'>'),'</a>';}}final
class
Debug{public
static$productionMode;public
static$consoleMode;public
static$time;private
static$firebugDetected;private
static$ajaxDetected;private
static$consoleData;public
static$maxDepth=3;public
static$maxLen=150;public
static$showLocation=FALSE;const
DEVELOPMENT=FALSE;const
PRODUCTION=TRUE;const
DETECT=NULL;public
static$strictMode=FALSE;public
static$onFatalError=array();public
static$mailer=array(__CLASS__,'defaultMailer');private
static$enabled=FALSE;private
static$logFile;private
static$logHandle;private
static$sendEmails;private
static$emailHeaders=array('To'=>'','From'=>'noreply@%host%','X-Mailer'=>'Nette Framework','Subject'=>'PHP: An error occurred on the server %host%','Body'=>'[%date%] %message%');private
static$colophons=array(array(__CLASS__,'getDefaultColophons'));private
static$enabledProfiler=FALSE;public
static$counters=array();const
LOG='LOG';const
INFO='INFO';const
WARN='WARN';const
ERROR='ERROR';const
TRACE='TRACE';const
EXCEPTION='EXCEPTION';const
GROUP_START='GROUP_START';const
GROUP_END='GROUP_END';final
public
function
__construct(){throw
new
LogicException("Cannot instantiate static class ".get_class($this));}public
static
function
init(){self::$time=microtime(TRUE);self::$consoleMode=PHP_SAPI==='cli';self::$productionMode=self::DETECT;self::$firebugDetected=isset($_SERVER['HTTP_USER_AGENT'])&&strpos($_SERVER['HTTP_USER_AGENT'],'FirePHP/');self::$ajaxDetected=isset($_SERVER['HTTP_X_REQUESTED_WITH'])&&$_SERVER['HTTP_X_REQUESTED_WITH']==='XMLHttpRequest';register_shutdown_function(array(__CLASS__,'shutdownHandler'));}public
static
function
shutdownHandler(){static$types=array(E_ERROR=>1,E_CORE_ERROR=>1,E_COMPILE_ERROR=>1,E_PARSE=>1);$error=error_get_last();if(isset($types[$error['type']])){if(!headers_sent()){header('HTTP/1.1 500 Internal Server Error');}if(ini_get('html_errors')){$error['message']=html_entity_decode(strip_tags($error['message']));}self::processException(new
FatalErrorException($error['message'],0,$error['type'],$error['file'],$error['line'],NULL),TRUE);}if(self::$productionMode){return;}foreach(headers_list()as$header){if(strncasecmp($header,'Content-Type:',13)===0){if(substr($header,14,9)==='text/html'){break;}return;}}if(self::$enabledProfiler){if(self::$firebugDetected){self::fireLog('Nette profiler',self::GROUP_START);foreach(self::$colophons
as$callback){foreach((array)call_user_func($callback,'profiler')as$line)self::fireLog(strip_tags($line));}self::fireLog(NULL,self::GROUP_END);}if(!self::$ajaxDetected){$colophons=self::$colophons;?>
<style type="text/css">
/* <![CDATA[ */
#netteProfilerContainer {
position: fixed;
_position: absolute;
right: 5px;
bottom: 5px;
z-index: 23178;
}
#netteProfiler {
font: normal normal 11px/1.4 Consolas, Arial;
position: relative;
padding: 1px;
color: black;
background: #EEE;
border: 1px dotted gray;
cursor: move;
opacity: .70;
=filter: alpha(opacity=70);
}
#netteProfiler * {
color: inherit;
background: inherit;
text-align: inherit;
}
#netteProfilerIcon {
position: absolute;
right: 0;
top: 0;
line-height: 1;
padding: 4px;
color: black;
text-decoration: none;
}
#netteProfiler:hover {
opacity: 1;
=filter: none;
}
#netteProfiler ul {
margin: 0;
padding: 0;
width: 350px;
}
#netteProfiler li {
margin: 0;
padding: 1px;
text-align: left;
list-style: none;
}
#netteProfiler span[title] {
border-bottom: 1px dotted gray;
cursor: help;
}
#netteProfiler strong {
color: red;
}
/* ]]> */
</style>
<div id="netteProfilerContainer">
<div id="netteProfiler">
<a id="netteProfilerIcon" href="#"><abbr>&#x25bc;</abbr></a
><ul>
<?php foreach($colophons
as$callback):?>
<?php foreach((array)call_user_func($callback,'profiler')as$line):?><li><?php echo$line,"\n"?></li><?php endforeach?>
<?php endforeach?>
</ul>
</div>
</div>
<script type="text/javascript">
/* <![CDATA[ */
document.getElementById('netteProfiler').onmousedown = function(e) {
e = e || event;
this.posX = parseInt(this.style.left + '0');
this.posY = parseInt(this.style.top + '0');
this.mouseX = e.clientX;
this.mouseY = e.clientY;
var thisObj = this;
document.documentElement.onmousemove = function(e) {
e = e || event;
thisObj.style.left = (e.clientX - thisObj.mouseX + thisObj.posX) + "px";
thisObj.style.top = (e.clientY - thisObj.mouseY + thisObj.posY) + "px";
return false;
};
document.documentElement.onmouseup = function(e) {
document.documentElement.onmousemove = null;
document.documentElement.onmouseup = null;
return false;
};
};
document.getElementById('netteProfilerIcon').onclick = function(e) {
var arrow = this.getElementsByTagName('abbr')[0];
var panel = this.nextSibling;
var collapsed = panel.currentStyle ? panel.currentStyle.display == 'none' : getComputedStyle(panel, null).display == 'none';
arrow.innerHTML = collapsed ? String.fromCharCode(0x25bc) : 'Profiler ' + String.fromCharCode(0x25ba);
panel.style.display = collapsed ? 'block' : 'none';
arrow.parentNode.style.position = collapsed ? 'absolute' : 'static';
return false;
}
document.body.appendChild(document.getElementById('netteProfilerContainer'));
/* ]]> */
</script>
<?php }}if(self::$consoleData){$payload=self::$consoleData;if(!function_exists('_netteDumpCb2')){function
_netteDumpCb2($m){return"$m[1]<a href='#' onclick='return !netteToggle(this)'>$m[2]($m[3]) ".($m[3]<7?'<abbr>&#x25bc;</abbr> </a><code>':'<abbr>&#x25ba;</abbr> </a><code class="collapsed">');}}ob_start();?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex,noarchive">
<meta name="generator" content="Nette Framework">
<title>Nette Debug Console</title>
<style type="text/css">
/* <![CDATA[ */
body {
margin: 0;
padding: 0;
font: 9pt/1.5 Verdana, sans-serif;
background: white;
color: #333;
}
h1 {
font-size: 13pt;
margin: 0;
padding: 2px 8px;
background: black;
color: white;
border-bottom: 1px solid black;
}
h2 {
font: 11pt/1.5 sans-serif;
margin: 0;
padding: 2px 8px;
background: #3484d2;
color: white;
}
a {
text-decoration: none;
color: #4197E3;
}
a abbr {
font-family: sans-serif;
color: #999;
}
p {
margin: .8em 0
}
pre, code, table {
font: 9pt/1.5 Consolas, monospace;
}
pre, table {
background: #fffbcc;
padding: .4em .7em;
border: 1px dotted silver;
}
table pre {
padding: 0;
margin: 0;
border: none;
}
pre.dump span {
color: #c16549;
}
pre.dump a {
color: #333;
}
table {
border-collapse: collapse;
width: 100%;
}
td, th {
vertical-align: top;
text-align: left;
border: 1px solid #eeeebb;
}
th {
width: 10;
padding: 2px 3px 2px 8px;
font-weight: bold;
}
td {
padding: 2px 8px 2px 3px;
}
.odd, .odd pre {
background: #faf5c3;
}
/* ]]> */
</style>
<script type="text/javascript">
/* <![CDATA[ */
document.write('<style> .collapsed { display: none; } <\/style>');
function netteToggle(link, panelId)
{
var arrow = link.getElementsByTagName('abbr')[0];
var panel = panelId ? document.getElementById(panelId) : link.nextSibling;
while (panel.nodeType !== 1) panel = panel.nextSibling;
var collapsed = panel.currentStyle ? panel.currentStyle.display == 'none' : getComputedStyle(panel, null).display == 'none';
arrow.innerHTML = String.fromCharCode(collapsed ? 0x25bc : 0x25ba);
panel.style.display = collapsed ? (panel.tagName.toLowerCase() === 'code' ? 'inline' : 'block') : 'none';
return true;
}
/* ]]> */
</script>
</head>
<body>
<h1>Nette Debug Console</h1>
</body>
</html>
<?php $document=ob_get_clean()?>
<?php ob_start()?>
<?php foreach($payload
as$item):?>
<?php if($item['title']):?>
<h2><?php echo
htmlspecialchars($item['title'])?></h2>
<?php endif?>
<table>
<?php $i=0?>
<?php foreach((is_array($item['var'])?$item['var']:array(''=>$item['var']))as$key=>$val):?>
<tr class="<?php echo$i++%
2?'odd':'even'?>">
<th><?php echo
htmlspecialchars($key)?></th>
<td><?php echo
preg_replace_callback('#(<pre class="dump">|\s+)?(.*)\((\d+)\) <code>#','_netteDumpCb2',Debug::dump($val,TRUE))?></td>
</tr>
<?php endforeach?>
</table>
<?php endforeach?>
<?php $body=ob_get_clean()?>
<script type="text/javascript">
/* <![CDATA[ */
if (typeof _netteConsole === 'undefined') {
_netteConsole = window.open('','_netteConsole','width=700,height=700,resizable,scrollbars=yes');
_netteConsole.document.write(<?php echo
json_encode(preg_replace('#\s+#',' ',$document))?>);
_netteConsole.document.close();
_netteConsole.document.onkeyup = function(e) {
e = e || _netteConsole.event;
if (e.keyCode == 27) _netteConsole.close();
}
_netteConsole.document.body.focus();
}
_netteConsole.document.body.innerHTML = _netteConsole.document.body.innerHTML + <?php echo
json_encode($body)?>;
/* ]]> */
</script>
<?php }}public
static
function
dump($var,$return=FALSE){if(!$return&&self::$productionMode){return$var;}$output="<pre class=\"dump\">".self::_dump($var,0)."</pre>\n";if(self::$showLocation){$trace=debug_backtrace();if(isset($trace[0]['file'],$trace[0]['line'])){$output=substr_replace($output,' <small>'.htmlspecialchars("in file {$trace[0]['file']} on line {$trace[0]['line']}",ENT_NOQUOTES).'</small>',-8,0);}}if(self::$consoleMode){$output=htmlspecialchars_decode(strip_tags($output),ENT_NOQUOTES);}if($return){return$output;}else{echo$output;return$var;}}public
static
function
consoleDump($var,$title=NULL){if(!self::$productionMode){self::$consoleData[]=array('title'=>$title,'var'=>$var);}return$var;}private
static
function
_dump(&$var,$level){if(is_bool($var)){return"<span>bool</span>(".($var?'TRUE':'FALSE').")\n";}elseif($var===NULL){return"<span>NULL</span>\n";}elseif(is_int($var)){return"<span>int</span>($var)\n";}elseif(is_float($var)){return"<span>float</span>($var)\n";}elseif(is_string($var)){if(self::$maxLen&&strlen($var)>self::$maxLen){$s=htmlSpecialChars(substr($var,0,self::$maxLen),ENT_NOQUOTES).' ... ';}else{$s=htmlSpecialChars($var,ENT_NOQUOTES);}return"<span>string</span>(".strlen($var).") \"$s\"\n";}elseif(is_array($var)){$s="<span>array</span>(".count($var).") ";$space=str_repeat($space1=' ',$level);static$marker;if($marker===NULL)$marker=uniqid("\x00",TRUE);if(empty($var)){}elseif(isset($var[$marker])){$s.="{\n$space$space1*RECURSION*\n$space}";}elseif($level<self::$maxDepth||!self::$maxDepth){$s.="<code>{\n";$var[$marker]=0;foreach($var
as$k=>&$v){if($k===$marker)continue;$s.="$space$space1".(is_int($k)?$k:"\"$k\"")." => ".self::_dump($v,$level+1);}unset($var[$marker]);$s.="$space}</code>";}else{$s.="{\n$space$space1...\n$space}";}return$s."\n";}elseif(is_object($var)){$arr=(array)$var;$s="<span>object</span>(".get_class($var).") (".count($arr).") ";$space=str_repeat($space1=' ',$level);static$list=array();if(empty($arr)){$s.="{}";}elseif(in_array($var,$list,TRUE)){$s.="{\n$space$space1*RECURSION*\n$space}";}elseif($level<self::$maxDepth||!self::$maxDepth){$s.="<code>{\n";$list[]=$var;foreach($arr
as$k=>&$v){$m='';if($k[0]==="\x00"){$m=$k[1]==='*'?' <span>protected</span>':' <span>private</span>';$k=substr($k,strrpos($k,"\x00")+1);}$s.="$space$space1\"$k\"$m => ".self::_dump($v,$level+1);}array_pop($list);$s.="$space}</code>";}else{$s.="{\n$space$space1...\n$space}";}return$s."\n";}elseif(is_resource($var)){return"<span>resource of type</span>(".get_resource_type($var).")\n";}else{return"<span>unknown type</span>\n";}}public
static
function
timer($name=NULL){static$time=array();$now=microtime(TRUE);$delta=isset($time[$name])?$now-$time[$name]:0;$time[$name]=$now;return$delta;}public
static
function
enable($mode=NULL,$logFile=NULL,$email=NULL){error_reporting(E_ALL|E_STRICT);if(is_bool($mode)){self::$productionMode=$mode;}if(self::$productionMode===self::DETECT){if(class_exists('Environment')){self::$productionMode=Environment::isProduction();}elseif(isset($_SERVER['SERVER_ADDR'])||isset($_SERVER['LOCAL_ADDR'])){$addr=isset($_SERVER['SERVER_ADDR'])?$_SERVER['SERVER_ADDR']:$_SERVER['LOCAL_ADDR'];$oct=explode('.',$addr);self::$productionMode=$addr!=='::1'&&(count($oct)!==4||($oct[0]!=='10'&&$oct[0]!=='127'&&($oct[0]!=='172'||$oct[1]<16||$oct[1]>31)&&($oct[0]!=='169'||$oct[1]!=='254')&&($oct[0]!=='192'||$oct[1]!=='168')));}else{self::$productionMode=!self::$consoleMode;}}if(self::$productionMode&&$logFile!==FALSE){self::$logFile='log/php_error.log';if(class_exists('Environment')){if(is_string($logFile)){self::$logFile=Environment::expand($logFile);}else
try{self::$logFile=Environment::expand('%logDir%/php_error.log');}catch(InvalidStateException$e){}}elseif(is_string($logFile)){self::$logFile=$logFile;}ini_set('error_log',self::$logFile);}if(function_exists('ini_set')){ini_set('display_errors',!self::$productionMode);ini_set('html_errors',!self::$logFile&&!self::$consoleMode);ini_set('log_errors',(bool)self::$logFile);}elseif(ini_get('log_errors')!=(bool)self::$logFile||(ini_get('display_errors')!=!self::$productionMode&&ini_get('display_errors')!==(self::$productionMode?'stderr':'stdout'))){throw
new
NotSupportedException('Function ini_set() must be enabled.');}self::$sendEmails=self::$logFile&&$email;if(self::$sendEmails){if(is_string($email)){self::$emailHeaders['To']=$email;}elseif(is_array($email)){self::$emailHeaders=$email+self::$emailHeaders;}}if(!defined('E_DEPRECATED')){define('E_DEPRECATED',8192);}if(!defined('E_USER_DEPRECATED')){define('E_USER_DEPRECATED',16384);}set_exception_handler(array(__CLASS__,'exceptionHandler'));set_error_handler(array(__CLASS__,'errorHandler'));self::$enabled=TRUE;}public
static
function
isEnabled(){return
self::$enabled;}public
static
function
exceptionHandler(Exception$exception){if(!headers_sent()){header('HTTP/1.1 500 Internal Server Error');}self::processException($exception,TRUE);exit;}public
static
function
errorHandler($severity,$message,$file,$line,$context){if($severity===E_RECOVERABLE_ERROR||$severity===E_USER_ERROR){throw
new
FatalErrorException($message,0,$severity,$file,$line,$context);}elseif(($severity&error_reporting())!==$severity){return
NULL;}elseif(self::$strictMode){self::processException(new
FatalErrorException($message,0,$severity,$file,$line,$context),TRUE);exit;}static$types=array(E_WARNING=>'Warning',E_USER_WARNING=>'Warning',E_NOTICE=>'Notice',E_USER_NOTICE=>'Notice',E_STRICT=>'Strict standards',E_DEPRECATED=>'Deprecated',E_USER_DEPRECATED=>'Deprecated');$type=isset($types[$severity])?$types[$severity]:'Unknown error';if(self::$logFile){if(self::$sendEmails){self::sendEmail("$type: $message in $file on line $line");}return
FALSE;}elseif(!self::$productionMode&&self::$firebugDetected&&!headers_sent()){$message=strip_tags($message);self::fireLog("$type: $message in $file on line $line",self::ERROR);return
NULL;}return
FALSE;}public
static
function
processException(Exception$exception,$outputAllowed=FALSE){if(self::$logFile){error_log("PHP Fatal error: Uncaught $exception");$file=@strftime('%d-%b-%Y %H-%M-%S ',Debug::$time).strstr(number_format(Debug::$time,4,'~',''),'~');$file=dirname(self::$logFile)."/exception $file.html";self::$logHandle=@fopen($file,'x');if(self::$logHandle){ob_start(array(__CLASS__,'writeFile'),1);self::paintBlueScreen($exception);ob_end_flush();fclose(self::$logHandle);}if(self::$sendEmails){self::sendEmail((string)$exception);}}elseif(self::$productionMode){}elseif(self::$consoleMode){if($outputAllowed){echo"$exception\n";foreach(self::$colophons
as$callback){foreach((array)call_user_func($callback,'bluescreen')as$line)echo
strip_tags($line)."\n";}}}elseif(self::$firebugDetected&&self::$ajaxDetected&&!headers_sent()){self::fireLog($exception,self::EXCEPTION);}elseif($outputAllowed){if(!headers_sent()){@ob_end_clean();while(ob_get_level()&&@ob_end_clean());header('Content-Encoding:',TRUE);}self::paintBlueScreen($exception);}elseif(self::$firebugDetected&&!headers_sent()){self::fireLog($exception,self::EXCEPTION);}foreach(self::$onFatalError
as$handler){fixCallback($handler);call_user_func($handler,$exception);}}public
static
function
paintBlueScreen(Exception$exception){$internals=array();foreach(array('Object','ObjectMixin')as$class){if(class_exists($class,FALSE)){$rc=new
ReflectionClass($class);$internals[$rc->getFileName()]=TRUE;}}$colophons=self::$colophons;if(!function_exists('_netteDebugPrintCode')){function
_netteDebugPrintCode($file,$line,$count=15){if(function_exists('ini_set')){ini_set('highlight.comment','#999; font-style: italic');ini_set('highlight.default','#000');ini_set('highlight.html','#06b');ini_set('highlight.keyword','#d24; font-weight: bold');ini_set('highlight.string','#080');}$start=max(1,$line-floor($count/2));$source=@file_get_contents($file);if(!$source)return;$source=explode("\n",highlight_string($source,TRUE));$spans=1;echo$source[0];$source=explode('<br />',$source[1]);array_unshift($source,NULL);$i=$start;while(--$i>=1){if(preg_match('#.*(</?span[^>]*>)#',$source[$i],$m)){if($m[1]!=='</span>'){$spans++;echo$m[1];}break;}}$source=array_slice($source,$start,$count,TRUE);end($source);$numWidth=strlen((string)key($source));foreach($source
as$n=>$s){$spans+=substr_count($s,'<span')-substr_count($s,'</span');$s=str_replace(array("\r","\n"),array('',''),$s);if($n===$line){printf("<span class='highlight'>Line %{$numWidth}s: %s\n</span>%s",$n,strip_tags($s),preg_replace('#[^>]*(<[^>]+>)[^<]*#','$1',$s));}else{printf("<span class='line'>Line %{$numWidth}s:</span> %s\n",$n,$s);}}echo
str_repeat('</span>',$spans),'</code>';}function
_netteDump($var){return
preg_replace_callback('#(<pre class="dump">|\s+)?(.*)\((\d+)\) <code>#','_netteDumpCb',Debug::dump($var,TRUE));}function
_netteDumpCb($m){return"$m[1]<a href='#' onclick='return !netteToggle(this)'>$m[2]($m[3]) ".(trim($m[1])||$m[3]<7?'<abbr>&#x25bc;</abbr> </a><code>':'<abbr>&#x25ba;</abbr> </a><code class="collapsed">');}function
_netteOpenPanel($name,$collapsed){static$id;$id++;?>
<div class="panel">
<h2><a href="#" onclick="return !netteToggle(this, 'pnl<?php echo$id?>')"><?php echo
htmlSpecialChars($name)?> <abbr><?php echo$collapsed?'&#x25ba;':'&#x25bc;'?></abbr></a></h2>
<div id="pnl<?php echo$id?>" class="<?php echo$collapsed?'collapsed ':''?>inner">
<?php
}function
_netteClosePanel(){?>
</div>
</div>
<?php
}}static$errorTypes=array(E_ERROR=>'Fatal Error',E_USER_ERROR=>'User Error',E_RECOVERABLE_ERROR=>'Recoverable Error',E_CORE_ERROR=>'Core Error',E_COMPILE_ERROR=>'Compile Error',E_PARSE=>'Parse Error',E_WARNING=>'Warning',E_CORE_WARNING=>'Core Warning',E_COMPILE_WARNING=>'Compile Warning',E_USER_WARNING=>'User Warning',E_NOTICE=>'Notice',E_USER_NOTICE=>'User Notice',E_STRICT=>'Strict',E_DEPRECATED=>'Deprecated',E_USER_DEPRECATED=>'User Deprecated');$title=($exception
instanceof
FatalErrorException&&isset($errorTypes[$exception->getSeverity()]))?$errorTypes[$exception->getSeverity()]:get_class($exception);$rn=0;if(headers_sent()){echo'</pre></xmp></table>';}?><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="robots" content="noindex,noarchive">
<meta name="generator" content="Nette Framework">
<title><?php echo
htmlspecialchars($title)?></title>
<style type="text/css">
/* <![CDATA[ */
body {
margin: 0 0 2em;
padding: 0;
}
#netteBluescreen {
font: 9pt/1.5 Verdana, sans-serif;
background: white;
color: #333;
position: absolute;
left: 0;
top: 0;
width: 100%;
z-index: 23178;
text-align: left;
}
#netteBluescreen * {
color: inherit;
background: inherit;
text-align: inherit;
}
#netteBluescreenIcon {
position: absolute;
right: .5em;
top: .5em;
z-index: 23179;
text-decoration: none;
background: red;
padding: 3px;
}
#netteBluescreenIcon abbr {
color: black !important;
}
#netteBluescreen h1 {
font: 18pt/1.5 Verdana, sans-serif !important;
margin: .6em 0;
}
#netteBluescreen h2 {
font: 14pt/1.5 sans-serif !important;
color: #888;
margin: .6em 0;
}
#netteBluescreen a {
text-decoration: none;
color: #4197E3;
}
#netteBluescreen a abbr {
font-family: sans-serif;
color: #999;
}
#netteBluescreen h3 {
font: bold 10pt/1.5 Verdana, sans-serif !important;
margin: 1em 0;
padding: 0;
}
#netteBluescreen p {
margin: .8em 0
}
#netteBluescreen pre, #netteBluescreen code, #netteBluescreen table {
font: 9pt/1.5 Consolas, monospace !important;
}
#netteBluescreen pre, #netteBluescreen table {
background: #fffbcc;
padding: .4em .7em;
border: 1px dotted silver;
}
#netteBluescreen table pre {
padding: 0;
margin: 0;
border: none;
}
#netteBluescreen pre.dump span {
color: #c16549;
}
#netteBluescreen pre.dump a {
color: #333;
}
#netteBluescreen div.panel {
border-bottom: 1px solid #eee;
padding: 1px 2em;
}
#netteBluescreen div.inner {
padding: 0.1em 1em 1em;
background: #f5f5f5;
}
#netteBluescreen table {
border-collapse: collapse;
width: 100%;
}
#netteBluescreen td, #netteBluescreen th {
vertical-align: top;
text-align: left;
padding: 2px 3px;
border: 1px solid #eeeebb;
}
#netteBluescreen th {
width: 10%;
font-weight: bold;
}
#netteBluescreen .odd, #netteBluescreen .odd pre {
background-color: #faf5c3;
}
#netteBluescreen ul {
font: 7pt/1.5 Verdana, sans-serif !important;
padding: 1em 2em 50px;
}
#netteBluescreen .highlight, #netteBluescreenError {
background: red;
color: white;
font-weight: bold;
font-style: normal;
display: block;
}
#netteBluescreen .line {
color: #9e9e7e;
font-weight: normal;
font-style: normal;
}
/* ]]> */
</style>
<script type="text/javascript">
/* <![CDATA[ */
document.write('<style> .collapsed { display: none; } <\/style>');
function netteToggle(link, panelId)
{
var arrow = link.getElementsByTagName('abbr')[0];
var panel = panelId ? document.getElementById(panelId) : link.nextSibling;
while (panel.nodeType !== 1) panel = panel.nextSibling;
var collapsed = panel.currentStyle ? panel.currentStyle.display == 'none' : getComputedStyle(panel, null).display == 'none';
arrow.innerHTML = String.fromCharCode(collapsed ? 0x25bc : 0x25ba);
panel.style.display = collapsed ? (panel.tagName.toLowerCase() === 'code' ? 'inline' : 'block') : 'none';
return true;
}
/* ]]> */
</script>
</head>
<body>
<div id="netteBluescreen">
<a id="netteBluescreenIcon" href="#" onclick="return !netteToggle(this)"><abbr>&#x25bc;</abbr></a
><div>
<div id="netteBluescreenError" class="panel">
<h1><?php echo
htmlspecialchars($title),($exception->getCode()?' #'.$exception->getCode():'')?></h1>
<p><?php echo
htmlspecialchars($exception->getMessage())?></p>
</div>
<?php $ex=$exception;$level=0;?>
<?php do{?>
<?php if($level++):?>
<?php _netteOpenPanel('Caused by',TRUE)?>
<div class="panel">
<h1><?php echo
htmlspecialchars(get_class($ex)),($ex->getCode()?' #'.$ex->getCode():'')?></h1>
<p><?php echo
htmlspecialchars($ex->getMessage())?></p>
</div>
<?php endif?>
<?php $collapsed=isset($internals[$ex->getFile()]);?>
<?php if(is_file($ex->getFile())):?>
<?php _netteOpenPanel('Source file',$collapsed)?>
<p><strong>File:</strong> <?php echo
htmlspecialchars($ex->getFile())?> &nbsp; <strong>Line:</strong> <?php echo$ex->getLine()?></p>
<pre><?php _netteDebugPrintCode($ex->getFile(),$ex->getLine())?></pre>
<?php _netteClosePanel()?>
<?php endif?>
<?php _netteOpenPanel('Call stack',FALSE)?>
<ol>
<?php foreach($ex->getTrace()as$key=>$row):?>
<li><p>
<?php if(isset($row['file'])):?>
<span title="<?php echo
htmlSpecialChars($row['file'])?>"><?php echo
htmlSpecialChars(basename(dirname($row['file']))),'/<b>',htmlSpecialChars(basename($row['file'])),'</b></span> (',$row['line'],')'?>
<?php else:?>
&lt;PHP inner-code&gt;
<?php endif?>
<?php if(isset($row['file'])&&is_file($row['file'])):?><a href="#" onclick="return !netteToggle(this, 'src<?php echo"$level-$key"?>')">source <abbr>&#x25ba;</abbr></a>&nbsp; <?php endif?>
<?php if(isset($row['class']))echo$row['class'].$row['type']?>
<?php echo$row['function']?>
(<?php if(!empty($row['args'])):?><a href="#" onclick="return !netteToggle(this, 'args<?php echo"$level-$key"?>')">arguments <abbr>&#x25ba;</abbr></a><?php endif?>)
</p>
<?php if(!empty($row['args'])):?>
<div class="collapsed" id="args<?php echo"$level-$key"?>">
<table>
<?php
try{$r=isset($row['class'])?new
ReflectionMethod($row['class'],$row['function']):new
ReflectionFunction($row['function']);$params=$r->getParameters();}catch(Exception$e){$params=array();}foreach($row['args']as$k=>$v){echo'<tr><th>',(isset($params[$k])?'$'.$params[$k]->name:"#$k"),'</th><td>';echo
_netteDump($v);echo"</td></tr>\n";}?>
</table>
</div>
<?php endif?>
<?php if(isset($row['file'])&&is_file($row['file'])):?>
<pre <?php if(!$collapsed||isset($internals[$row['file']]))echo'class="collapsed"';else$collapsed=FALSE?> id="src<?php echo"$level-$key"?>"><?php _netteDebugPrintCode($row['file'],$row['line'])?></pre>
<?php endif?>
</li>
<?php endforeach?>
<?php if(!isset($row)):?>
<li><i>empty</i></li>
<?php endif?>
</ol>
<?php _netteClosePanel()?>
<?php if($ex
instanceof
IDebuggable):?>
<?php foreach($ex->getPanels()as$name=>$panel):?>
<?php _netteOpenPanel($name,empty($panel['expanded']))?>
<?php echo$panel['content']?>
<?php _netteClosePanel()?>
<?php endforeach?>
<?php endif?>
<?php if(isset($ex->context)&&is_array($ex->context)):?>
<?php _netteOpenPanel('Variables',TRUE)?>
<table>
<?php
foreach($ex->context
as$k=>$v){echo'<tr><th>$',htmlspecialchars($k),'</th><td>',_netteDump($v),"</td></tr>\n";}?>
</table>
<?php _netteClosePanel()?>
<?php endif?>
<?php }while((method_exists($ex,'getPrevious')&&$ex=$ex->getPrevious())||(isset($ex->previous)&&$ex=$ex->previous));?>
<?php while(--$level)_netteClosePanel()?>
<?php _netteOpenPanel('Environment',TRUE)?>
<?php
$list=get_defined_constants(TRUE);if(!empty($list['user'])):?>
<h3><a href="#" onclick="return !netteToggle(this, 'pnl-env-const')">Constants <abbr>&#x25bc;</abbr></a></h3>
<table id="pnl-env-const">
<?php
foreach($list['user']as$k=>$v){echo'<tr'.($rn++%2?' class="odd"':'').'><th>',htmlspecialchars($k),'</th>';echo'<td>',_netteDump($v),"</td></tr>\n";}?>
</table>
<?php endif?>
<h3><a href="#" onclick="return !netteToggle(this, 'pnl-env-files')">Included files <abbr>&#x25ba;</abbr></a>(<?php echo
count(get_included_files())?>)</h3>
<table id="pnl-env-files" class="collapsed">
<?php
foreach(get_included_files()as$v){echo'<tr'.($rn++%2?' class="odd"':'').'><td>',htmlspecialchars($v),"</td></tr>\n";}?>
</table>
<h3>$_SERVER</h3>
<?php if(empty($_SERVER)):?>
<p><i>empty</i></p>
<?php else:?>
<table>
<?php
foreach($_SERVER
as$k=>$v)echo'<tr'.($rn++%2?' class="odd"':'').'><th>',htmlspecialchars($k),'</th><td>',_netteDump($v),"</td></tr>\n";?>
</table>
<?php endif?>
<?php _netteClosePanel()?>
<?php _netteOpenPanel('HTTP request',TRUE)?>
<?php if(function_exists('apache_request_headers')):?>
<h3>Headers</h3>
<table>
<?php
foreach(apache_request_headers()as$k=>$v)echo'<tr'.($rn++%2?' class="odd"':'').'><th>',htmlspecialchars($k),'</th><td>',htmlspecialchars($v),"</td></tr>\n";?>
</table>
<?php endif?>
<?php foreach(array('_GET','_POST','_COOKIE')as$name):?>
<h3>$<?php echo$name?></h3>
<?php if(empty($GLOBALS[$name])):?>
<p><i>empty</i></p>
<?php else:?>
<table>
<?php
foreach($GLOBALS[$name]as$k=>$v)echo'<tr'.($rn++%2?' class="odd"':'').'><th>',htmlspecialchars($k),'</th><td>',_netteDump($v),"</td></tr>\n";?>
</table>
<?php endif?>
<?php endforeach?>
<?php _netteClosePanel()?>
<?php _netteOpenPanel('HTTP response',TRUE)?>
<h3>Headers</h3>
<?php if(headers_list()):?>
<pre><?php
foreach(headers_list()as$s)echo
htmlspecialchars($s),'<br>';?></pre>
<?php else:?>
<p><i>no headers</i></p>
<?php endif?>
<?php _netteClosePanel()?>
<ul>
<?php foreach($colophons
as$callback):?>
<?php foreach((array)call_user_func($callback,'bluescreen')as$line):?><li><?php echo$line,"\n"?></li><?php endforeach?>
<?php endforeach?>
</ul>
</div>
</div>
<script type="text/javascript">
document.body.appendChild(document.getElementById('netteBluescreen'));
</script>
</body>
</html><?php }public
static
function
writeFile($buffer){fwrite(self::$logHandle,$buffer);}private
static
function
sendEmail($message){$monitorFile=self::$logFile.'.monitor';if(!is_file($monitorFile)){if(@file_put_contents($monitorFile,'e-mail has been sent')){call_user_func(self::$mailer,$message);}}}private
static
function
defaultMailer($message){$host=isset($_SERVER['HTTP_HOST'])?$_SERVER['HTTP_HOST']:(isset($_SERVER['SERVER_NAME'])?$_SERVER['SERVER_NAME']:'');$headers=str_replace(array('%host%','%date%','%message%'),array($host,@date('Y-m-d H:i:s',Debug::$time),$message),self::$emailHeaders);$subject=$headers['Subject'];$to=$headers['To'];$body=$headers['Body'];unset($headers['Subject'],$headers['To'],$headers['Body']);$header='';foreach($headers
as$key=>$value){$header.="$key: $value\r\n";}$body=str_replace("\r\n","\n",$body);if(PHP_OS!='Linux')$body=str_replace("\n","\r\n",$body);mail($to,$subject,$body,$header);}public
static
function
enableProfiler(){self::$enabledProfiler=TRUE;}public
static
function
disableProfiler(){self::$enabledProfiler=FALSE;}public
static
function
addColophon($callback){fixCallback($callback);if(!is_callable($callback)){$able=is_callable($callback,TRUE,$textual);throw
new
InvalidArgumentException("Colophon handler '$textual' is not ".($able?'callable.':'valid PHP callback.'));}if(!in_array($callback,self::$colophons,TRUE)){self::$colophons[]=$callback;}}public
static
function
getDefaultColophons($sender){if($sender==='profiler'){$arr[]='Elapsed time: <b>'.number_format((microtime(TRUE)-Debug::$time)*1000,1,'.',' ').'</b> ms | Allocated memory: <b>'.number_format(memory_get_peak_usage()/1000,1,'.',' ').'</b> kB';foreach((array)self::$counters
as$name=>$value){if(is_array($value))$value=implode(', ',$value);$arr[]=htmlSpecialChars($name).' = <strong>'.htmlSpecialChars($value).'</strong>';}$autoloaded=class_exists('AutoLoader',FALSE)?AutoLoader::$count:0;$s='<span>'.count(get_included_files()).'/'.$autoloaded.' files</span>, ';$exclude=array('stdClass','Exception','ErrorException','Traversable','IteratorAggregate','Iterator','ArrayAccess','Serializable','Closure');foreach(get_loaded_extensions()as$ext){$ref=new
ReflectionExtension($ext);$exclude=array_merge($exclude,$ref->getClassNames());}$classes=array_diff(get_declared_classes(),$exclude);$intf=array_diff(get_declared_interfaces(),$exclude);$func=get_defined_functions();$func=(array)@$func['user'];$consts=get_defined_constants(TRUE);$consts=array_keys((array)@$consts['user']);foreach(array('classes','intf','func','consts')as$item){$s.='<span '.($$item?'title="'.implode(", ",$$item).'"':'').'>'.count($$item).' '.$item.'</span>, ';}$arr[]=$s;}if($sender==='bluescreen'){$arr[]='Report generated at '.@date('Y/m/d H:i:s',Debug::$time);if(isset($_SERVER['HTTP_HOST'],$_SERVER['REQUEST_URI'])){$url=(isset($_SERVER['HTTPS'])&&strcasecmp($_SERVER['HTTPS'],'off')?'https://':'http://').htmlSpecialChars($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);$arr[]='<a href="'.$url.'">'.$url.'</a>';}$arr[]='PHP '.htmlSpecialChars(PHP_VERSION);if(isset($_SERVER['SERVER_SOFTWARE']))$arr[]=htmlSpecialChars($_SERVER['SERVER_SOFTWARE']);$arr[]=htmlSpecialChars(Framework::NAME.' '.Framework::VERSION).' <i>(revision '.htmlSpecialChars(Framework::REVISION).')</i>';}return$arr;}public
static
function
fireDump($var,$key){self::fireSend(2,array((string)$key=>$var));return$var;}public
static
function
fireLog($message,$priority=self::LOG,$label=NULL){if($message
instanceof
Exception){if($priority!==self::EXCEPTION&&$priority!==self::TRACE){$priority=self::TRACE;}$message=array('Class'=>get_class($message),'Message'=>$message->getMessage(),'File'=>$message->getFile(),'Line'=>$message->getLine(),'Trace'=>$message->getTrace(),'Type'=>'','Function'=>'');foreach($message['Trace']as&$row){if(empty($row['file']))$row['file']='?';if(empty($row['line']))$row['line']='?';}}elseif($priority===self::GROUP_START){$label=$message;$message=NULL;}return
self::fireSend(1,self::replaceObjects(array(array('Type'=>$priority,'Label'=>$label),$message)));}private
static
function
fireSend($index,$payload){if(self::$productionMode)return
NULL;if(headers_sent())return
FALSE;header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');if($index===1){header('X-Wf-nette-Structure-1: http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1');}elseif($index===2){header('X-Wf-nette-Structure-2: http://meta.firephp.org/Wildfire/Structure/FirePHP/Dump/0.1');}$payload=json_encode($payload);static$counter;foreach(str_split($payload,4990)as$s){$num=++$counter;header("X-Wf-nette-$index-1-n$num: |$s|\\");}header("X-Wf-nette-$index-1-n$num: |$s|");return
TRUE;}static
private
function
replaceObjects($val){if(is_object($val)){return'object '.get_class($val).'';}elseif(is_string($val)){return@iconv('UTF-16','UTF-8//IGNORE',iconv('UTF-8','UTF-16//IGNORE',$val));}elseif(is_array($val)){foreach($val
as$k=>$v){unset($val[$k]);$k=@iconv('UTF-16','UTF-8//IGNORE',iconv('UTF-8','UTF-16//IGNORE',$k));$val[$k]=self::replaceObjects($v);}}return$val;}}Debug::init();

847
examples/Nette/Debugger.php Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,62 +1,62 @@
The Nette License, Version 1
============================
Licenses
========
Copyright (c) 2004, 2008 David Grudl (http://davidgrudl.com)
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.
This license is a legal agreement between you and David Grudl (the "Author")
for the use of Nette Framework (the "Software"). By obtaining, using and/or
copying the Software, you agree that you have read, understood, and will
comply with the terms and conditions of this license.
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.
PERMITTED USE
-------------
* 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.
You are permitted to use, copy, modify, and distribute the Software and its
documentation, with or without modification, for any purpose, provided that
the following conditions are met:
* 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.
1. A copy of this license agreement must be included with the distribution.
2. Redistributions of source code must retain the above copyright notice in
all source code files.
3. Redistributions in binary form must reproduce the above copyright notice
in the documentation and/or other materials provided with the distribution.
4. Products derived from the Software must include an acknowledgment that
they are derived from Nette Framework in their documentation and/or other
materials provided with the distribution.
5. The name "Nette Framework" must not be used to endorse or promote products
derived from the Software without prior written permission from Author.
6. Products derived from the Software may not be called "Nette Framework",
nor may "Nette" appear in their name, without prior written
permission from Author.
INDEMNITY
---------
You agree to indemnify and hold harmless the Author and any contributors
for any direct, indirect, incidental, or consequential third-party claims,
actions or suits, as well as any related expenses, liabilities, damages,
settlements or fees arising from your use or misuse of the Software,
or a violation of any terms of this license.
DISCLAIMER OF WARRANTY
----------------------
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND
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 HOLDER BE LIABLE FOR
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 +0,0 @@
Disallow: /

View File

@@ -1,3 +1,3 @@
This file is part of Nette Framework
For more information please see http://nettephp.com
For more information please see http://nette.org

View File

@@ -1,16 +1,36 @@
<h1>dibi::connect() example</h1>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Connecting to Databases | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
// connects to SQlite
// connects to SQlite using dibi class
echo '<p>Connecting to Sqlite: ';
try {
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
));
echo 'OK';
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to SQlite using DibiConnection object
echo '<p>Connecting to Sqlite: ';
try {
$connection = new DibiConnection(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
echo 'OK';
@@ -25,7 +45,7 @@ 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=utf8');
dibi::connect('driver=mysql&host=localhost&username=root&password=xxx&database=test&charset=cp1250');
echo 'OK';
} catch (DibiException $e) {
@@ -37,7 +57,7 @@ echo "</p>\n";
// connects to MySQLi using array
echo '<p>Connecting to MySQL: ';
echo '<p>Connecting to MySQLi: ';
try {
dibi::connect(array(
'driver' => 'mysqli',
@@ -45,7 +65,10 @@ try {
'username' => 'root',
'password' => 'xxx',
'database' => 'dibi',
'charset' => 'utf8',
'options' => array(
MYSQLI_OPT_CONNECT_TIMEOUT => 30
),
'flags' => MYSQLI_CLIENT_COMPRESS,
));
echo 'OK';
@@ -64,7 +87,7 @@ try {
'driver' => 'odbc',
'username' => 'root',
'password' => '***',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/sample.mdb',
'dsn' => 'Driver={Microsoft Access Driver (*.mdb)};Dbq='.dirname(__FILE__).'/data/sample.mdb',
));
echo 'OK';
@@ -128,6 +151,25 @@ echo "</p>\n";
// connects to MS SQL 2005
echo '<p>Connecting to MS SQL 2005: ';
try {
dibi::connect(array(
'driver' => 'mssql2005',
'host' => '(local)',
'username' => 'Administrator',
'password' => 'xxx',
'database' => 'main',
));
echo 'OK';
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
// connects to Oracle
echo '<p>Connecting to Oracle: ';
try {
@@ -142,4 +184,4 @@ try {
} catch (DibiException $e) {
echo get_class($e), ': ', $e->getMessage(), "\n";
}
echo "</p>\n";
echo "</p>\n";

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

BIN
examples/data/sample.s3db Normal file

Binary file not shown.

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

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

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Database Reflection | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// retrieve database reflection
$database = dibi::getDatabaseInfo();
echo "<h2>Database '{$database->name}'</h2>\n";
echo "<ul>\n";
foreach ($database->getTables() as $table) {
echo '<li>', ($table->view ? 'view' : 'table') . " $table->name</li>\n";
}
echo "</ul>\n";
// table reflection
$table = $database->getTable('products');
echo "<h2>Table '{$table->name}'</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 "</ul>\n";
echo "Indexes";
echo "<ul>\n";
foreach ($table->getIndexes() as $index) {
echo "<li>{$index->name} " . ($index->primary ? 'primary ' : '') . ($index->unique ? 'unique' : '') . ' (';
foreach ($index->getColumns() as $column) {
echo "$column->name, ";
}
echo ")</li>\n";
}
echo "</ul>\n";

View File

@@ -1,33 +0,0 @@
<h1>dibi dump example</h1>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$res = dibi::query('
SELECT * FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
');
echo '<h2>dibi::dump()</h2>';
// dump last query (dibi::$sql)
dibi::dump();
// -> SELECT * FROM [products] INNER JOIN [orders] USING ([product_id]) INNER JOIN [customers] USING ([customer_id])
// dump result table
echo '<h2>DibiResult::dump()</h2>';
$res->dump();
// -> [table]

View File

@@ -0,0 +1,34 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Dumping SQL and Result Set | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
$res = dibi::query('
SELECT * FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo '<h2>dibi::dump()</h2>';
// dump last query (dibi::$sql)
dibi::dump();
// dump result table
echo '<h2>DibiResult::dump()</h2>';
$res->dump();

View File

@@ -1,94 +0,0 @@
<h1>dibi fetch example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
/*
TABLE products
product_id | title
-----------+----------
1 | Chair
2 | Table
3 | Computer
*/
// fetch a single row
$row = dibi::fetch('SELECT title FROM [products]');
Debug::dump($row); // Chair
echo '<hr>';
// fetch a single value
$value = dibi::fetchSingle('SELECT [title] FROM [products]');
Debug::dump($value); // Chair
echo '<hr>';
// fetch complete result set
$all = dibi::fetchAll('SELECT * FROM [products]');
Debug::dump($all);
echo '<hr>';
// fetch complete result set like association array
$res = dibi::query('SELECT * FROM [products]');
$assoc = $res->fetchAssoc('title'); // key
Debug::dump($assoc);
echo '<hr>';
// fetch complete result set like pairs key => value
$pairs = $res->fetchPairs('product_id', 'title');
Debug::dump($pairs);
echo '<hr>';
// fetch row by row
foreach ($res as $n => $row) {
Debug::dump($row);
}
echo '<hr>';
// fetch row by row with defined offset
foreach ($res->getIterator(2) as $n => $row) {
Debug::dump($row);
}
// fetch row by row with defined offset and limit
foreach ($res->getIterator(2, 1) as $n => $row) {
Debug::dump($row);
}
// more complex association array
$res = dibi::query('
SELECT *
FROM [products]
INNER JOIN [orders] USING ([product_id])
INNER JOIN [customers] USING ([customer_id])
');
$assoc = $res->fetchAssoc('customers.name,products.title'); // key
Debug::dump($assoc);
echo '<hr>';
$assoc = $res->fetchAssoc('customers.name,#,products.title'); // key
Debug::dump($assoc);
echo '<hr>';
$assoc = $res->fetchAssoc('customers.name,=,products.title'); // key
Debug::dump($assoc);
echo '<hr>';

View File

@@ -0,0 +1,86 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Fetching Examples | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
ndebug();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
/*
TABLE products
product_id | title
-----------+----------
1 | Chair
2 | Table
3 | Computer
*/
// fetch a single row
echo "<h2>fetch()</h2>\n";
$row = dibi::fetch('SELECT title FROM products');
dump($row); // Chair
// fetch a single value
echo "<h2>fetchSingle()</h2>\n";
$value = dibi::fetchSingle('SELECT title FROM products');
dump($value); // Chair
// fetch complete result set
echo "<h2>fetchAll()</h2>\n";
$all = dibi::fetchAll('SELECT * FROM products');
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);
// fetch complete result set like pairs key => value
echo "<h2>fetchPairs('product_id', 'title')</h2>\n";
$pairs = $res->fetchPairs('product_id', 'title');
dump($pairs);
// fetch row by row
echo "<h2>using foreach</h2>\n";
foreach ($res as $n => $row) {
dump($row);
}
// more complex association array
$res = dibi::query('
SELECT *
FROM products
INNER JOIN orders USING (product_id)
INNER JOIN customers USING (customer_id)
');
echo "<h2>fetchAssoc('customers.name|products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name|products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('customers.name[]products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name[]products.title'); // key
dump($assoc);
echo "<h2>fetchAssoc('customers.name->products.title')</h2>\n";
$assoc = $res->fetchAssoc('customers.name->products.title'); // key
dump($assoc);

View File

@@ -0,0 +1,19 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Importing SQL Dump from File | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
$count = dibi::loadFile('compress.zlib://data/sample.dump.sql.gz');
echo 'Number of SQL commands:', $count;

View File

@@ -1,17 +0,0 @@
<h1>dibi import SQL dump example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$count = dibi::loadFile('compress.zlib://sample.dump.sql.gz');
echo 'Number of SQL commands:', $count;

View File

@@ -1,30 +0,0 @@
<h1>dibi metatypes example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$res = dibi::query('SELECT * FROM [customers]');
// auto-converts this column to integer
$res->setType('customer_id', Dibi::INTEGER);
$res->setType('added', Dibi::DATETIME, 'H:i j.n.Y');
$row = $res->fetch();
Debug::dump($row);
// outputs:
// object(DibiRow)#3 (3) {
// customer_id => int(1)
// name => string(11) "Dave Lister"
// added => string(15) "17:20 11.3.2007"
// }

View File

@@ -0,0 +1,31 @@
<!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

@@ -0,0 +1,30 @@
<!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

@@ -1,28 +0,0 @@
<h1>Nette\Debug & dibi example</h1>
<p>Dibi can display and log exceptions via Nette\Debug, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nettephp.com
</ul>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
// enable Nette\Debug
Debug::enable();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// throws error
dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38);

View File

@@ -1,28 +0,0 @@
<h1>Nette\Debug & dibi example 2</h1>
<p>Dibi can dump variables via Nette\Debug, part of Nette Framework.</p>
<ul>
<li>Nette Framework: http://nettephp.com
</ul>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
// enable Nette\Debug
Debug::enable();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// throws error
Debug::consoleDump( dibi::fetchAll('SELECT * FROM [customers] WHERE [customer_id] < %i', 38), '[customers]' );

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Query Language & Conditions | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// some variables
$cond1 = TRUE;
$cond2 = FALSE;
$foo = -1;
$bar = 2;
// conditional variable
$name = $cond1 ? 'K%' : NULL;
// if & end
dibi::test('
SELECT *
FROM customers
%if', isset($name), 'WHERE name LIKE ?', $name, '%end'
);
// -> SELECT * FROM customers WHERE name LIKE 'K%'
// if & else & (optional) end
dibi::test("
SELECT *
FROM people
WHERE id > 0
%if", ($foo > 0), "AND foo=?", $foo, "
%else %if", ($bar > 0), "AND bar=?", $bar, "
");
// -> SELECT * FROM people WHERE id > 0 AND bar=2
// nested condition
dibi::test('
SELECT *
FROM customers
WHERE
%if', isset($name), 'name LIKE ?', $name, '
%if', $cond2, 'AND admin=1 %end
%else 1 LIMIT 10 %end'
);
// -> SELECT * FROM customers WHERE LIMIT 10

View File

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

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Result Set Data Types | dibi</h1>
<?php
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
ndebug();
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'data/sample.sdb',
));
// 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');
dump( $res->fetch() );
// outputs:
// object(DibiRow)#3 (3) {
// customer_id => int(1)
// name => string(11) "Dave Lister"
// added => object(DateTime53) {}
// }
// using auto-detection (works well with MySQL or other strictly typed databases)
$res = dibi::query('SELECT * FROM [customers]');
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"
// }

View File

@@ -1,89 +0,0 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<h1>dibi SQL builder example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// dibi detects INSERT or REPLACE command
dibi::test('
REPLACE INTO [products]', array(
'title' => 'Super product',
'price' => 318,
'active' => TRUE,
));
// -> REPLACE INTO [products] ([title], [price], [active]) VALUES ('Super product', 318, 1)
// multiple INSERT command
$array = array(
'title' => 'Super Product',
'price' => 12,
'brand' => NULL,
'created' => dibi::datetime(),
);
dibi::test("INSERT INTO [products]", $array, $array, $array);
// -> INSERT INTO [products] ([title], [price], [brand], [created]) VALUES ('Super Product', ...) , (...) , (...)
// dibi detects UPDATE command
dibi::test("
UPDATE [colors] SET", array(
'color' => 'blue',
'order' => 12,
), "
WHERE [id]=%i", 123);
// -> UPDATE [colors] SET [color]='blue', [order]=12 WHERE [id]=123
// SELECT
$ipMask = '192.168.%';
$timestamp = mktime(0, 0, 0, 10, 13, 1997);
dibi::test('
SELECT COUNT(*) as [count]
FROM [comments]
WHERE [ip] LIKE %s', $ipMask, '
AND [date] > ', dibi::date($timestamp)
);
// -> SELECT COUNT(*) as [count] FROM [comments] WHERE [ip] LIKE '192.168.%' AND [date] > 876693600
// IN array
$array = array(1, 2, 3);
dibi::test("
SELECT *
FROM [people]
WHERE [id] IN (", $array, ")
");
// -> SELECT * FROM [people] WHERE [id] IN ( 1, 2, 3 )
// ORDER BY array
$order = array(
'field1' => 'asc',
'field2' => 'desc',
);
dibi::test("
SELECT *
FROM [people]
ORDER BY %by", $order, "
");
// -> SELECT * FROM [people] ORDER BY [field1] ASC, [field2] DESC

View File

@@ -1,58 +0,0 @@
<style>
pre.dibi { padding-bottom: 10px; }
</style>
<h1>dibi conditional SQL example</h1>
<pre>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
$cond1 = TRUE;
$cond2 = FALSE;
$foo = -1;
$bar = 2;
$name = $cond1 ? 'K%' : NULL;
// if & end
dibi::test('
SELECT *
FROM [customers]
%if', isset($name), 'WHERE [name] LIKE %s', $name, '%end'
);
// -> SELECT * FROM [customers] WHERE [name] LIKE 'K%'
// if & else & (optional) end
dibi::test("
SELECT *
FROM [people]
WHERE [id] > 0
%if", ($foo > 0), "AND [foo]=%i", $foo, "
%else %if", ($bar > 0), "AND [bar]=%i", $bar, "
");
// -> SELECT * FROM [people] WHERE [id] > 0 AND [bar]=2
// nested condition
dibi::test('
SELECT *
FROM [customers]
WHERE
%if', isset($name), '[name] LIKE %s', $name, '
%if', $cond2, 'AND [admin]=1 %end
%else 1 LIMIT 10 %end'
);
// -> SELECT * FROM [customers] WHERE LIMIT 10

View File

@@ -1,49 +0,0 @@
<h1>dibi prefix & substitute example</h1>
<?php
require_once 'Nette/Debug.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
));
// create new substitution :blog: ==> wp_
dibi::addSubst('blog', 'wp_');
dibi::test("UPDATE :blog:items SET [text]='Hello World'");
// -> UPDATE wp_items SET [text]='Hello World'
// create new substitution :: (empty) ==> my_
dibi::addSubst('', 'my_');
dibi::test("UPDATE [database.::table] SET [text]='Hello World'");
// -> UPDATE [database].[my_table] SET [text]='Hello World'
// create substitution fallback
function substFallBack($expr)
{
if (defined($expr)) {
return constant($expr);
} else {
return 'the_' . $expr;
}
}
dibi::setSubstFallBack('substFallBack');
dibi::test("UPDATE [:account:user] SET [name]='John Doe', [active]=:true:");
// -> UPDATE [the_accountuser] SET [name]='John Doe', [active]=1

View File

@@ -1,7 +1,10 @@
<h1>IDibiVariable example</h1>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using DateTime | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
@@ -11,7 +14,7 @@ date_default_timezone_set('Europe/Prague');
// CHANGE TO REAL PARAMETERS!
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
'formatDate' => "'Y-m-d'",
'formatDateTime' => "'Y-m-d H-i-s'",
));
@@ -20,9 +23,10 @@ dibi::connect(array(
// generate and dump SQL
dibi::test("
INSERT INTO [mytable]", array(
'id' => 123,
'date' => dibi::date('12.3.2007'),
'stamp' => dibi::dateTime('23.1.2007 10:23'),
));
INSERT INTO [mytable]", array(
'id' => 123,
'date' => new DateTime('12.3.2007'),
'stamp' => new DateTime('23.1.2007 10:23'),
)
);
// -> INSERT INTO [mytable] ([id], [date], [stamp]) VALUES (123, '2007-03-12', '2007-01-23 10-23-00')

View File

@@ -1,14 +1,17 @@
<h1>dibi extension method example</h1>
<pre>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Extension Methods | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
ndebug();
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
));
@@ -25,4 +28,4 @@ function DibiResult_prototype_fetchShuffle(DibiResult $obj)
// fetch complete result set shuffled
$res = dibi::query('SELECT * FROM [customers]');
$all = $res->fetchShuffle();
Debug::dump($all);
dump($all);

View File

@@ -1,7 +1,10 @@
<h1>dibi dump example</h1>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Fluent Syntax | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
@@ -9,7 +12,7 @@ date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
));
@@ -33,8 +36,6 @@ dibi::select('product_id')->as('id')
echo "\n";
// SELECT ...
echo dibi::select('title')->as('id')
->from('products')
@@ -43,8 +44,6 @@ echo dibi::select('title')->as('id')
echo "\n";
// INSERT ...
dibi::insert('products', $record)
->setFlag('IGNORE')
@@ -53,40 +52,32 @@ dibi::insert('products', $record)
echo "\n";
// UPDATE ...
dibi::update('products', $record)
->where('product_id = %d', $id)
->where('product_id = ?', $id)
->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
echo "\n";
// DELETE ...
dibi::delete('products')
->where('product_id = %d', $id)
->where('product_id = ?', $id)
->test();
// -> DELETE FROM [products] WHERE product_id = 10
echo "\n";
// custom commands
dibi::command()
->update('products')
->where('product_id = %d', $id)
->where('product_id = ?', $id)
->set($record)
->test();
// -> UPDATE [products] SET [title]='Super product', [price]=318, [active]=1 WHERE product_id = 10
echo "\n";
dibi::command()
->truncate('products')
->test();

View File

@@ -1,14 +1,16 @@
<h1>dibi apply limit/offset example</h1>
<pre>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Limit & Offset | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
));
@@ -17,14 +19,12 @@ dibi::test('SELECT * FROM [products]');
// -> SELECT * FROM [products]
echo '<hr>';
// with limit = 2
dibi::test('SELECT * FROM [products] %lmt', 2);
// -> SELECT * FROM [products] LIMIT 2
echo '<hr>';
// with limit = 2, offset = 1
dibi::test('SELECT * FROM [products] %lmt %ofs', 2, 1);

View File

@@ -1,7 +1,10 @@
<h1>dibi logger example</h1>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Logger | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
@@ -9,28 +12,29 @@ date_default_timezone_set('Europe/Prague');
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'profiler' => TRUE,
'database' => 'data/sample.sdb',
// enable query logging to this file
'profiler' => array(
'run' => TRUE,
'file' => 'data/log.sql',
),
));
// enable log to this file
dibi::getProfiler()->setFile('log.sql');
try {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = %i', 1);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] = ?', 1);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', 5);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', 5);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < %i', 38);
$res = dibi::query('SELECT FROM [customers] WHERE [customer_id] < ?', 38);
} catch (DibiException $e) {
echo '<p>', get_class($e), ': ', $e->getMessage(), '</p>';
}
echo "<h2>File log.sql:</h2>";
// outputs a log file
echo "<h2>File data/log.sql:</h2>";
echo '<pre>', file_get_contents('log.sql'), '</pre>';
echo '<pre>', file_get_contents('data/log.sql'), '</pre>';

View File

@@ -1,23 +1,31 @@
<?php ob_start(1) // needed by FirePHP ?>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Profiler | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'profiler' => TRUE,
'database' => 'data/sample.sdb',
'profiler' => array(
'run' => TRUE,
)
));
// execute some queries...
for ($i=0; $i<20; $i++) {
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < %i', $i);
$res = dibi::query('SELECT * FROM [customers] WHERE [customer_id] < ?', $i);
}
// display output
?>
<h1>Dibi profiler example</h1>
<p>Last query: <strong><?php echo dibi::$sql; ?></strong></p>
<p>Number of queries: <strong><?php echo dibi::$numOfQueries; ?></strong></p>
@@ -33,4 +41,4 @@ for ($i=0; $i<20; $i++) {
<ul>
<li>Firebug: https://addons.mozilla.org/en-US/firefox/addon/1843
<li>FirePHP: http://www.firephp.org/
</ul>
</ul>

View File

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

View File

@@ -1,18 +1,20 @@
<h1>dibi transaction example</h1>
<pre>
<!DOCTYPE html><link rel="stylesheet" href="data/style.css">
<h1>Using Transactions | dibi</h1>
<?php
require_once 'Nette/Debug.php';
require_once 'Nette/Debugger.php';
require_once '../dibi/dibi.php';
dibi::connect(array(
'driver' => 'sqlite',
'database' => 'sample.sdb',
'database' => 'data/sample.sdb',
));
echo "<h2>Before:</h2>\n";
echo "<h2>Before</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows
@@ -21,10 +23,13 @@ dibi::begin();
dibi::query('INSERT INTO [products]', array(
'title' => 'Test product',
));
echo "<h2>After INSERT</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
dibi::rollback(); // or dibi::commit();
echo "<h2>After:</h2>\n";
echo "<h2>After rollback</h2>\n";
dibi::query('SELECT * FROM [products]')->dump();
// -> 3 rows
// -> 3 rows again

View File

@@ -1,16 +0,0 @@
<style>
h1 {
font-family: Trebuchet MS,"Geneva CE", lucida, sans-serif;
color: #1e5eb6;
}
img {
border: none;
}
</style>
<h1>Icon for your website</h1>
<a href="http://dibiphp.com" title="dibi - tiny 'n' smart database abstraction layer"
><img src="dibi-powered.gif" width="80" height="15" alt="dibi powered" /></a>

View File

@@ -1,69 +0,0 @@
----------------------------------------------------------------------------------
Tento text je NEOFICI<43>LN<4C>M p<>ekladem "Dibi license". Nevyjad<61>uje pr<70>vn<76> podstatu
podm<EFBFBD>nek pro <20><><EFBFBD>en<65> tohoto softwaru - k tomuto <20><>elu slou<6F><75> v<>hradn<64> p<>vodn<64>
anglick<EFBFBD> verze licence.
----------------------------------------------------------------------------------
Dibi Licence, verze 1
======================
Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
V<EFBFBD>echna pr<70>va vyhrazena.
Tato licence je pr<70>vn<76> ujedn<64>n<EFBFBD> mezi v<>mi a Davidem Grudlem (d<>le Author)
pro pot<6F>eby pou<6F>it<69> "dibi" (d<>le "Software"). Z<>sk<73>n<EFBFBD>m, pou<6F>it<69>m
a/nebo zkop<6F>rov<6F>n<EFBFBD>m Software projevujete souhlas s t<>m, <20>e jste p<>e<EFBFBD>etli,
porozum<EFBFBD>li a budete jednat v souladu s podm<64>nkami t<>to licence.
POVOLEN<EFBFBD> POU<4F>IT<49>
----------------
Je povoleno pou<6F><75>vat, kop<6F>rovat, modifikovat a distribuovat Software
a jeho dokumentaci, v p<>vodn<64>m i upravovan<61>m tvaru, pro jak<61>koliv <20><>el,
za p<>edpokladu, <20>e jsou spln<6C>ny tyto podm<64>nky:
1. Kopie t<>to licen<65>n<EFBFBD> smlouvy mus<75> b<>t sou<6F><75>st<73> distribuce.
2. <20><><EFBFBD>en<65> zdrojov<6F> k<>d mus<75> zachovat v<><76>e uvedenou informaci o autorsk<73>ch
pr<70>vech ve v<>ech souborech zdrojov<6F>ho k<>du.
3. <20><><EFBFBD>en<65> bin<69>rn<72> tvar mus<75> reprodukovat v<><76>e uvedenou informaci o autorsk<73>ch
pr<70>vech v dokumentaci a/nebo jin<69>ch materi<72>lech poskytovan<61>ch s distribuc<75>.
4. Produkty odvozen<65> od Software mus<75> obsahovat potvrzen<65>, <20>e jsou odvozen<65>
od "dibi", ve sv<73> dokumentaci a/nebo jin<69>ch materi<72>lech
poskytovan<61>ch s distribuc<75>.
5. N<>zev "dibi" nesm<73> b<>t pou<6F>it p<>i podpo<70>e nebo propagaci produkt<6B>
odvozen<65>mi ze Software bez p<>edchoz<6F>ho p<>semn<6D>ho souhlasu Autora.
6. Produkty odvozen<65> od Software nesm<73> b<>t nazv<7A>ny "dibi",
ani se nesm<73> "dibi" objevit v jejich n<>zvu bez p<>edchoz<6F>ho
p<>semn<6D>ho souhlasu Autora.
ZBAVEN<EFBFBD> ZODPOV<4F>DNOSTI
---------------------
Souhlas<EFBFBD>te se zbaven<65>m zodpov<6F>dnosti a kryt<79>m Autora a p<>isp<73>vatel<65> v<><76>i
jak<EFBFBD>mkoliv p<><70>m<EFBFBD>m, nep<65><70>m<EFBFBD>m, n<>hodn<64>m nebo n<>sledn<64>m odjinud poch<63>zej<65>c<EFBFBD>m <20>kod<6F>m,
<EFBFBD>alob<EFBFBD>m nebo spor<6F>m, jako<6B> i p<>ed v<>emi souvisej<65>c<EFBFBD>mi n<>klady, z<>vazky,
od<EFBFBD>kodn<EFBFBD>n<EFBFBD>mi, <20>hradami nebo poplatky vypl<70>vaj<61>c<EFBFBD>ch z pou<6F><75>v<EFBFBD>n<EFBFBD> nebo
nespr<EFBFBD>vn<EFBFBD>ho u<>it<69> Software, nebo z poru<72>en<65> podm<64>nek t<>to licence.
Z<EFBFBD>RUKA SE NEPOSKYTUJE
---------------------
TENTO SOFTWARE JE POSKYTOV<4F>N DR<44>ITELEM LICENCE A JEHO P<>ISP<53>VATELI "JAK STOJ<4F> A LE<4C><45>"
A JAK<41>KOLIV V<>SLOVN<56> NEBO P<>EDPOKL<4B>DAN<41> Z<>RUKY V<>ETN<54>, ALE NEJEN, P<>EDPOKL<4B>DAN<41>CH
OBCHODN<EFBFBD>CH Z<>RUK A Z<>RUKY VHODNOSTI PRO JAK<41>KOLIV <20><>EL JSOU POP<4F>ENY.
DR<EFBFBD>ITEL, ANI P<>ISP<53>VATEL<45> NEBUDOU V <20><>DN<44>M P<><50>PAD<41> ODPOV<4F>DNI ZA JAK<41>KOLIV P<><50>M<EFBFBD>,
NEP<EFBFBD><EFBFBD>M<EFBFBD>, N<>HODN<44>, ZVL<56><4C>TN<54>, P<><50>KLADN<44> NEBO VYPL<50>VAJ<41>C<EFBFBD> <20>KODY (V<>ETN<54>, ALE NEJEN,
<EFBFBD>KOD VZNIKL<4B>CH NARU<52>EN<45>M DOD<4F>VEK ZBO<42><4F> NEBO SLU<4C>EB; ZTR<54>TOU POU<4F>ITELNOSTI,
DAT NEBO ZISK<53>; NEBO P<>ERU<52>EN<45>M OBCHODN<44> <20>INNOSTI) JAKKOLIV ZP<5A>SOBEN<45> NA Z<>KLAD<41>
JAK<EFBFBD>KOLIV TEORIE O ZODPOV<4F>DNOSTI, A<> U<> PLYNOUC<55> Z JIN<49>HO SMLUVN<56>HO VZTAHU,
UR<EFBFBD>IT<EFBFBD> ZODPOV<4F>DNOSTI NEBO P<>E<EFBFBD>INU (V<>ETN<54> NEDBALOSTI) NA JAK<41>MKOLIV ZP<5A>SOBU POU<4F>IT<49>
TOHOTO SOFTWARE, I V P<><50>PAD<41>, <20>E DR<44>ITEL PR<50>V BYL UPOZORN<52>N NA MO<4D>NOST TAKOV<4F>CH <20>KOD.

View File

@@ -1,62 +1,61 @@
The Dibi License, Version 1
============================
Licenses
========
Copyright (c) 2005, 2008 David Grudl (http://davidgrudl.com)
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.
This license is a legal agreement between you and David Grudl (the "Author")
for the use of "dibi" (the "Software"). By obtaining, using and/or
copying the Software, you agree that you have read, understood, and will
comply with the terms and conditions of this license.
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.
PERMITTED USE
-------------
* 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.
You are permitted to use, copy, modify, and distribute the Software and its
documentation, with or without modification, for any purpose, provided that
the following conditions are met:
* 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.
1. A copy of this license agreement must be included with the distribution.
2. Redistributions of source code must retain the above copyright notice in
all source code files.
3. Redistributions in binary form must reproduce the above copyright notice
in the documentation and/or other materials provided with the distribution.
4. Products derived from the Software must include an acknowledgment that
they are derived from "dibi" in their documentation and/or other
materials provided with the distribution.
5. The name "dibi" must not be used to endorse or promote products
derived from the Software without prior written permission from Author.
6. Products derived from the Software may not be called "dibi",
nor may "dibi" appear in their name, without prior written
permission from Author.
INDEMNITY
---------
You agree to indemnify and hold harmless the Author and any contributors
for any direct, indirect, incidental, or consequential third-party claims,
actions or suits, as well as any related expenses, liabilities, damages,
settlements or fees arising from your use or misuse of the Software,
or a violation of any terms of this license.
DISCLAIMER OF WARRANTY
----------------------
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND
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 HOLDER BE LIABLE FOR
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,7 +1,3 @@
Dibi (c) David Grudl, 2005-2009 (http://davidgrudl.com)
Introduction
------------
@@ -10,10 +6,6 @@ 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.
The files in this archive are released under the Dibi license.
See license.txt in this directory for a copy of the license.
Documentation and Examples
--------------------------
@@ -24,7 +16,6 @@ available on the homepage:
http://dibiphp.com
Dibi.minified
-------------
@@ -35,7 +26,6 @@ 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,72 @@
<?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]

138
tests/NetteTest/Assert.php Normal file
View File

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

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

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

@@ -0,0 +1,312 @@
<?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;
}
}

View File

@@ -0,0 +1,230 @@
<?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 runner.
*
* @author David Grudl
* @package Nette\Test
*/
class TestRunner
{
const OUTPUT = 'output';
const EXPECTED = 'expect';
const HEADERS = 'headers';
/** @var string path to test file/directory */
public $path;
/** @var string php-cgi binary */
public $phpBinary;
/** @var string php-cgi command-line arguments */
public $phpArgs;
/** @var string php-cgi environment variables */
public $phpEnvironment;
/** @var bool display skipped tests information? */
public $displaySkipped = FALSE;
/**
* Runs all tests.
* @return void
*/
public function run()
{
$count = 0;
$failed = $passed = $skipped = array();
if (is_file($this->path)) {
$files = array($this->path);
} else {
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->path));
}
foreach ($files as $entry) {
$entry = (string) $entry;
$info = pathinfo($entry);
if (!isset($info['extension']) || $info['extension'] !== 'phpt') {
continue;
}
$count++;
$testCase = new TestCase($entry);
$testCase->setPhp($this->phpBinary, $this->phpArgs, $this->phpEnvironment);
try {
$testCase->run();
echo '.';
$passed[] = array($testCase->getName(), $entry);
} catch (TestCaseException $e) {
if ($e->getCode() === TestCaseException::SKIPPED) {
echo 's';
$skipped[] = array($testCase->getName(), $entry, $e->getMessage());
} else {
echo 'F';
$failed[] = array($testCase->getName(), $entry, $e->getMessage());
$this->log($entry, $testCase->getOutput(), self::OUTPUT);
$this->log($entry, $testCase->getExpectedOutput(), self::EXPECTED);
if ($testCase->getExpectedHeaders() !== NULL) {
$this->log($entry, $testCase->getHeaders(), self::OUTPUT, self::HEADERS);
$this->log($entry, $testCase->getExpectedHeaders(), self::EXPECTED, self::HEADERS);
}
}
}
}
$failedCount = count($failed);
$skippedCount = count($skipped);
if ($this->displaySkipped && $skippedCount) {
echo "\n\nSkipped:\n";
foreach ($skipped as $i => $item) {
list($name, $file, $message) = $item;
echo "\n", ($i + 1), ") $name\n $message\n $file\n";
}
}
if (!$count) {
echo "No tests found\n";
} elseif ($failedCount) {
echo "\n\nFailures:\n";
foreach ($failed as $i => $item) {
list($name, $file, $message) = $item;
echo "\n", ($i + 1), ") $name\n $message\n $file\n";
}
echo "\nFAILURES! ($count tests, $failedCount failures, $skippedCount skipped)\n";
return FALSE;
} else {
echo "\n\nOK ($count tests, $skippedCount skipped)\n";
}
return TRUE;
}
/**
* Returns output file for logging.
* @param string
* @param string
* @param string
* @param string
* @return void
*/
public function log($testFile, $content, $type, $section = '')
{
$file = dirname($testFile) . '/' . $type . '/' . basename($testFile, '.phpt') . ($section ? ".$section" : '') . '.raw';
@mkdir(dirname($file)); // @ - directory may already exist
file_put_contents($file, $content);
}
/**
* Parses configuration file.
* @return void
*/
public function parseConfigFile()
{
$configFile = dirname(__FILE__) . '/config.ini';
if (file_exists($configFile)) {
$this->config = parse_ini_file($configFile, TRUE);
if ($this->config === FALSE) {
throw new Exception('Config file parsing failed.');
}
foreach ($this->config as & $environment) {
$environment += array(
'binary' => 'php-cgi',
'args' => '',
'environment' => '',
);
// shorthand options
if (isset($environment['php.ini'])) {
$environment['args'] .= ' -c '. escapeshellarg($environment['php.ini']);
}
if (isset($environment['libraries'])) {
$environment['environment'] .= 'LD_LIBRARY_PATH='. escapeshellarg($environment['libraries']) .' ';
}
}
}
}
/**
* Parses command line arguments.
* @return void
*/
public function parseArguments()
{
$this->phpBinary = 'php-cgi';
$this->phpArgs = '';
$this->phpEnvironment = '';
$this->path = getcwd(); // current directory
$args = new ArrayIterator(array_slice(isset($_SERVER['argv']) ? $_SERVER['argv'] : array(), 1));
foreach ($args as $arg) {
if (!preg_match('#^[-/][a-z]$#', $arg)) {
if ($path = realpath($arg)) {
$this->path = $path;
} else {
throw new Exception("Invalid path '$arg'.");
}
} else switch ($arg[1]) {
case 'p':
$args->next();
$this->phpBinary = $args->current();
break;
case 'c':
case 'd':
$args->next();
$this->phpArgs .= " -$arg[1] " . escapeshellarg($args->current());
break;
case 'l':
$args->next();
$this->phpEnvironment .= 'LD_LIBRARY_PATH='. escapeshellarg($args->current()) . ' ';
break;
case 'e':
$args->next();
$name = $args->current();
if (!isset($this->config[$name])) {
throw new Exception("Unknown environment name '$name'.");
}
$this->phpBinary = $this->config[$name]['binary'];
$this->phpArgs = $this->config[$name]['args'];
$this->phpEnvironment = $this->config[$name]['environment'];
break;
case 's':
$this->displaySkipped = TRUE;
break;
default:
throw new Exception("Unknown option -$arg[1].");
exit;
}
}
}
}

67
tests/config.ini Normal file
View File

@@ -0,0 +1,67 @@
[mysql]
driver = mysql
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[mysqli]
driver = mysqli
host = localhost
username = dibi
password = dibi
database = dibi_test
charset = utf8
[sqlite]
driver = sqlite
database = DIR "/data/sample.sdb"
[sqlite3]
driver = sqlite3
database = DIR "/data/sample.sdb3"
[odbc]
driver = odbc
username = dibi
password = dibi
dsn = "Driver={Microsoft Access Driver (*.mdb)};Dbq=" DIR "/data/sample.mdb"
[postgresql]
driver = postgre
host = localhost
port = 5432
username = dibi
password = dibi
database = dibi_test
persistent = TRUE
[sqlite-pdo]
driver = pdo
dsn = "sqlite2::" DIR "/data/sample.sdb"
[mysql-pdo]
driver = pdo
dsn = "mysql:dbname=dibi_test;host=localhost"
username = dibi
password = dibi
[mssql]
driver = mssql
host = localhost
username = dibi
password = dibi
[mssql2005]
driver = mssql2005
host = "(local)"
username = dibi
password = dibi
database = dibi_test
[oracle]
driver = oracle
username = dibi
password = dibi
database = dibi_test

BIN
tests/data/sample.mdb Normal file

Binary file not shown.

149
tests/data/sample.mysql Normal file
View File

@@ -0,0 +1,149 @@
-- phpMyAdmin SQL Dump
-- version 2.11.1.2
-- http://www.phpmyadmin.net
--
-- Po<50><6F>ta<74>: localhost
-- Vygenerov<6F>no: Ned<65>le 02. prosince 2007, 19:49
-- Verze MySQL: 5.0.45
-- Verze PHP: 5.2.1
SET FOREIGN_KEY_CHECKS=0;
SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
--
-- Datab<61>ze: `dibi`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `customers`
--
DROP TABLE IF EXISTS `customers`;
CREATE TABLE IF NOT EXISTS `customers` (
`customer_id` int(11) NOT NULL auto_increment,
`name` varchar(100) default NULL,
PRIMARY KEY (`customer_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=7 ;
--
-- Vypisuji data pro tabulku `customers`
--
INSERT INTO `customers` (`customer_id`, `name`) VALUES
(1, 'Dave Lister'),
(2, 'Arnold Rimmer'),
(3, 'The Cat'),
(4, 'Holly'),
(5, 'Kryten'),
(6, 'Kristine Kochanski');
-- --------------------------------------------------------
--
-- Struktura tabulky `enumtest`
--
DROP TABLE IF EXISTS `enumtest`;
CREATE TABLE IF NOT EXISTS `enumtest` (
`id` int(11) NOT NULL auto_increment,
`test` enum('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `enumtest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `orders`
--
DROP TABLE IF EXISTS `orders`;
CREATE TABLE IF NOT EXISTS `orders` (
`order_id` int(11) NOT NULL,
`customer_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
`amount` float NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `orders`
--
INSERT INTO `orders` (`order_id`, `customer_id`, `product_id`, `amount`) VALUES
(1, 2, 1, 7),
(2, 2, 3, 2),
(3, 1, 2, 3),
(4, 6, 3, 5);
-- --------------------------------------------------------
--
-- Struktura tabulky `products`
--
DROP TABLE IF EXISTS `products`;
CREATE TABLE IF NOT EXISTS `products` (
`product_id` int(11) NOT NULL auto_increment,
`title` varchar(100) default NULL,
PRIMARY KEY (`product_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `products`
--
INSERT INTO `products` (`product_id`, `title`) VALUES
(1, 'Chair'),
(2, 'Table'),
(3, 'Computer');
-- --------------------------------------------------------
--
-- Struktura tabulky `settest`
--
DROP TABLE IF EXISTS `settest`;
CREATE TABLE IF NOT EXISTS `settest` (
`id` int(11) NOT NULL auto_increment,
`test` set('a','b','c') NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
--
-- Vypisuji data pro tabulku `settest`
--
-- --------------------------------------------------------
--
-- Struktura tabulky `where`
--
DROP TABLE IF EXISTS `where`;
CREATE TABLE IF NOT EXISTS `where` (
`select` int(11) NOT NULL,
`dot.dot` int(11) NOT NULL,
`is` int(11) NOT NULL,
`quot'n' space` int(11) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Vypisuji data pro tabulku `where`
--
INSERT INTO `where` (`select`, `dot.dot`, `is`, `quot'n' space`) VALUES
(1, 2, 3, 4);
SET FOREIGN_KEY_CHECKS=1;
SET SQL_MODE="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION";

BIN
tests/data/sample.sdb Normal file

Binary file not shown.

BIN
tests/data/sample.sdb3 Normal file

Binary file not shown.

26
tests/initialize.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
/**
* Test initialization and helpers.
*
* @author David Grudl
* @package Nette\Test
*/
require dirname(__FILE__) . '/NetteTest/TestHelpers.php';
require dirname(__FILE__) . '/NetteTest/Assert.php';
require dirname(__FILE__) . '/../dibi/dibi.php';
date_default_timezone_set('Europe/Prague');
TestHelpers::startup();
if (function_exists('class_alias')) {
class_alias('TestHelpers', 'T');
} else {
class T extends TestHelpers {}
}
// load connections
define('DIR', dirname(__FILE__));
$config = parse_ini_file('config.ini', TRUE);

View File

@@ -1 +1 @@
Dibi 1.1 (revision $WCREV$ released on $WCDATE$)
Dibi 2.0 (revision $WCREV$ released on $WCDATE$)