12 Commits
v1.3 ... v1.6

Author SHA1 Message Date
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
3 changed files with 290 additions and 68 deletions

View File

@@ -22,13 +22,11 @@ class DB
public static $insert_id = 0;
public static $num_rows = 0;
public static $affected_rows = 0;
public static $stmt = null;
public static $queryResult = null;
public static $queryResultType = null;
public static $old_db = null;
public static $current_db = null;
public static $current_db_limit = 0;
public static $dbName = null;
public static $dbName = '';
public static $user = '';
public static $password = '';
public static $host = 'localhost';
@@ -36,19 +34,19 @@ class DB
public static $encoding = 'latin1';
public static $queryMode = 'queryAllRows';
public static $success_handler = false;
public static $error_handler = 'meekrodb_error_handler';
public static $error_handler = true;
public static $throw_exception_on_error = false;
public static $param_char = '%';
public static function get($dbName = '') {
public static function get() {
static $mysql = null;
if ($mysql == null) {
if (! DB::$port) DB::$port = ini_get('mysqli.default_port');
if (DB::$dbName != '') $dbName = DB::$dbName;
DB::$current_db = $dbName;
$mysql = new mysqli(DB::$host, DB::$user, DB::$password, $dbName, DB::$port);
DB::queryNull("SET NAMES %s", DB::$encoding);
}
DB::$current_db = DB::$dbName;
$mysql = new mysqli(DB::$host, DB::$user, DB::$password, DB::$dbName, DB::$port);
$mysql->set_charset(DB::$encoding);
}
return $mysql;
}
@@ -63,12 +61,11 @@ class DB
public static function numRows() { return DB::$num_rows; }
public static function useDB() { $args = func_get_args(); return call_user_func_array('DB::setDB', $args); }
public static function setDB($dbName, $limit=0) {
public static function setDB($dbName) {
$db = DB::get();
DB::$old_db = DB::$current_db;
if (! $db->select_db($dbName)) die("unable to set db to $dbName");
DB::$current_db = $dbName;
DB::$current_db_limit = $limit;
}
@@ -85,7 +82,7 @@ class DB
}
public static function escape($str) {
$db = DB::get(DB::$dbName);
$db = DB::get();
return $db->real_escape_string($str);
}
@@ -133,7 +130,14 @@ class DB
$buildquery = "UPDATE " . self::formatTableName($table) . " SET ";
$keyval = array();
foreach ($params as $key => $value) {
$keyval[] = "`" . $key . "`=" . (is_int($value) ? $value : "'" . DB::escape($value) . "'");
if (is_object($value) && ($value instanceof MeekroDBEval)) {
$value = $value->text;
} else {
if (is_array($value)) $value = serialize($value);
$value = (is_int($value) ? $value : "'" . DB::escape($value) . "'");
}
$keyval[] = "`" . $key . "`=" . $value;
}
$buildquery = "UPDATE " . self::formatTableName($table) . " SET " . implode(', ', $keyval) . " WHERE " . $where;
@@ -141,19 +145,46 @@ class DB
call_user_func_array('DB::queryNull', $args);
}
public static function insertOrReplace($which, $table, $data) {
$data = unserialize(serialize($data)); // break references within array
$keys_str = implode(', ', DB::wrapStr(array_keys($data), '`'));
public static function insertOrReplace($which, $table, $datas) {
$datas = unserialize(serialize($datas)); // break references within array
$keys = null;
foreach ($data as &$datum) {
if (is_array($datum)) $datum = serialize($datum);
$datum = "'" . DB::escape($datum) . "'";
if (isset($datas[0]) && is_array($datas[0])) {
$many = true;
} else {
$datas = array($datas);
$many = false;
}
foreach ($datas as $data) {
if (! $keys) {
$keys = array_keys($data);
if ($many) sort($keys);
}
$insert_values = array();
foreach ($keys as $key) {
if ($many && !isset($data[$key])) die("insert/replace many: each assoc array must have the same keys!");
$datum = $data[$key];
if (is_object($datum) && ($datum instanceof MeekroDBEval)) {
$datum = $datum->text;
} else {
$datum = (is_int($datum) ? $datum : "'" . DB::escape($datum) . "'");
}
$insert_values[] = $datum;
}
$values[] = '(' . implode(', ', $insert_values) . ')';
}
$values_str = implode(', ', array_values($data));
$table = self::formatTableName($table);
$keys_str = implode(', ', DB::wrapStr($keys, '`'));
$values_str = implode(',', $values);
DB::queryNull("$which INTO $table ($keys_str) VALUES ($values_str)");
DB::queryNull("$which INTO $table ($keys_str) VALUES $values_str");
}
public static function insert($table, $data) {
@@ -164,21 +195,30 @@ class DB
return DB::insertOrReplace('REPLACE', $table, $data);
}
public static function delete() {
$args = func_get_args();
$table = self::formatTableName(array_shift($args));
$where = array_shift($args);
$buildquery = "DELETE FROM $table WHERE $where";
array_unshift($args, $buildquery);
call_user_func_array('DB::queryNull', $args);
}
public static function sqleval() {
$args = func_get_args();
$text = call_user_func_array('DB::parseQueryParams', $args);
return new MeekroDBEval($text);
}
public static function columnList($table) {
return DB::queryOneColumn('Field', "SHOW COLUMNS FROM $table");
}
public static function tableList($db = null) {
if ($db) DB::useDB($db);
$db = DB::$current_db;
return DB::queryOneColumn('Tables_in_' . $db, "SHOW TABLES");
}
private static function checkUseDB() {
if (DB::$current_db_limit > 0) {
DB::$current_db_limit -= 1;
if (DB::$current_db_limit == 0) DB::useDB(DB::$old_db);
}
$result = DB::queryFirstColumn('SHOW TABLES');
if ($db && DB::$old_db) DB::useDB(DB::$old_db);
return $result;
}
public static function parseQueryParamsOld() {
@@ -201,26 +241,25 @@ class DB
return $sql;
}
/*
%s = string
%i = integer
%d = decimal / double
%b = backtick
%l = literal
%ls = list of strings
%li = list of integers
%ld = list of doubles
%ll = list of literals
%lb = list of backticks
*/
public static function parseQueryParamsNew() {
$args = func_get_args();
$sql = array_shift($args);
$posList = array();
$pos_adj = 0;
$types = array('%ll', '%ls', '%l', '%li', '%ld', '%lb', '%s', '%i', '%d', '%b', '%ss');
$param_char_length = strlen(DB::$param_char);
$types = array(
DB::$param_char . 'll', // list of literals
DB::$param_char . 'ls', // list of strings
DB::$param_char . 'l', // literal
DB::$param_char . 'li', // list of integers
DB::$param_char . 'ld', // list of decimals
DB::$param_char . 'lb', // list of backticks
DB::$param_char . 's', // string
DB::$param_char . 'i', // integer
DB::$param_char . 'd', // double / decimal
DB::$param_char . 'b', // backtick
DB::$param_char . 'ss' // search string (like string, surrounded with %'s)
);
foreach ($types as $type) {
$lastPos = 0;
@@ -235,26 +274,25 @@ class DB
foreach ($posList as $pos => $type) {
$arg = array_shift($args);
$type = substr($type, $param_char_length);
$length_type = strlen($type) + $param_char_length;
if (in_array($type, array('%s', '%i', '%d', '%b', '%l'))) {
if (in_array($type, array('s', 'i', 'd', 'b', 'l'))) {
$array_type = false;
$arg = array($arg);
$length_type = strlen($type);
$type = '%l' . substr($type, 1);
} else if ($type == '%ss') {
$type = 'l' . $type;
} else if ($type == 'ss') {
$result = "'%" . DB::escape(str_replace(array('%', '_'), array('\%', '\_'), $arg)) . "%'";
$length_type = strlen($type);
} else {
$array_type = true;
$length_type = strlen($type);
if (! is_array($arg)) die("Badly formatted SQL query: $sql -- expecting array, but didn't get one!");
}
if ($type == '%ls') $result = DB::wrapStr($arg, "'", true);
else if ($type == '%li') $result = array_map('intval', $arg);
else if ($type == '%ld') $result = array_map('floatval', $arg);
else if ($type == '%lb') $result = array_map('DB::formatTableName', $arg);
else if ($type == '%ll') $result = $arg;
if ($type == 'ls') $result = DB::wrapStr($arg, "'", true);
else if ($type == 'li') $result = array_map('intval', $arg);
else if ($type == 'ld') $result = array_map('floatval', $arg);
else if ($type == 'lb') $result = array_map('DB::formatTableName', $arg);
else if ($type == 'll') $result = $arg;
else if (! $result) die("Badly formatted SQL query: $sql");
if (is_array($result)) {
@@ -309,8 +347,10 @@ class DB
if (DB::$success_handler) $runtime = microtime(true) - $starttime;
if (!$sql || $error = DB::checkError()) {
if (function_exists(DB::$error_handler)) {
call_user_func(DB::$error_handler, array(
if (DB::$error_handler) {
$error_handler = is_callable(DB::$error_handler) ? DB::$error_handler : 'meekrodb_error_handler';
call_user_func($error_handler, array(
'query' => $sql,
'error' => $error
));
@@ -322,7 +362,7 @@ class DB
}
} else if (DB::$success_handler) {
$runtime = sprintf('%f', $runtime * 1000);
$success_handler = function_exists(DB::$success_handler) ? DB::$success_handler : 'meekrodb_debugmode_handler';
$success_handler = is_callable(DB::$success_handler) ? DB::$success_handler : 'meekrodb_debugmode_handler';
call_user_func($success_handler, array(
'query' => $sql,
@@ -338,9 +378,6 @@ class DB
if ($is_buffered) DB::$num_rows = $result->num_rows;
else DB::$num_rows = null;
//TODO: fix DB switch back
//DB::checkUseDB();
if ($is_null) {
DB::freeResult($result);
DB::$queryResult = DB::$queryResultType = null;
@@ -361,6 +398,26 @@ class DB
return $result;
}
public static function queryAllArrays() {
$args = func_get_args();
$query = call_user_func_array('DB::queryUnbuf', $args);
$result = DB::fetchAllArrays($query);
DB::freeResult($query);
DB::$num_rows = count($result);
return $result;
}
public static function queryOneList() { $args = func_get_args(); return call_user_func_array('DB::queryFirstList', $args); }
public static function queryFirstList() {
$args = func_get_args();
$query = call_user_func_array('DB::queryUnbuf', $args);
$result = DB::fetchArray($query);
DB::freeResult($query);
return $result;
}
public static function queryOneRow() { $args = func_get_args(); return call_user_func_array('DB::queryFirstRow', $args); }
public static function queryFirstRow() {
$args = func_get_args();
@@ -371,7 +428,20 @@ class DB
}
public static function queryFirstColumn() { $args = func_get_args(); return DB::prependCall('DB::queryOneColumn', $args, null); }
public static function queryFirstColumn() {
$args = func_get_args();
$results = call_user_func_array('DB::queryAllArrays', $args);
$ret = array();
if (!count($results) || !count($results[0])) return $ret;
foreach ($results as $row) {
$ret[] = $row[0];
}
return $ret;
}
public static function queryOneColumn() {
$args = func_get_args();
$column = array_shift($args);
@@ -391,7 +461,13 @@ class DB
return $ret;
}
public static function queryFirstField() { $args = func_get_args(); return DB::prependCall('DB::queryOneField', $args, null); }
public static function queryFirstField() {
$args = func_get_args();
$row = call_user_func_array('DB::queryFirstList', $args);
if ($row == null) return null;
return $row[0];
}
public static function queryOneField() {
$args = func_get_args();
$column = array_shift($args);
@@ -431,6 +507,22 @@ class DB
}
return $A;
}
public static function fetchArray($result = null) {
if ($result === null) $result = DB::$queryResult;
if (! ($result instanceof MySQLi_Result)) return null;
return $result->fetch_row();
}
public static function fetchAllArrays($result = null) {
$A = array();
while ($row = DB::fetchArray($result)) {
$A[] = $row;
}
return $A;
}
}
class WhereClause {
@@ -547,4 +639,12 @@ function meekrodb_debugmode_handler($params) {
}
}
class MeekroDBEval {
public $text = '';
function __construct($text) {
$this->text = $text;
}
}
?>

View File

@@ -86,9 +86,11 @@ class BasicTest extends SimpleTest {
$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::$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::$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');
@@ -97,6 +99,16 @@ class BasicTest extends SimpleTest {
$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);
}
function test_4_query() {
@@ -106,14 +118,124 @@ class BasicTest extends SimpleTest {
$results = DB::query("SELECT * FROM accounts WHERE username!=%s", "Charlie's Friend");
$this->assert(count($results) === 2);
$columnlist = DB::columnList('accounts');
$this->assert(count($columnlist) === 5);
$this->assert($columnlist[0] === 'id');
$this->assert($columnlist[4] === 'height');
$tablelist = DB::tableList();
$this->assert(count($tablelist) === 1);
$this->assert($tablelist[0] === 'accounts');
$tablelist = null;
$tablelist = DB::tableList('libdb_test');
$this->assert(count($tablelist) === 1);
$this->assert($tablelist[0] === 'accounts');
}
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');
DB::update('accounts', array(
'password' => DB::sqleval("REPEAT('blah', %i)", 4),
), 'username=%s', 'newguy');
$row = null;
$row = DB::queryOneRow("SELECT * FROM accounts WHERE username=%s", 'newguy');
$this->assert($row['password'] === 'blahblahblahblah');
DB::query("DELETE FROM accounts WHERE password=%s", 'blahblahblahblah');
$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 username=%s AND height=%d", 'gonesoon', 199.194);
$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
);
DB::insert('accounts', $ins);
$this->assert(DB::affectedRows() === 2);
$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');
}
function test_5_error_handler() {
global $error_callback_worked;
global $error_callback_worked, $static_error_callback_worked, $nonstatic_error_callback_worked,
$anonymous_error_callback_worked;
DB::$error_handler = 'new_error_callback';
DB::query("SELET * FROM accounts");
$this->assert($error_callback_worked === 1);
DB::$error_handler = array('BasicTest', 'static_error_callback');
DB::query("SELET * FROM accounts");
$this->assert($static_error_callback_worked === 1);
DB::$error_handler = array($this, 'nonstatic_error_callback');
DB::query("SELET * FROM accounts");
$this->assert($nonstatic_error_callback_worked === 1);
DB::$error_handler = function($params) {
global $anonymous_error_callback_worked;
if (substr_count($params['error'], 'You have an error in your SQL syntax')) $anonymous_error_callback_worked = 1;
};
DB::query("SELET * FROM accounts");
$this->assert($anonymous_error_callback_worked === 1);
}
public static function static_error_callback($params) {
global $static_error_callback_worked;
if (substr_count($params['error'], 'You have an error in your SQL syntax')) $static_error_callback_worked = 1;
}
public function nonstatic_error_callback($params) {
global $nonstatic_error_callback_worked;
if (substr_count($params['error'], 'You have an error in your SQL syntax')) $nonstatic_error_callback_worked = 1;
}
function test_6_exception_catch() {

View File

@@ -48,7 +48,7 @@ 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();
}