147 Commits
v1.0 ... v2.4

Author SHA1 Message Date
Sergey Tsalkov
e30c240d54 cleanup README, add note about $logfile 2021-06-29 04:31:23 +00:00
Sergey Tsalkov
13f249f200 update readme for new version 2021-06-28 22:37:53 +00:00
Sergey Tsalkov
f7a52ca410 DATE_FORMAT() tests for %% escaping 2021-06-28 22:20:59 +00:00
Sergey Tsalkov
d944430401 drop DBHelper 2021-06-27 05:35:28 +00:00
Sergey Tsalkov
a10b76b2de cleanup some error-checking code
add lots more tests for that code
2021-06-27 03:17:47 +00:00
Sergey Tsalkov
50ed9675f3 fix pre_parse and pre_run hooks
add testing for all hooks (roll into previous ErrorTest group)
2021-06-26 00:52:37 +00:00
Sergey Tsalkov
1724b7276b cleanup MeekroDBWalk a bit 2021-06-25 23:48:44 +00:00
Sergey Tsalkov
aad5cbbbac cleanup and better test queryWalk() 2021-06-25 18:23:59 +00:00
Sergey Tsalkov
5108480e03 drop unused code 2021-06-25 17:40:41 +00:00
Sergey Tsalkov
cd64dd724b add tests for queryWalk() 2021-06-25 17:35:39 +00:00
Sergey Tsalkov
15f40efe5d add DB::queryWalk() -- still untested
add DB::lastQuery() (with tests)
2021-06-24 03:05:46 +00:00
Sergey Tsalkov
30f3d9454a DB::debugMode() is now deprecated (but still works)
recommended debug system will use $logfile to log SQL to a file
2021-06-23 21:23:20 +00:00
Sergey Tsalkov
dc21f646af DB::$logfile can be used to log all queries to either a resource or a file
DB::debugMode() is now a shorthand for "DB::$logfile = STDOUT"
2021-06-23 03:05:22 +00:00
Sergey Tsalkov
50fd6ed6d2 actually bring back deprecated methods, keep them for now to maintain backwards compatability 2021-06-23 01:27:03 +00:00
Sergey Tsalkov
6487873c68 new hook-based system for inserting callbacks to run before and after a query
debugMode() now works through the new system
2021-06-22 06:01:28 +00:00
Sergey Tsalkov
ac626b0795 simplify parser a bit 2021-06-20 20:53:31 +00:00
Sergey Tsalkov
bac5503d5a PHP 8 complains when trying to access closed $mysqli object, not sure how to test this properly
I don't think we need this test very much, anyway
2021-06-20 03:13:11 +00:00
Sergey Tsalkov
42894b70ae re-design query parser, new model has cleaner code and allows for %% escaping of % characters 2021-06-20 03:12:15 +00:00
Sergey Tsalkov
20c7acd8a4 DB::parse() lets you generate queries without running them 2021-06-20 02:32:56 +00:00
Sergey Tsalkov
c0f3a7f31d restore to working in PHP 5.3 2021-06-20 02:32:56 +00:00
Sergey Tsalkov
1d2ad974f8 drop deprecated methods:
queryOneRow (alias of queryFirstRow)
queryOneList (alias of queryFirstList)
queryOneColumn
queryOneField
2021-06-20 02:32:56 +00:00
Sergey Tsalkov
2ea4c4c539 __callStatic() should have proper exception thrown if invalid method is used 2021-06-20 02:32:56 +00:00
Sergey Tsalkov
7c1f9bc226 DB::$usenull is removed, it makes no sense in a modern PHP context 2021-06-20 02:32:56 +00:00
Sergey Tsalkov
dab71efacd DB::columnList() now returns details 2021-06-20 02:28:26 +00:00
Sergey Tsalkov
38545ddb3a remove more PHP 5.2 cruft, we now require PHP 5.3+ 2021-06-20 02:28:26 +00:00
Sergey Tsalkov
0fb0071314 use __callStatic() to clean up code a bit
next release will require PHP 5.3+ (up from 5.2)
2021-06-20 02:26:21 +00:00
Sergey Tsalkov
03ff51a997 fix composer.json
packagist demands lowercase package names now
2021-01-23 22:27:10 -06:00
Sergey Tsalkov
4ff57845e4 Update README.md
Remove old broken link.
2020-07-11 10:23:08 -05:00
Sergey Tsalkov
e8c19ca2f8 clean up update() and delete(), both will now be compatible with named and numbered arguments
resolves #54
2020-07-10 01:13:31 +00:00
Sergey Tsalkov
fa46824df1 get_class_vars() is broken in PHP 7.4 with opcache enabled, so stop using it
looks like this fix actually works with PHP 5.2 after all, so not dropping backwards compatibility
resolves #64
2020-07-10 00:00:27 +00:00
Sergey Tsalkov
226a08611f small test fix to pass on PHP 7.3 2020-07-09 19:51:28 +00:00
wadih
031901f8b6 Add a warning to prevent accidents (#65) 2020-07-08 21:19:59 -05:00
Magnus Månsson
30c7343bae Pre sql handler (#59)
Add a pre_sql_handler
2020-02-06 22:32:29 -06:00
Magnus Månsson
b99ed41c94 Implemented param char ssb and sse (#58) 2020-02-06 22:20:53 -06:00
Sergey Tsalkov
18d9ab2337 make sure insertOrReplace() gets valid input, throw an error if it doesn't 2020-01-27 08:45:36 -08:00
Sergey Tsalkov
027681b350 bugfix: strtolower() doesn't work with certain locales, so work around that 2020-01-24 15:42:43 -08:00
Sergey Tsalkov
677d5659bb Update README.md 2019-01-26 11:46:13 -06:00
Sergey Tsalkov
621f2e7a86 Update README.md 2019-01-26 11:45:50 -06:00
Sergey Tsalkov
6e1c252702 Update README.md 2019-01-26 10:23:50 -06:00
Sergey Tsalkov
8e19d9d6c3 bugfix in WhereClause: negate didn't wrap multiple clauses in extra parens
Fixes #41
2017-10-15 19:10:35 +00:00
Sergey Tsalkov
22e49b280e clarify that project is LGPLv3 2017-10-15 18:20:37 +00:00
Sergey Tsalkov
b41887505d fix to last commit 2017-10-15 17:56:10 +00:00
Mike Damm
cb10e249b2 Add support for custom unix sockets (#49)
Add support for custom unix sockets
2017-10-08 21:04:22 -07:00
Sergey Tsalkov
aabc77b116 Update README.md 2016-11-21 17:01:59 -08:00
Sergey Tsalkov
8bef6d6618 fix bug with hhvm 2015-03-04 00:43:54 +00:00
Sergey Tsalkov
da46a1eacc DB::insert() and the like should use __toString() value for objects whenever possible 2014-11-27 01:30:42 +00:00
Sergey Tsalkov
076a7e2b9b oops 2014-10-27 02:02:32 +00:00
Sergey Tsalkov
67b24c916d additional test with %li0 2014-10-27 02:00:21 +00:00
Sergey Tsalkov
29c8e42a09 escape and sanitize could be occasionally useful as public functions 2014-10-27 02:00:21 +00:00
Sergey Tsalkov
6fdcffab24 few minor cleanups to last commit 2014-10-27 02:00:21 +00:00
Sergey Tsalkov
e0660a9368 %? no longer handles arrays and hashes for security purposes
those are moved into %l?, %ll?, %hc, %ha, %ho for different uses
2014-10-27 02:00:21 +00:00
Sergey Tsalkov
0474a41a56 functions generally shouldn't keep going after a nonsql error 2014-10-27 02:00:21 +00:00
Sergey Tsalkov
1fe2fdea16 when an instance of MeekroDB is created, its default settings will be those of the static instance 2014-10-27 02:00:20 +00:00
Sergey Tsalkov
83256937b8 make some useful functions public 2014-08-25 20:20:39 -07:00
Sergey Tsalkov
eb36858f1a columnList() properly escapes table names 2014-06-16 22:40:22 +00:00
Sergey Tsalkov
1d797b306e bugfix: insert/update type functions don't break when using a param_char other than % 2014-06-14 02:41:06 +00:00
Sergey Tsalkov
7c819bfc24 the connect_options variable lets you set mysqli_options, like a connection timeout 2014-06-14 01:58:51 +00:00
Sergey Tsalkov
1d147e169a ssl support (still untested) 2014-06-14 01:31:32 +00:00
Sergey Tsalkov
391702700d clean up static class init syntax a bit 2014-06-14 01:06:05 +00:00
Sergey Tsalkov
37fd169be3 minor bugfix: don't assume that the query results for queryFirstRow() or queryFirstList() will be an array (can also be a bool) 2014-06-14 00:27:38 +00:00
Sergey Tsalkov
7c5c03c576 minor cleanups 2014-06-13 21:19:10 +00:00
Sergey Tsalkov
a0a2f702e2 bugfix: don't crash if nested transactions are enabled, and the first command issued is a start transaction 2014-03-31 01:50:10 -07:00
Sergey Tsalkov
2707bcba7d minor bugfix: don't drop identical query components in WhereClause (thanks Alexander!) 2013-12-06 11:17:54 -08:00
Sergey Tsalkov
7b0da839de add yet another test for this weird bug people are reporting
tests *still* all pass, I can't reproduce the bug
2013-11-08 14:14:46 -08:00
Sergey Tsalkov
1d51c2b674 add additional test case for a reported bug where '%s' in data is parsed as a query placeholder
I can't reproduce the bug
2013-09-13 22:36:33 -07:00
SergeyTsalkov
de63573e98 Merge pull request #14 from bcash/master
Added passthru of mysqli error code to MeekroDBException object
2013-06-30 01:31:12 -07:00
Brian Cash
26fcce650a Passes mysqli error code to MeekroDBException 2013-06-22 11:56:01 -07:00
Sergey Tsalkov
e403c774c8 Merge branch 'master' of github.com:SergeyTsalkov/meekrodb 2013-06-21 16:27:25 -07:00
Sergey Tsalkov
740ca7bc67 restore %ss to working, add unit test so it won't break again 2013-06-21 16:27:05 -07:00
SergeyTsalkov
7563c660ad Update readme to include brief installation section 2013-06-09 17:08:20 -06:00
Sergey Tsalkov
3741291f44 add a few more fields to composer.json 2013-06-08 13:55:16 -07:00
Sergey Tsalkov
7e00296b32 add license to composer file 2013-06-08 13:38:26 -07:00
Sergey Tsalkov
e2e95adda2 composer fix 2013-06-08 13:32:01 -07:00
Sergey Tsalkov
34a3de3f84 add composer.json for composer support -- not yet tested 2013-06-08 13:28:27 -07:00
Sergey Tsalkov
cc8dbe47eb %i should allow big ints on 32 bit systems 2013-06-07 00:03:30 -07:00
SergeyTsalkov
8090f4e7dc Update README.md 2013-06-05 00:19:36 -07:00
SergeyTsalkov
58e564c3d5 Update README.md 2013-06-05 01:08:41 -06:00
Sergey Tsalkov
253e803160 WhereClause can't have a __toString() method anymore because of how it's handled in combinatino with preparseQueryParams() now 2013-06-05 00:01:10 -07:00
SergeyTsalkov
c05a575be0 Create README.md 2013-06-05 01:01:08 -06:00
Sergey Tsalkov
e61d66fdf8 new DateTime() can be used with insert-like commands, and with %? as well 2013-06-02 19:45:44 -07:00
Sergey Tsalkov
e9a55de0b4 DB::$usenull param can be used to decide whether insert()-like commands insert NULL or empty string for NULL 2013-06-02 19:22:14 -07:00
Sergey Tsalkov
9e4cbc28c1 %t and %lt variables can be used to hold a datetime, which will be formatted to mysql standards 2013-06-02 19:10:28 -07:00
Sergey Tsalkov
b6397a719c merge freeResult and checkError functions back into queryHelper() since they're only used once 2013-06-02 18:17:59 -07:00
Sergey Tsalkov
65ec35c591 modify WhereClause to hold off on evaluating parameters
preparseQueryParams will now receive full WhereClause object and evaluate there
2013-06-02 15:51:58 -07:00
Sergey Tsalkov
1d61a11098 add %? autodetection parameter, simplify functions such as insert() and update() to rely on it 2013-06-02 15:17:14 -07:00
Sergey Tsalkov
ecd5fe190f clean up query parsing code -- will be necessary for transition to prepared statement approach 2013-06-02 12:52:04 -07:00
Sergey Tsalkov
e94f75fe2a DB::disconnect() allows easy disconnecting from mysql 2013-03-19 12:02:47 -07:00
Sergey Tsalkov
ce11e65070 successful insert/replace/update/delete will return true 2013-03-19 11:31:50 -07:00
Sergey Tsalkov
6cb757797b minor fix for calculated columns with fullcolumns 2013-03-19 11:20:20 -07:00
Sergey Tsalkov
027e1529ba create DB::queryFullColumns() which will supply full column names in the format table.column 2013-02-22 22:11:27 -08:00
Sergey Tsalkov
e03ac71b45 more organization and simplification
add queryAllLists() and queryRawUnbuf() methods
2013-02-22 21:28:41 -08:00
Sergey Tsalkov
f89d70e49e bugfix: can use null value for named or numbered parameters 2013-02-22 16:32:15 -08:00
Sergey Tsalkov
6babe98b11 bugfix: free any extra results so CALL commands don't fail 2013-02-22 16:18:18 -08:00
Sergey Tsalkov
039468f3a3 more cleanups and remove old and unused code 2013-02-22 15:08:08 -08:00
Sergey Tsalkov
5e9507181f code cleanups 2013-02-22 12:37:11 -08:00
Sergey Tsalkov
7364df36cf include a formatting error if they pass an array where we're not expecting one 2013-02-22 11:52:18 -08:00
Sergey Tsalkov
7df5c9baa2 update copyright year 2012-09-20 21:05:19 -07:00
Sergey Tsalkov
ed347e841a verticalslice and reindex cleanups and tests 2012-09-20 20:50:05 -07:00
Sergey Tsalkov
2ffb770f8a bugfix: don't fail when inserting multiple rows at once, and one of the values is NULL 2012-09-19 14:58:09 -07:00
Sergey Tsalkov
ed9c29fe49 add DB::transactionDepth() for checking how many transactions are active
when not using nested transactions, will return 1 or 0
2012-09-18 21:08:28 -07:00
Sergey Tsalkov
9a41bf34a1 a few nested transaction cleanups
functionality for committing/rolling back all active transactions
2012-09-18 20:54:39 -07:00
Sergey Tsalkov
24c1b930df add DBHelper class with functions verticalSlice and reIndex 2012-09-18 20:13:43 -07:00
Sergey Tsalkov
d40bc57dde add nested transactions (mysql >= 5.5 only) 2012-09-18 19:08:37 -07:00
Sergey Tsalkov
c62fc187ee quick testing change 2012-09-09 17:31:57 -07:00
Sergey Tsalkov
6b324d1291 using $where is now equivalent to $where->text() 2012-09-09 14:49:43 -07:00
Sergey Tsalkov
5b4e5a959a fix named parameters 2012-07-22 17:43:34 -07:00
Sergey Tsalkov
7582961ee1 named parameters for queries 2012-07-22 17:29:59 -07:00
Sergey Tsalkov
2d49f4f350 remove backtrace from default error message -- it can leak private information 2012-07-08 13:04:57 -07:00
Sergey Tsalkov
9930fc073a restore %lb parameter to working, add unit test so it doesn't break again 2012-02-03 17:19:54 -08:00
Sergey Tsalkov
788c506f16 success handler gets "affected" var for affected rows 2012-01-25 13:59:42 -08:00
Sergey Tsalkov
bbf9534326 add execution time to tests 2011-12-24 17:27:53 -08:00
Sergey Tsalkov
1d5844bcc0 major redesign to enable multiple instances via "new MeekroDB()" while preserving full backwards
compatability

static methods DB::whatever() will still be the recommended method of use, but the new object oriented
approach is now available to those who want it
2011-12-24 16:12:13 -08:00
Sergey Tsalkov
34c0f5e0a7 add E_STRICT to tests 2011-12-24 13:07:44 -08:00
Sergey Tsalkov
7825f90080 can now refer to a specific argument in a query
e.g. %s2 for the third argument, taken as a string
2011-12-17 19:54:55 -08:00
Sergey Tsalkov
a1f01a4f87 few more fixes and tests for WhereClause 2011-09-23 22:03:15 -07:00
Sergey Tsalkov
80cc78edcd insertUpdate() can now handle a second array for the update component, or nothing, so it'll
re-use the insert array for the update component
2011-09-23 21:29:40 -07:00
Sergey Tsalkov
5050205e58 add insertUpdate() function for "insert ... on duplicate key update" behavior 2011-09-23 19:15:41 -07:00
Sergey Tsalkov
9f3aa571dc add some simple tests for the WhereClause system, which will now be official and documented 2011-09-23 18:44:34 -07:00
Sergey Tsalkov
a2800ef04b add insertIgnore() function -- pretty self explanatory 2011-09-23 16:11:10 -07:00
Sergey Tsalkov
164c7157a6 queryRaw() will now be officially supported, returns mysqli_result instead of array of assoc
arrays
2011-09-23 15:57:36 -07:00
Sergey Tsalkov
f09308a3f7 re-organize test files, make it easy to test for both php 5.2 and 5.3 2011-09-23 15:51:35 -07:00
Sergey Tsalkov
69bfe15ebb add test case regarding BLOBs 2011-08-25 23:54:52 -07:00
Sergey Tsalkov
7621b50a02 handle non-sql errors (such as failure to connect to MySQL server) in a user-defined way
adds DB::$nonsql_error_handler and DB::$throw_exception_on_nonsql_error
2011-08-25 23:34:37 -07:00
Sergey Tsalkov
4122fe52c1 collapse some redundant code into a function 2011-06-02 18:57:46 -04:00
Sergey Tsalkov
8072f6ab07 use static class variable, not static function variable, to hold mysql connection 2011-06-01 17:30:28 -04:00
Sergey Tsalkov
860da8d18a insert(), replace(), and update() now handle the value NULL correctly 2011-05-31 21:50:24 -04:00
Sergey Tsalkov
0ed2837f3d on second thought, we won't have insertMany() and replaceMany(), but insert() and replace()
will figure out which one you mean

this means that a minor undocumented feature, being able to insert an array and have it
automatically serialized, can't exist anymore
2011-04-29 01:48:27 -04:00
Sergey Tsalkov
65cae974c5 remove unused variable 2011-04-28 18:51:53 -04:00
Sergey Tsalkov
96892fa6c2 DB::$param_char can now be used to change the char that needs to appear in front of params (% is the default) 2011-04-28 18:41:13 -04:00
Sergey Tsalkov
ad4889da09 revert last commit -- this is a bad way to do it, and I'll have to come up with something else
to let people use strings like %i in their SQL and bypass the parser
2011-04-28 18:14:59 -04:00
Sergey Tsalkov
12604d7854 bugfix: let people have literal %'s in their queries without confusing the parser
by using %% instead
2011-04-23 20:31:18 -04:00
Sergey Tsalkov
8c5299c2e2 * add insertMany() and replaceMany() to let you do multiple inserts in one SQL command
* error and success handlers can now be set to static and object methods
2011-04-23 00:46:51 -04:00
Sergey Tsalkov
964a36e4c2 sqleval() can now be used with %s, %i, and all that good stuff
there is probably no good reason to do this, as things like md5() can be done
from PHP, but people keep asking for it, so here it is :)
2011-04-22 23:29:09 -04:00
Sergey Tsalkov
68774532e1 add delete() function -- mostly for completeness with insert(), update(), etc 2011-04-22 23:15:14 -04:00
Sergey Tsalkov
2985815750 * add tests for tableList() and columnList() -- those are now supported functions
* remove some unused code, minor cleanup
2011-04-22 23:03:37 -04:00
Sergey Tsalkov
4faebb957c add DB::sqleval() -- can be used with insert() and update() to inject raw sql stuff like NOW() 2011-04-08 13:56:28 -04:00
Sergey Tsalkov
819acb9bff use fetch_row and not fetch_assoc for queryFirstField and queryFirstColumn -- it should be a bit faster 2011-03-30 13:29:23 -04:00
Sergey Tsalkov
449760eb2a set the character encoding in the "more proper" way, as recommended by the mysqli manual 2011-03-17 13:14:39 -04:00
Sergey Tsalkov
74feeb6513 now you can use DB::$success_handler to replace the "debug mode" handler in the same way that you can
replace the error handler
2011-03-15 16:03:56 -04:00
Sergey Tsalkov
c14c69ffa7 let user specify port with DB::$port -- defaults to mysqli.default_port from the php config (probably 3306) 2011-03-15 14:42:58 -04:00
Sergey Tsalkov
894d8a0530 various fixes for PHP 5.2 compatability -- now finally tested and working with 5.2 2011-03-15 13:06:45 -04:00
Sergey Tsalkov
b6b22833e9 this is distributed under the LGPL v3 -- add copyright information 2011-03-13 17:16:18 -04:00
Sergey Tsalkov
5525d22a9b we can now throw an exception on errors if DB::$throw_exception_on_error is set 2011-03-04 17:23:42 -05:00
Sergey Tsalkov
0a7b323e81 you can now specify your own error handling function by changing DB::$error_handler 2011-03-04 16:49:17 -05:00
Sergey Tsalkov
620c607f61 remove array_map with inline functions for PHP 5.2 compatability
thanks to Adam for pointing this out!
2011-03-02 12:46:07 -05:00
Sergey Tsalkov
78c8192fdd increase "error output" level and fix various minor bugs
none of these seem to affect functionality, it's just a matter of clean code
thanks to ++ for pointing this out!
2011-03-01 00:12:31 -05:00
Sergey Tsalkov
d01a8be627 more basic tests 2011-03-01 00:12:25 -05:00
15 changed files with 2591 additions and 389 deletions

1
.gitignore vendored
View File

@@ -0,0 +1 @@
simpletest/test_setup.php

165
LICENSE Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

167
README.md Normal file
View File

@@ -0,0 +1,167 @@
MeekroDB -- The Simple PHP MySQL Library
========
Learn more: http://www.meekro.com
MeekroDB is:
* A PHP MySQL library that lets you **get more done with fewer lines of code**, and **makes SQL injection 100% impossible**.
* Google's #1 search result for "php mysql library" since 2013, with **thousands of deployments worldwide**.
* A library with a **perfect security track record**. No bugs relating to security or SQL injection have ever been discovered.
Installation
========
When you're ready to get started, see the [Quick Start Guide](http://www.meekro.com/quickstart.php) on our website.
### Manual Setup
Include the `db.class.php` file into your project and set it up like this:
```php
require_once 'db.class.php';
DB::$user = 'my_database_user';
DB::$password = 'my_database_password';
DB::$dbName = 'my_database_name';
```
### Composer
Add this to your `composer.json`
```json
{
"require": {
"sergeytsalkov/meekrodb": "*"
}
}
```
Code Examples
========
### Grab some rows from the database and print out a field from each row.
```php
$accounts = DB::query("SELECT * FROM accounts WHERE type = %s AND age > %i", $type, 15);
foreach ($accounts as $account) {
echo $account['username'] . "\n";
}
```
### Insert a new row.
```php
DB::insert('mytable', array(
'name' => $name,
'rank' => $rank,
'location' => $location,
'age' => $age,
'intelligence' => $intelligence
));
```
### Grab one row or field
```php
$account = DB::queryFirstRow("SELECT * FROM accounts WHERE username=%s", 'Joe');
$number_accounts = DB::queryFirstField("SELECT COUNT(*) FROM accounts");
```
### Use a list in a query
```php
DB::query("SELECT * FROM tbl WHERE name IN %ls AND age NOT IN %li", array('John', 'Bob'), array(12, 15));
```
### Log all queries and errors
```php
// log all queries and errors to file, or ..
DB::$logfile = '/home/username/logfile.txt';
// log all queries and errors to screen
DB::$logfile = fopen('php://output', 'w');
```
### Nested Transactions
```php
DB::$nested_transactions = true;
DB::startTransaction(); // outer transaction
// .. some queries..
$depth = DB::startTransaction(); // inner transaction
echo $depth . 'transactions are currently active'; // 2
// .. some queries..
DB::commit(); // commit inner transaction
// .. some queries..
DB::commit(); // commit outer transaction
```
### Lots More - See: http://meekro.com/docs
How is MeekroDB better than PDO?
========
### Optional Static Class Mode
Most web apps will only ever talk to one database. This means that
passing $db objects to every function of your code just adds unnecessary clutter.
The simplest approach is to use static methods such as DB::query(), and that's how
MeekroDB works. Still, if you need database objects, MeekroDB can do that too.
### Do more with fewer lines of code
The code below escapes your parameters for safety, runs the query, and grabs
the first row of results. Try doing that in one line with PDO.
```php
$account = DB::queryFirstRow("SELECT * FROM accounts WHERE username=%s", 'Joe');
```
Or how about just one field?
```php
$created_at = DB::queryFirstField("SELECT created_at FROM accounts WHERE username=%s", 'Joe');
```
### Work with list parameters easily
Using MySQL's IN keyword should not be hard. MeekroDB smooths out the syntax for you,
PDO does not.
```php
$accounts = DB::query("SELECT * FROM accounts WHERE username IN %ls", array('Joe', 'Frank'));
```
### Simple inserts
Using MySQL's INSERT should not be more complicated than passing in an
associative array. MeekroDB also simplifies many related commands, including
the useful and bizarre INSERT .. ON DUPLICATE UPDATE command. PDO does none of this.
```php
DB::insert('accounts', array('username' => 'John', 'password' => 'whatever'));
```
### Nested transactions
MySQL's SAVEPOINT commands lets you create nested transactions, but only
if you keep track of SAVEPOINT ids yourself. MeekroDB does this for you,
so you can have nested transactions with no complexity or learning curve.
```php
DB::$nested_transactions = true;
DB::startTransaction(); // outer transaction
// .. some queries..
$depth = DB::startTransaction(); // inner transaction
echo $depth . 'transactions are currently active'; // 2
// .. some queries..
DB::commit(); // commit inner transaction
// .. some queries..
DB::commit(); // commit outer transaction
```
### Flexible debug logging and error handling
You can log all queries (and any errors they produce) to a file for debugging purposes. You can also add hooks that let you run your own functions at any point in the query handling process.
My Other Projects
========
A little shameless self-promotion!
* [Ark Server Hosting](https://arkservers.io) -- Ark: Survival Evolved server hosting by ArkServers.io!
* [Best Minecraft Server Hosting](https://bestminecraft.org) -- Ranking and recommendations for minecraft server hosting!
* [brooce](https://github.com/SergeyTsalkov/brooce) - Language-agnostic job queue written in Go! Write your jobs in any language, schedule them from any language, run them anywhere!

29
composer.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "sergeytsalkov/meekrodb",
"description": "The Simple PHP/MySQL Library",
"homepage": "http://www.meekro.com",
"support": {
"email": "support@meekro.com"
},
"keywords": [
"mysql",
"database",
"mysqli",
"pdo"
],
"license": "LGPL-3.0",
"authors": [
{
"name": "Sergey Tsalkov",
"email": "stsalkov@gmail.com"
}
],
"require": {
"php": ">=5.3.0"
},
"autoload": {
"classmap": [
"db.class.php"
]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,39 @@
<?
<?php
class BasicTest extends SimpleTest {
function __construct() {
require_once '../db.class.php';
DB::$user = 'libdb_user';
DB::$password = 'sdf235sklj';
DB::$dbName = 'libdb_test';
DB::query("DROP DATABASE libdb_test");
DB::query("CREATE DATABASE libdb_test");
DB::useDB('libdb_test');
foreach (DB::tableList() as $table) {
DB::query("DROP TABLE %b", $table);
}
}
function test_1_create_table() {
DB::query("CREATE TABLE `accounts` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`profile_id` INT NOT NULL,
`username` VARCHAR( 255 ) NOT NULL ,
`password` VARCHAR( 255 ) NOT NULL ,
`password` VARCHAR( 255 ) NULL ,
`age` INT NOT NULL DEFAULT '10',
`height` DOUBLE NOT NULL DEFAULT '10.0'
`height` DOUBLE NOT NULL DEFAULT '10.0',
`favorite_word` VARCHAR( 255 ) NULL DEFAULT 'hi',
`birthday` TIMESTAMP NOT NULL
) ENGINE = InnoDB");
DB::query("CREATE TABLE `profile` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`signature` VARCHAR( 255 ) NULL DEFAULT 'donewriting'
) ENGINE = InnoDB");
DB::query("CREATE TABLE `fake%s_table` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 255 ) NULL DEFAULT 'blah'
) ENGINE = InnoDB");
}
function test_1_5_empty_table() {
$counter = DB::queryFirstField("SELECT COUNT(*) FROM accounts");
$this->assert($counter === strval(0));
$this->assert(DB::lastQuery() === 'SELECT COUNT(*) FROM accounts');
$row = DB::queryFirstRow("SELECT * FROM accounts");
$this->assert($row === null);
@@ -42,11 +52,12 @@ class BasicTest extends SimpleTest {
}
function test_2_insert_row() {
DB::insert('accounts', array(
$true = DB::insert('accounts', array(
'username' => 'Abe',
'password' => 'hello'
));
$this->assert($true === true);
$this->assert(DB::affectedRows() === 1);
$counter = DB::queryFirstField("SELECT COUNT(*) FROM accounts");
@@ -60,34 +71,372 @@ class BasicTest extends SimpleTest {
'age' => 15,
'height' => 10.371
));
DB::insert('`libdb_test`.`accounts`', array(
$dbname = DB::$dbName;
DB::insert("`$dbname`.`accounts`", array(
'username' => 'Charlie\'s Friend',
'password' => 'goodbye',
'age' => 30,
'height' => 155.23
'height' => 155.23,
'favorite_word' => null,
));
$this->assert(DB::insertId() === 3);
$counter = DB::queryFirstField("SELECT COUNT(*) FROM accounts");
$this->assert($counter === strval(3));
$bart = DB::queryFirstRow("SELECT * FROM accounts WHERE age IN %li AND height IN %ld AND username IN %ls",
DB::insert('`accounts`', array(
'username' => 'Deer',
'password' => '',
'age' => 15,
'height' => 10.371
));
$username = DB::queryFirstField("SELECT username FROM accounts WHERE password=%s0", null);
$this->assert($username === 'Deer');
$password = DB::queryFirstField("SELECT password FROM accounts WHERE favorite_word IS NULL");
$this->assert($password === 'goodbye');
DB::insertUpdate('accounts', array(
'id' => 3,
'favorite_word' => null,
));
DB::$param_char = '###';
$bart = DB::queryFirstRow("SELECT * FROM accounts WHERE age IN ###li AND height IN ###ld AND username IN ###ls",
array(15, 25), array(10.371, 150.123), array('Bart', 'Barts'));
$this->assert($bart['username'] === 'Bart');
DB::insert('accounts', array('username' => 'f_u'));
DB::query("DELETE FROM accounts WHERE username=###s", 'f_u');
DB::$param_char = '%';
$charlie_password = DB::queryFirstField("SELECT password FROM accounts WHERE username IN %ls AND username = %s",
array('Charlie', 'Charlie\'s Friend'), 'Charlie\'s Friend');
$this->assert($charlie_password === 'goodbye');
$charlie_password = DB::queryOneField('password', "SELECT * FROM accounts WHERE username IN %ls AND username = %s",
array('Charlie', 'Charlie\'s Friend'), 'Charlie\'s Friend');
$this->assert($charlie_password === 'goodbye');
$passwords = DB::queryFirstColumn("SELECT password FROM accounts WHERE username=%s", 'Bart');
$this->assert(count($passwords) === 1);
$this->assert($passwords[0] === 'hello');
$username = $password = $age = null;
list($age, $username, $password) = DB::queryOneList("SELECT age,username,password FROM accounts WHERE username=%s", 'Bart');
$this->assert($username === 'Bart');
$this->assert($password === 'hello');
$this->assert($age == 15);
$mysqli_result = DB::queryRaw("SELECT * FROM accounts WHERE favorite_word IS NULL");
$this->assert($mysqli_result instanceof MySQLi_Result);
$row = $mysqli_result->fetch_assoc();
$this->assert($row['password'] === 'goodbye');
$this->assert($mysqli_result->fetch_assoc() === null);
}
function test_4_query() {
$results = DB::query("SELECT * FROM accounts WHERE username=%s", 'Charlie\'s Friend');
DB::update('accounts', array(
'birthday' => new DateTime('10 September 2000 13:13:13')
), 'username=%s', 'Charlie\'s Friend');
$results = DB::query("SELECT * FROM accounts WHERE username=%s AND birthday IN %lt", 'Charlie\'s Friend', array('September 10 2000 13:13:13'));
$this->assert(count($results) === 1);
$this->assert($results[0]['age'] == 30 && $results[0]['password'] == 'goodbye');
$this->assert($results[0]['age'] === '30' && $results[0]['password'] === 'goodbye');
$this->assert($results[0]['birthday'] == '2000-09-10 13:13:13');
$results = DB::query("SELECT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert(count($results) === 2);
$this->assert(count($results) === 3);
$columnList = DB::columnList('accounts');
$columnKeys = array_keys($columnList);
$this->assert(count($columnList) === 8);
$this->assert($columnList['id']['type'] == 'int(11)');
$this->assert($columnList['height']['type'] == 'double');
$this->assert($columnKeys[5] == 'height');
$tablelist = DB::tableList();
$this->assert(count($tablelist) === 3);
$this->assert($tablelist[0] === 'accounts');
$tablelist = null;
$tablelist = DB::tableList(DB::$dbName);
$this->assert(count($tablelist) === 3);
$this->assert($tablelist[0] === 'accounts');
$date = DB::queryFirstField("SELECT DATE_FORMAT(birthday, '%%m/%%d/%%Y') FROM accounts WHERE username=%s", "Charlie's Friend");
$this->assert($date === '09/10/2000');
$date = DB::queryFirstField("SELECT DATE_FORMAT('2009-10-04 22:23:00', '%m/%d/%Y')");;
$this->assert($date === '10/04/2009');
}
function test_4_1_query() {
DB::insert('accounts', array(
'username' => 'newguy',
'password' => DB::sqleval("REPEAT('blah', %i)", '3'),
'age' => DB::sqleval('171+1'),
'height' => 111.15
));
$row = DB::queryOneRow("SELECT * FROM accounts WHERE password=%s", 'blahblahblah');
$this->assert($row['username'] === 'newguy');
$this->assert($row['age'] === '172');
$true = DB::update('accounts', array(
'password' => DB::sqleval("REPEAT('blah', %i)", 4),
'favorite_word' => null,
), 'username=%s_name', array('name' => 'newguy'));
$row = null;
$row = DB::queryOneRow("SELECT * FROM accounts WHERE username=%s", 'newguy');
$this->assert($true === true);
$this->assert($row['password'] === 'blahblahblahblah');
$this->assert($row['favorite_word'] === null);
$row = DB::query("SELECT * FROM accounts WHERE password=%s_mypass AND (password=%s_mypass) AND username=%s_myuser",
array('myuser' => 'newguy', 'mypass' => 'blahblahblahblah')
);
$this->assert(count($row) === 1);
$this->assert($row[0]['username'] === 'newguy');
$this->assert($row[0]['age'] === '172');
$row = DB::query("SELECT * FROM accounts WHERE password IN %li AND password IN %li0 AND username=%s", array('blahblahblahblah'), 'newguy');
$this->assert(count($row) === 1);
$this->assert($row[0]['username'] === 'newguy');
$this->assert($row[0]['age'] === '172');
$true = DB::query("DELETE FROM accounts WHERE password=%s", 'blahblahblahblah');
$this->assert($true === true);
$this->assert(DB::affectedRows() === 1);
}
function test_4_2_delete() {
DB::insert('accounts', array(
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
));
$ct = DB::queryFirstField("SELECT COUNT(*) FROM accounts WHERE %ha", array('username' => 'gonesoon', 'height' => 199.194));
$this->assert(intval($ct) === 1);
$ct = DB::queryFirstField("SELECT COUNT(*) FROM accounts WHERE username=%s1 AND height=%d0 AND height=%d", 199.194, 'gonesoon');
$this->assert(intval($ct) === 1);
DB::delete('accounts', 'username=%s AND age=%i AND height=%d', 'gonesoon', '61', '199.194');
$this->assert(DB::affectedRows() === 1);
$ct = DB::queryFirstField("SELECT COUNT(*) FROM accounts WHERE username=%s AND height=%d", 'gonesoon', '199.194');
$this->assert(intval($ct) === 0);
}
function test_4_3_insertmany() {
$ins[] = array(
'username' => '1ofmany',
'password' => 'something',
'age' => 23,
'height' => 190.194
);
$ins[] = array(
'password' => 'somethingelse',
'username' => '2ofmany',
'age' => 25,
'height' => 190.194
);
$ins[] = array(
'password' => NULL,
'username' => '3ofmany',
'age' => 15,
'height' => 111.951
);
DB::insert('accounts', $ins);
$this->assert(DB::affectedRows() === 3);
$rows = DB::query("SELECT * FROM accounts WHERE height=%d ORDER BY age ASC", 190.194);
$this->assert(count($rows) === 2);
$this->assert($rows[0]['username'] === '1ofmany');
$this->assert($rows[0]['age'] === '23');
$this->assert($rows[1]['age'] === '25');
$this->assert($rows[1]['password'] === 'somethingelse');
$this->assert($rows[1]['username'] === '2ofmany');
$nullrow = DB::queryOneRow("SELECT * FROM accounts WHERE username LIKE %ss", '3ofman');
$this->assert($nullrow['password'] === NULL);
$this->assert($nullrow['age'] === '15');
}
function test_5_insert_blobs() {
DB::query("CREATE TABLE `store data` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`picture` BLOB
) ENGINE = InnoDB");
$columns = DB::columnList('store data');
$this->assert(count($columns) === 2);
$this->assert($columns['picture']['type'] === 'blob');
$smile = file_get_contents(__DIR__ . '/smile1.jpg');
DB::insert('store data', array(
'picture' => $smile,
));
DB::queryOneRow("INSERT INTO %b (picture) VALUES (%s)", 'store data', $smile);
$getsmile = DB::queryFirstField("SELECT picture FROM %b WHERE id=1", 'store data');
$getsmile2 = DB::queryFirstField("SELECT picture FROM %b WHERE id=2", 'store data');
$this->assert($smile === $getsmile);
$this->assert($smile === $getsmile2);
}
function test_6_insert_ignore() {
DB::insertIgnore('accounts', array(
'id' => 1, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
));
}
function test_7_insert_update() {
$true = DB::insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
), 'age = age + %i', 1);
$this->assert($true === true);
$this->assert(DB::affectedRows() === 2); // a quirk of MySQL, even though only 1 row was updated
$result = DB::query("SELECT * FROM accounts WHERE age = %i", 16);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '10.371');
DB::insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'blahblahdude',
'age' => 233,
'height' => 199.194
));
$result = DB::query("SELECT * FROM accounts WHERE age = %i", 233);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '199.194');
$this->assert($result[0]['username'] === 'blahblahdude');
DB::insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
), array(
'age' => 74,
));
$result = DB::query("SELECT * FROM accounts WHERE age = %i", 74);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '199.194');
$this->assert($result[0]['username'] === 'blahblahdude');
$multiples[] = array(
'id' => 3, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
);
$multiples[] = array(
'id' => 1, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
);
DB::insertUpdate('accounts', $multiples, array('age' => 914));
$this->assert(DB::affectedRows() === 4);
$result = DB::query("SELECT * FROM accounts WHERE age=914 ORDER BY id ASC");
$this->assert(count($result) === 2);
$this->assert($result[0]['username'] === 'Abe');
$this->assert($result[1]['username'] === 'Charlie\'s Friend');
$true = DB::query("UPDATE accounts SET age=15, username='Bart' WHERE age=%i", 74);
$this->assert($true === true);
$this->assert(DB::affectedRows() === 1);
}
function test_8_lb() {
$data = array(
'username' => 'vookoo',
'password' => 'dookoo',
);
$true = DB::query("INSERT into accounts %lb VALUES %ls", array_keys($data), array_values($data));
$result = DB::query("SELECT * FROM accounts WHERE username=%s", 'vookoo');
$this->assert($true === true);
$this->assert(count($result) === 1);
$this->assert($result[0]['password'] === 'dookoo');
}
function test_9_fullcolumns() {
$true = DB::insert('profile', array(
'id' => 1,
'signature' => 'u_suck'
));
DB::query("UPDATE accounts SET profile_id=1 WHERE id=2");
$r = DB::queryFullColumns("SELECT accounts.*, profile.*, 1+1 FROM accounts
INNER JOIN profile ON accounts.profile_id=profile.id");
$this->assert($true === true);
$this->assert(count($r) === 1);
$this->assert($r[0]['accounts.id'] === '2');
$this->assert($r[0]['profile.id'] === '1');
$this->assert($r[0]['profile.signature'] === 'u_suck');
$this->assert($r[0]['1+1'] === '2');
}
function test_901_updatewithspecialchar() {
$data = 'www.mysite.com/product?s=t-%s-%%3d%%3d%i&RCAID=24322';
DB::update('profile', array('signature' => $data), 'id=%i', 1);
$signature = DB::queryFirstField("SELECT signature FROM profile WHERE id=%i", 1);
$this->assert($signature === $data);
DB::update('profile',array('signature'=> "%li "),"id = %d",1);
$signature = DB::queryFirstField("SELECT signature FROM profile WHERE id=%i", 1);
$this->assert($signature === "%li ");
}
function test_902_faketable() {
DB::insert('fake%s_table', array('name' => 'karen'));
$count = DB::queryFirstField("SELECT COUNT(*) FROM %b", 'fake%s_table');
$this->assert($count === '1');
DB::update('fake%s_table', array('name' => 'haren%s'), 'name=%s_name', array('name' => 'karen'));
DB::delete('fake%s_table', 'name=%s0', 'haren%s');
$count = DB::queryFirstField("SELECT COUNT(*) FROM %b", 'fake%s_table');
$this->assert($count === '0');
}
function test_10_parse() {
$parsed_query = DB::parse("SELECT * FROM %b WHERE id=%i AND name=%s", 'accounts', 5, 'Joe');
$correct_query = "SELECT * FROM `accounts` WHERE id=5 AND name='Joe'";
$this->assert($parsed_query === $correct_query);
$parsed_query = DB::parse("SELECT DATE_FORMAT(birthday, '%%Y-%%M-%%d %%h:%%i:%%s') AS mydate FROM accounts WHERE id=%i", 5);
$correct_query = "SELECT DATE_FORMAT(birthday, '%Y-%M-%d %h:%i:%s') AS mydate FROM accounts WHERE id=5";
$this->assert($parsed_query === $correct_query);
}
}

17
simpletest/CallTest.php Normal file
View File

@@ -0,0 +1,17 @@
<?php
class CallTest extends SimpleTest {
function test_1_create_procedure() {
DB::query("DROP PROCEDURE IF EXISTS myProc");
DB::query("CREATE PROCEDURE myProc()
BEGIN
SELECT * FROM accounts;
END");
}
function test_2_run_procedure() {
$r = DB::query("CALL myProc()");
$this->assert($r[0]['username'] === 'Abe');
$this->assert($r[2]['age'] === '914');
}
}

287
simpletest/HookTest.php Normal file
View File

@@ -0,0 +1,287 @@
<?php
function my_error_handler($hash) {
global $error_callback_worked;
if (substr_count($hash['error'], 'You have an error in your SQL syntax')) $error_callback_worked = 1;
return false;
}
function my_success_handler($hash) {
global $debug_callback_worked;
if (substr_count($hash['query'], 'SELECT')) $debug_callback_worked = 1;
return false;
}
class HookTest extends SimpleTest {
static function static_error_callback($hash) {
global $static_error_callback_worked;
if (substr_count($hash['error'], 'You have an error in your SQL syntax')) $static_error_callback_worked = 1;
return false;
}
function nonstatic_error_callback($hash) {
global $nonstatic_error_callback_worked;
if (substr_count($hash['error'], 'You have an error in your SQL syntax')) $nonstatic_error_callback_worked = 1;
return false;
}
function test_1_error_handler() {
global $error_callback_worked, $static_error_callback_worked, $nonstatic_error_callback_worked;
DB::addHook('run_failed', 'my_error_handler');
DB::query("SELET * FROM accounts");
$this->assert($error_callback_worked === 1);
DB::removeHooks('run_failed');
DB::addHook('run_failed', array('HookTest', 'static_error_callback'));
DB::query("SELET * FROM accounts");
$this->assert($static_error_callback_worked === 1);
DB::removeHooks('run_failed');
DB::addHook('run_failed', array($this, 'nonstatic_error_callback'));
DB::query("SELET * FROM accounts");
$this->assert($nonstatic_error_callback_worked === 1);
DB::removeHooks('run_failed');
}
function test_2_exception_catch() {
$dbname = DB::$dbName;
try {
DB::query("SELET * FROM accounts");
} catch(MeekroDBException $e) {
$this->assert(substr_count($e->getMessage(), 'You have an error in your SQL syntax'));
$this->assert($e->getQuery() === 'SELET * FROM accounts');
$exception_was_caught = 1;
}
$this->assert($exception_was_caught === 1);
$this->assert(DB::lastQuery() === 'SELET * FROM accounts');
try {
DB::insert("`$dbname`.`accounts`", array(
'id' => 2,
'username' => 'Another Dude\'s \'Mom"',
'password' => 'asdfsdse',
'age' => 35,
'height' => 555.23
));
} catch(MeekroDBException $e) {
$this->assert(substr_count($e->getMessage(), 'Duplicate entry'));
$exception_was_caught = 2;
}
$this->assert($exception_was_caught === 2);
}
function test_3_success_handler() {
global $debug_callback_worked;
DB::addHook('run_success', 'my_success_handler');
DB::query("SELECT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert($debug_callback_worked === 1);
DB::removeHooks('run_success');
}
function test_4_error_handler() {
global $anonymous_error_callback_worked;
$error_handler = function($hash) {
global $anonymous_error_callback_worked;
if (substr_count($hash['error'], 'You have an error in your SQL syntax')) {
$anonymous_error_callback_worked = 1;
}
return false;
};
DB::addHook('run_failed', $error_handler);
DB::query("SELET * FROM accounts");
$this->assert($anonymous_error_callback_worked === 1);
DB::removeHooks('run_failed');
}
function test_5_post_run_success() {
$callback_worked = false;
$fn = function($hash) use (&$callback_worked) {
if (!isset($hash['error']) && !isset($hash['exception'])) {
$callback_worked = true;
}
};
DB::addHook('post_run', $fn);
DB::query("SELECT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert($callback_worked);
DB::removeHooks('post_run');
}
function test_6_post_run_failed() {
$callback_worked = false;
$fn = function($hash) use (&$callback_worked) {
if ($hash['error'] && $hash['exception']) {
$expected_query = "SELEC * FROM accounts WHERE username!='Charlie\'s Friend'";
$expected_error = "error in your SQL syntax";
if ($hash['exception']->getQuery() == $expected_query && substr_count($hash['error'], $expected_error)) {
$callback_worked = true;
}
}
};
DB::addHook('post_run', $fn);
DB::addHook('run_failed', function() { return false; }); // disable exception throwing
DB::query("SELEC * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert($callback_worked);
DB::removeHooks('post_run');
DB::removeHooks('run_failed');
}
function test_7_pre_run() {
$callback_worked = false;
$fn = function($args) { return str_replace('SLCT', 'SELET', $args['query']); };
$fn2 = function($args) { return str_replace('SELET', 'SELECT', $args['query']); };
$fn3 = function($args) use (&$callback_worked) { $callback_worked = true; };
DB::addHook('pre_run', $fn);
DB::addHook('pre_run', $fn2);
$last_hook = DB::addHook('pre_run', $fn3);
$results = DB::query("SLCT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert(count($results) == 4);
$this->assert($callback_worked);
$callback_worked = false;
DB::removeHook('pre_run', $last_hook);
$results = DB::query("SLCT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert(count($results) == 4);
$this->assert(!$callback_worked);
DB::removeHooks('pre_run');
}
function test_8_pre_parse() {
$callback_worked = false;
$fn = function($args) {
$args['query'] = str_replace('SLCT', 'SELECT', $args['query']);
return array($args['query'], $args['args']);
};
$fn2 = function($args) {
$args['args'][0] = '1ofmany';
return array($args['query'], $args['args']);
};
$fn3 = function() use (&$callback_worked) {
$callback_worked = true;
};
DB::addHook('pre_parse', $fn);
DB::addHook('pre_parse', $fn2);
DB::addHook('pre_parse', $fn3);
$row = DB::queryFirstRow("SLCT * FROM accounts WHERE username=%s", "asdf");
$this->assert($row['password'] == 'something');
$this->assert($callback_worked);
DB::removeHooks('pre_parse');
}
function test_9_enough_args() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id=%i AND username=%s", 1);
} catch (MeekroDBException $e) {
if ($e->getMessage() == 'Expected 2 args, but only got 1!') {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_10_named_keys_present() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id=%i_id AND username=%s_username", array('username' => 'asdf'));
} catch (MeekroDBException $e) {
if ($e->getMessage() == "Couldn't find named arg id!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_11_expect_array() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id IN %li", 5);
} catch (MeekroDBException $e) {
if ($e->getMessage() == "Expected an array for arg 0 but didn't get one!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_12_named_keys_without_array() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id=%i_named", 1);
} catch (MeekroDBException $e) {
if ($e->getMessage() == "If you use named args, you must pass an assoc array of args!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_13_mix_named_numbered_args() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id=%i_named AND username=%s", array('named' => 1));
} catch (MeekroDBException $e) {
if ($e->getMessage() == "You can't mix named and numbered args!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_14_arrays_not_empty() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id IN %li", array());
} catch (MeekroDBException $e) {
if ($e->getMessage() == "Arg 0 array can't be empty!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
function test_15_named_array_not_empty() {
$error_worked = false;
try {
DB::query("SELECT * FROM accounts WHERE id IN %li_ids", array('ids' => array()));
} catch (MeekroDBException $e) {
if ($e->getMessage() == "Arg ids array can't be empty!") {
$error_worked = true;
}
}
$this->assert($error_worked);
}
}
?>

314
simpletest/ObjectTest.php Normal file
View File

@@ -0,0 +1,314 @@
<?php
class ObjectTest extends SimpleTest {
public $mdb;
function __construct() {
$this->mdb = new MeekroDB();
foreach ($this->mdb->tableList() as $table) {
$this->mdb->query("DROP TABLE %b", $table);
}
}
function test_1_create_table() {
$this->mdb->query("CREATE TABLE `accounts` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 255 ) NOT NULL ,
`password` VARCHAR( 255 ) NULL ,
`age` INT NOT NULL DEFAULT '10',
`height` DOUBLE NOT NULL DEFAULT '10.0',
`favorite_word` VARCHAR( 255 ) NULL DEFAULT 'hi'
) ENGINE = InnoDB");
}
function test_1_5_empty_table() {
$counter = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts");
$this->assert($counter === strval(0));
$row = $this->mdb->queryFirstRow("SELECT * FROM accounts");
$this->assert($row === null);
$field = $this->mdb->queryFirstField("SELECT * FROM accounts");
$this->assert($field === null);
$field = $this->mdb->queryOneField('nothere', "SELECT * FROM accounts");
$this->assert($field === null);
$column = $this->mdb->queryFirstColumn("SELECT * FROM accounts");
$this->assert(is_array($column) && count($column) === 0);
$column = $this->mdb->queryOneColumn('nothere', "SELECT * FROM accounts"); //TODO: is this what we want?
$this->assert(is_array($column) && count($column) === 0);
}
function test_2_insert_row() {
$this->mdb->insert('accounts', array(
'username' => 'Abe',
'password' => 'hello'
));
$this->assert($this->mdb->affectedRows() === 1);
$counter = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts");
$this->assert($counter === strval(1));
}
function test_3_more_inserts() {
$this->mdb->insert('`accounts`', array(
'username' => 'Bart',
'password' => 'hello',
'age' => 15,
'height' => 10.371
));
$dbname = $this->mdb->dbName;
$this->mdb->insert("`$dbname`.`accounts`", array(
'username' => 'Charlie\'s Friend',
'password' => 'goodbye',
'age' => 30,
'height' => 155.23,
'favorite_word' => null,
));
$this->assert($this->mdb->insertId() === 3);
$counter = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts");
$this->assert($counter === strval(3));
$password = $this->mdb->queryFirstField("SELECT password FROM accounts WHERE favorite_word IS NULL");
$this->assert($password === 'goodbye');
$this->mdb->param_char = '###';
$bart = $this->mdb->queryFirstRow("SELECT * FROM accounts WHERE age IN ###li AND height IN ###ld AND username IN ###ls",
array(15, 25), array(10.371, 150.123), array('Bart', 'Barts'));
$this->assert($bart['username'] === 'Bart');
$this->mdb->param_char = '%';
$charlie_password = $this->mdb->queryFirstField("SELECT password FROM accounts WHERE username IN %ls AND username = %s",
array('Charlie', 'Charlie\'s Friend'), 'Charlie\'s Friend');
$this->assert($charlie_password === 'goodbye');
$charlie_password = $this->mdb->queryOneField('password', "SELECT * FROM accounts WHERE username IN %ls AND username = %s",
array('Charlie', 'Charlie\'s Friend'), 'Charlie\'s Friend');
$this->assert($charlie_password === 'goodbye');
$passwords = $this->mdb->queryFirstColumn("SELECT password FROM accounts WHERE username=%s", 'Bart');
$this->assert(count($passwords) === 1);
$this->assert($passwords[0] === 'hello');
$username = $password = $age = null;
list($age, $username, $password) = $this->mdb->queryOneList("SELECT age,username,password FROM accounts WHERE username=%s", 'Bart');
$this->assert($username === 'Bart');
$this->assert($password === 'hello');
$this->assert($age == 15);
$mysqli_result = $this->mdb->queryRaw("SELECT * FROM accounts WHERE favorite_word IS NULL");
$this->assert($mysqli_result instanceof MySQLi_Result);
$row = $mysqli_result->fetch_assoc();
$this->assert($row['password'] === 'goodbye');
$this->assert($mysqli_result->fetch_assoc() === null);
}
function test_4_query() {
$results = $this->mdb->query("SELECT * FROM accounts WHERE username=%s", 'Charlie\'s Friend');
$this->assert(count($results) === 1);
$this->assert($results[0]['age'] == 30 && $results[0]['password'] == 'goodbye');
$results = $this->mdb->query("SELECT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert(count($results) === 2);
$columnList = $this->mdb->columnList('accounts');
$columnKeys = array_keys($columnList);
$this->assert(count($columnList) === 6);
$this->assert($columnList['id']['type'] == 'int(11)');
$this->assert($columnList['height']['type'] == 'double');
$this->assert($columnKeys[4] == 'height');
$tablelist = $this->mdb->tableList();
$this->assert(count($tablelist) === 1);
$this->assert($tablelist[0] === 'accounts');
$tablelist = null;
$tablelist = $this->mdb->tableList($this->mdb->dbName);
$this->assert(count($tablelist) === 1);
$this->assert($tablelist[0] === 'accounts');
}
function test_4_1_query() {
$this->mdb->insert('accounts', array(
'username' => 'newguy',
'password' => $this->mdb->sqleval("REPEAT('blah', %i)", '3'),
'age' => $this->mdb->sqleval('171+1'),
'height' => 111.15
));
$row = $this->mdb->queryOneRow("SELECT * FROM accounts WHERE password=%s", 'blahblahblah');
$this->assert($row['username'] === 'newguy');
$this->assert($row['age'] === '172');
$this->mdb->update('accounts', array(
'password' => $this->mdb->sqleval("REPEAT('blah', %i)", 4),
'favorite_word' => null,
), 'username=%s', 'newguy');
$row = null;
$row = $this->mdb->queryOneRow("SELECT * FROM accounts WHERE username=%s", 'newguy');
$this->assert($row['password'] === 'blahblahblahblah');
$this->assert($row['favorite_word'] === null);
$this->mdb->query("DELETE FROM accounts WHERE password=%s", 'blahblahblahblah');
$this->assert($this->mdb->affectedRows() === 1);
}
function test_4_2_delete() {
$this->mdb->insert('accounts', array(
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
));
$ct = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts WHERE username=%s AND height=%d", 'gonesoon', 199.194);
$this->assert(intval($ct) === 1);
$ct = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts WHERE username=%s1 AND height=%d0 AND height=%d", 199.194, 'gonesoon');
$this->assert(intval($ct) === 1);
$this->mdb->delete('accounts', 'username=%s AND age=%i AND height=%d', 'gonesoon', '61', '199.194');
$this->assert($this->mdb->affectedRows() === 1);
$ct = $this->mdb->queryFirstField("SELECT COUNT(*) FROM accounts WHERE username=%s AND height=%d", 'gonesoon', '199.194');
$this->assert(intval($ct) === 0);
}
function test_4_3_insertmany() {
$ins[] = array(
'username' => '1ofmany',
'password' => 'something',
'age' => 23,
'height' => 190.194
);
$ins[] = array(
'password' => 'somethingelse',
'username' => '2ofmany',
'age' => 25,
'height' => 190.194
);
$this->mdb->insert('accounts', $ins);
$this->assert($this->mdb->affectedRows() === 2);
$rows = $this->mdb->query("SELECT * FROM accounts WHERE height=%d ORDER BY age ASC", 190.194);
$this->assert(count($rows) === 2);
$this->assert($rows[0]['username'] === '1ofmany');
$this->assert($rows[0]['age'] === '23');
$this->assert($rows[1]['age'] === '25');
$this->assert($rows[1]['password'] === 'somethingelse');
$this->assert($rows[1]['username'] === '2ofmany');
}
function test_5_insert_blobs() {
$this->mdb->query("CREATE TABLE `storedata` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`picture` BLOB
) ENGINE = InnoDB");
$smile = file_get_contents(__DIR__ . '/smile1.jpg');
$this->mdb->insert('storedata', array(
'picture' => $smile,
));
$this->mdb->query("INSERT INTO storedata (picture) VALUES (%s)", $smile);
$getsmile = $this->mdb->queryFirstField("SELECT picture FROM storedata WHERE id=1");
$getsmile2 = $this->mdb->queryFirstField("SELECT picture FROM storedata WHERE id=2");
$this->assert($smile === $getsmile);
$this->assert($smile === $getsmile2);
}
function test_6_insert_ignore() {
$this->mdb->insertIgnore('accounts', array(
'id' => 1, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
));
}
function test_7_insert_update() {
$this->mdb->insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
), 'age = age + %i', 1);
$this->assert($this->mdb->affectedRows() === 2); // a quirk of MySQL, even though only 1 row was updated
$result = $this->mdb->query("SELECT * FROM accounts WHERE age = %i", 16);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '10.371');
$this->mdb->insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'blahblahdude',
'age' => 233,
'height' => 199.194
));
$result = $this->mdb->query("SELECT * FROM accounts WHERE age = %i", 233);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '199.194');
$this->assert($result[0]['username'] === 'blahblahdude');
$this->mdb->insertUpdate('accounts', array(
'id' => 2, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
), array(
'age' => 74,
));
$result = $this->mdb->query("SELECT * FROM accounts WHERE age = %i", 74);
$this->assert(count($result) === 1);
$this->assert($result[0]['height'] === '199.194');
$this->assert($result[0]['username'] === 'blahblahdude');
$multiples[] = array(
'id' => 3, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
);
$multiples[] = array(
'id' => 1, //duplicate primary key
'username' => 'gonesoon',
'password' => 'something',
'age' => 61,
'height' => 199.194
);
$this->mdb->insertUpdate('accounts', $multiples, array('age' => 914));
$this->assert($this->mdb->affectedRows() === 4);
$result = $this->mdb->query("SELECT * FROM accounts WHERE age=914 ORDER BY id ASC");
$this->assert(count($result) === 2);
$this->assert($result[0]['username'] === 'Abe');
$this->assert($result[1]['username'] === 'Charlie\'s Friend');
$this->mdb->query("UPDATE accounts SET age=15, username='Bart' WHERE age=%i", 74);
$this->assert($this->mdb->affectedRows() === 1);
}
}
?>

View File

@@ -0,0 +1,30 @@
<?php
class TransactionTest extends SimpleTest {
function test_1_transactions() {
DB::$nested_transactions = false;
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 600, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 1);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 1);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
$depth = DB::rollback();
$this->assert($depth === 0);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 700);
$depth = DB::rollback();
$this->assert($depth === 0);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 700);
}
}
?>

View File

@@ -0,0 +1,85 @@
<?php
class TransactionTest_55 extends SimpleTest {
function test_1_transactions() {
DB::$nested_transactions = true;
$depth = DB::startTransaction();
$this->assert($depth === 1);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 2);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 3);
$this->assert(DB::transactionDepth() === 3);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 500, 'Abe');
$depth = DB::commit();
$this->assert($depth === 2);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 500);
$depth = DB::rollback();
$this->assert($depth === 1);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 700);
$depth = DB::commit();
$this->assert($depth === 0);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 700);
DB::$nested_transactions = false;
}
function test_2_transactions() {
DB::$nested_transactions = true;
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 600, 'Abe');
DB::startTransaction();
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 700, 'Abe');
DB::startTransaction();
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 800, 'Abe');
DB::rollback();
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 700);
DB::rollback();
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 600);
DB::$nested_transactions = false;
}
function test_3_transaction_rollback_all() {
DB::$nested_transactions = true;
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 200, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 1);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 300, 'Abe');
$depth = DB::startTransaction();
$this->assert($depth === 2);
DB::query("UPDATE accounts SET age=%i WHERE username=%s", 400, 'Abe');
$depth = DB::rollback(true);
$this->assert($depth === 0);
$age = DB::queryFirstField("SELECT age FROM accounts WHERE username=%s", 'Abe');
$this->assert($age == 200);
DB::$nested_transactions = false;
}
}
?>

48
simpletest/WalkTest.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
class WalkTest extends SimpleTest {
function test_1_walk() {
$Walk = DB::queryWalk("SELECT * FROM accounts");
$results = array();
while ($row = $Walk->next()) {
$results[] = $row;
}
$this->assert(count($results) == 8);
$this->assert($results[7]['username'] == 'vookoo');
}
function test_2_walk_empty() {
$Walk = DB::queryWalk("SELECT * FROM accounts WHERE id>100");
$results = array();
while ($row = $Walk->next()) {
$results[] = $row;
}
$this->assert(count($results) == 0);
}
function test_3_walk_insert() {
$Walk = DB::queryWalk("INSERT INTO profile (id) VALUES (100)");
$results = array();
while ($row = $Walk->next()) {
$results[] = $row;
}
$this->assert(count($results) == 0);
DB::query("DELETE FROM profile WHERE id=100");
}
function test_4_walk_incomplete() {
$Walk = DB::queryWalk("SELECT * FROM accounts");
$Walk->next();
unset($Walk);
// if $Walk hasn't been properly freed, this will produce an out of sync error
DB::query("SELECT * FROM accounts");
}
}

View File

@@ -0,0 +1,83 @@
<?php
class WhereClauseTest extends SimpleTest {
function test_1_basic_where() {
$where = new WhereClause('and');
$where->add('username=%s', 'Bart');
$where->add('password=%s', 'hello');
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 1);
$this->assert($result[0]['age'] === '15');
}
function test_2_simple_grouping() {
$where = new WhereClause('and');
$where->add('password=%s', 'hello');
$subclause = $where->addClause('or');
$subclause->add('age=%i', 15);
$subclause->add('age=%i', 14);
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 1);
$this->assert($result[0]['age'] === '15');
}
function test_3_negate_last() {
$where = new WhereClause('and');
$where->add('password=%s', 'hello');
$subclause = $where->addClause('or');
$subclause->add('username!=%s', 'Bart');
$subclause->negateLast();
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 1);
$this->assert($result[0]['age'] === '15');
}
function test_4_negate_last_query() {
$where = new WhereClause('and');
$where->add('password=%s', 'hello');
$subclause = $where->addClause('or');
$subclause->add('username!=%s', 'Bart');
$where->negateLast();
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 1);
$this->assert($result[0]['age'] === '15');
}
function test_5_negate() {
$where = new WhereClause('and');
$where->add('password=%s', 'hello');
$subclause = $where->addClause('or');
$subclause->add('username!=%s', 'Bart');
$subclause->negate();
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 1);
$this->assert($result[0]['age'] === '15');
}
function test_6_negate_two() {
$where = new WhereClause('and');
$where->add('password=%s', 'hello');
$where->add('username=%s', 'Bart');
$where->negate();
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 7);
}
function test_7_or() {
$where = new WhereClause('or');
$where->add('username=%s', 'Bart');
$where->add('username=%s', 'Abe');
$result = DB::query("SELECT * FROM accounts WHERE %l", $where);
$this->assert(count($result) === 2);
}
}
?>

BIN
simpletest/smile1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -1,7 +1,7 @@
#!/usr/bin/php
<?
<?php
class SimpleTest {
protected function assert($boolean) {
public function assert($boolean) {
if (! $boolean) $this->fail();
}
@@ -10,51 +10,66 @@ class SimpleTest {
debug_print_backtrace();
die;
}
public static function __listfiles($dir, $regex, $type='files', $rec = false) {
$A = array();
if (! $dir_handler = @opendir($dir)) return $A;
while (false !== ($filename = @readdir($dir_handler))) {
if ($filename == '.' || $filename == '..') continue;
if ($rec && is_dir("$dir/$filename")) $A = array_merge($A, File::listfiles("$dir/$filename", $regex, $type, true));
if (! preg_match($regex, $filename)) continue;
if ($type == 'files' && ! is_file("$dir/$filename")) continue;
if ($type == 'dirs' && ! is_dir("$dir/$filename")) continue;
if ($type == 'symlinks' && ! is_link("$dir/$filename")) continue;
$A[] = "$dir/$filename";
}
return $A;
}
}
$files = SimpleTest::__listfiles(__DIR__, '/^.*php$/i');
$classes_to_test = array();
foreach ($files as $fullpath) {
$filename = basename($fullpath);
if ($fullpath == __FILE__) continue;
require_once($fullpath);
$classes_to_test[] = str_replace('.php', '', $filename);
function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
ini_set('date.timezone', 'America/Los_Angeles');
error_reporting(E_ALL | E_STRICT);
require_once __DIR__ . '/../db.class.php';
require_once __DIR__ . '/test_setup.php'; //test config values go here
// WARNING: ALL tables in the database will be dropped before the tests, including non-test related tables.
DB::$user = $set_db_user;
DB::$password = $set_password;
DB::$dbName = $set_db;
DB::$host = $set_host;
DB::get(); //connect to mysql
require_once __DIR__ . '/BasicTest.php';
require_once __DIR__ . '/WalkTest.php';
require_once __DIR__ . '/CallTest.php';
require_once __DIR__ . '/ObjectTest.php';
require_once __DIR__ . '/WhereClauseTest.php';
require_once __DIR__ . '/HookTest.php';
require_once __DIR__ . '/TransactionTest.php';
$classes_to_test = array(
'BasicTest',
'WalkTest',
'CallTest',
'WhereClauseTest',
'ObjectTest',
'HookTest',
'TransactionTest',
);
$mysql_version = DB::serverVersion();
if ($mysql_version >= '5.5') {
require_once __DIR__ . '/TransactionTest_55.php';
$classes_to_test[] = 'TransactionTest_55';
} else {
echo "MySQL 5.5 not available (version is $mysql_version) -- skipping MySQL 5.5 tests\n";
}
$time_start = microtime_float();
foreach ($classes_to_test as $class) {
$object = new $class();
foreach (get_class_methods($object) as $method) {
if (substr($method, 0, 2) == '__') continue;
if (substr($method, 0, 4) != 'test') continue;
echo "Running $class::$method..\n";
$object->$method();
}
}
$time_end = microtime_float();
$time = round($time_end - $time_start, 2);
echo "Completed in $time seconds\n";
?>