diff --git a/README.md b/README.md index 6d2e2f2..ab7ab4b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,24 @@ -Simple static class for working with MySql databases +Simple static/non-static classes for working with MySql databases * **PHP version:** 7.0+ * **Composer:** `composer require power/db` * This code was taken as a basis https://github.com/colshrapnel/safemysql + +#### Two ways to use: static and non/static +All methods available in both class types +```php +use \Power\DB; + +// Non static +$db = new \Power\mDB('localhost', 'users', 'passwd', 'dbname', 'utf8mb4'); +// Select all records from users table +$data = $db->getAll('SELECT * FROM ?n', 'users'); + +// Static +DB::Init('localhost', 'users', 'passwd', 'dbname', 'utf8mb4'); +// Select all records from users table +$data = DB::getAll('SELECT * FROM ?n', 'users'); +``` + #### Work with one database ```php use \Power\DB; @@ -22,16 +39,21 @@ DB::getOne('SELECT count(*) FROM `users`'); // Insert some data $table_name = 'logs'; $data = [ - 'create_date' => ['now()'], // when the value is an array it will not be escaped, useful for functions or someone else code + 'create_date' => DB::pure('now()'), // when you don't need to escape value - use DB::pure method 'login' => 'tester', 'userid' => 5 ]; DB::query('INSERT INTO ?n SET ?u', $table_name, $data); +// or user insert method +DB::insert($table_name, $data); // Get inserted id from last query echo DB::insertId(); + +// Update records +DB::update($table_name, $data) ``` -#### Work with several databases +#### Work with several databases from static class ```php use \Power\DB; diff --git a/src/DB.php b/src/DB.php index fb3241c..13da22b 100644 --- a/src/DB.php +++ b/src/DB.php @@ -2,59 +2,41 @@ namespace Power; -use mysqli; use mysqli_result; +use stdClass; /** * Class DB + * @package Power * @method static begin_transaction ($flags = 0 , $name = 0 ) * @method static rollback ($flags = 0 , $name = 0 ) * @method static commit ($flags = 0 , $name = 0 ) + * @method static prepare (string $query) */ class DB { private static $initialized = false; private static $connections = []; - private static $configs = []; private static $current_instance = 0; - /** - * @var callable|null - */ - private static $error_handler; - /** - * @var bool set it to call error when no data found on query - */ - public static $fail_on_nodata = false; - private static $last_query_time = []; - /** - * @var int time to check connection alive from last query - */ - private static $ping_idle_time = 60; - /** - * @var array contains all queries statistics - */ - protected static $stats = []; - - /** - * @var bool|string Log all sql queries to filename - */ - private static $log_sql = false; - private static $log_sql_debug = false; - /** - * @var bool|string Log all errors to filename - */ - private static $error_log = false; private function __construct () {} + private static function CheckInit() + { + if (!self::$initialized) { + die('DB not initialized'); + } + } + /** * Switch current instance to needed database * @param int $id */ public static function Switch(int $id) { + self::CheckInit(); if ($id < 0 || $id > count(self::$connections) -1) { - self::ShowError('Wrong DB id '.$id); + self::instance()->ShowError('Wrong DB id '.$id); } self::$current_instance = $id; } @@ -65,18 +47,20 @@ public static function Switch(int $id) */ public static function SetErrorHandler(callable $handler) { - self::$error_handler = $handler; + self::CheckInit(); + self::instance()->SetErrorHandler($handler); } - public static function SetLogSql(string $file_name, bool $add_debug = false) + public static function SetLogSql(string $file_name, bool $add_debug = false) { - self::$log_sql = $file_name; - self::$log_sql_debug = $add_debug; + self::CheckInit(); + self::instance()->SetLogSql($file_name, $add_debug); } - public static function SetErrorLog(string $file_name) + public static function SetErrorLog(string $file_name) { - self::$error_log = $file_name; + self::CheckInit(); + self::instance()->SetErrorLog($file_name); } /** @@ -91,87 +75,23 @@ public static function SetErrorLog(string $file_name) */ public static function Init(string $mysql_host, string $mysql_user, string $mysql_pass, string $db_name = null, string $charset = null, int $port = null, string $socket = null): int { - self::$connections[] = null; - self::$configs[] = - [ - 'mysql_host' => $mysql_host, - 'mysql_user' => $mysql_user, - 'mysql_pass' => $mysql_pass, - 'db_name' => $db_name, - 'charset' => $charset, - 'port' => $port, - 'socket' => $socket - ]; + self::$connections[] = new mDB($mysql_host, $mysql_user, $mysql_pass, $db_name, $charset, $port, $socket); self::$initialized = true; - $id = count(self::$connections) - 1; - self::$last_query_time[$id] = time(); - return $id; + return count(self::$connections) - 1; } - /** - * @return mysqli|null - */ - public static function instance() + /** + * @return mDB + */ + public static function instance(): mDB { - self::ConnectBase(); + self::CheckInit(); return self::$connections[self::$current_instance]; } public static function __callStatic($method, $args) { - return call_user_func_array(array(self::instance(), $method), $args); - } - - private static function ConnectBase() - { - if (!self::$initialized) - { - self::ShowError('DB not initialized'); - } - if (self::$connections[self::$current_instance] !== null) { - if (self::$last_query_time[self::$current_instance] + self::$ping_idle_time < time()) - { - if (!self::$connections[self::$current_instance]->ping()) - { - // Trying to reconnect - self::$connections[self::$current_instance] = null; - } - } else { - return; - } - } - self::$connections[self::$current_instance] = new mysqli( - self::$configs[self::$current_instance]['mysql_host'], - self::$configs[self::$current_instance]['mysql_user'], - self::$configs[self::$current_instance]['mysql_pass'], - self::$configs[self::$current_instance]['db_name'], - self::$configs[self::$current_instance]['port'], - self::$configs[self::$current_instance]['socket']); - if (self::$connections[self::$current_instance]->connect_errno) { - self::ShowError('Database connect error'); - } - if (self::$configs[self::$current_instance]['charset'] !== null) - { - self::$connections[self::$current_instance]->set_charset(self::$configs[self::$current_instance]['charset']) or self::ShowError(self::$configs[self::$current_instance]->error); - } - self::$last_query_time[self::$current_instance] = time(); - } - - /** - * @param bool|string $msg - */ - public static function ShowError($msg = false ) - { - if (!$msg) { - $msg = 'Database query error'; - } - if (self::$error_handler) - { - $function = self::$error_handler; - $function($msg); - } else { - error_log($msg); - } + return call_user_func_array(array(self::instance()->getInstance(), $method), $args); } /** @@ -181,24 +101,12 @@ public static function ShowError($msg = false ) * $db->query("DELETE FROM table WHERE id=?i", $id); * * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return mysqli_result whatever mysqli_query returns + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @return bool|mysqli_result whatever mysqli_query returns */ public static function query(string $sql, ...$arg) { - return self::rawQuery(self::prepareQuery(func_get_args())); - } - - /** - * Conventional function to fetch single row. - * - * @param bool|mysqli_result $result - mysqli result - * @param int $mode - optional fetch mode - * @return array|false whatever mysqli_fetch_array returns - */ - public static function fetch($result, int $mode = MYSQLI_ASSOC) - { - return mysqli_fetch_array($result, $mode); + return self::instance()->query($sql, ...$arg); } /** @@ -208,7 +116,7 @@ public static function fetch($result, int $mode = MYSQLI_ASSOC) */ public static function affectedRows(): int { - return mysqli_affected_rows (self::instance()); + return self::instance()->affectedRows(); } /** @@ -218,17 +126,7 @@ public static function affectedRows(): int */ public static function insertId(): int { - return mysqli_insert_id(self::instance()); - } - - public static function num_rows($result) - { - return mysqli_num_rows($result); - } - - public static function free($result) - { - mysqli_free_result($result); + return self::instance()->insertId(); } /** @@ -239,28 +137,12 @@ public static function free($result) * $name = $db->getOne("SELECT name FROM table WHERE id=?i", $id); * * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return string|FALSE either first column of the first row of result set or FALSE if none found + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @return string|bool either first column of the first row of result set or FALSE if none found */ public static function getOne(string $sql, ...$arg) { - $query = self::prepareQuery(func_get_args()); - if ($res = self::rawQuery($query)) - { - $num_rows = self::num_rows($res); - if (self::$fail_on_nodata && !$num_rows) { - self::ShowError('Query result empty'); - } - if (!$num_rows) { - return false; - } - $row = self::fetch($res); - if (is_array($row)) { - return reset($row); - } - self::free($res); - } - return false; + return self::instance()->getOne($sql, ...$arg); } /** @@ -271,25 +153,12 @@ public static function getOne(string $sql, ...$arg) * $data = $db->getRow("SELECT * FROM table WHERE id=?i", $id); * * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return array|FALSE either associative array contains first row of result set or FALSE if none found + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @return array|bool either associative array contains first row of result set or FALSE if none found */ public static function getRow(string $sql, ...$arg) { - $query = self::prepareQuery(func_get_args()); - if ($res = self::rawQuery($query)) { - $num_rows = self::num_rows($res); - if (self::$fail_on_nodata && !$num_rows) { - self::ShowError('Query result empty'); - } - if (!$num_rows) { - return false; - } - $ret = self::fetch($res); - self::free($res); - return $ret; - } - return false; + return self::instance()->getRow($sql, ...$arg); } /** @@ -300,22 +169,12 @@ public static function getRow(string $sql, ...$arg) * $ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s", $tag); * * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query * @return array enumerated array of first fields of all rows of resultset or empty array if none found */ public static function getCol(string $sql, ...$arg): array { - $ret = array(); - $query = self::prepareQuery(func_get_args()); - if ( $res = self::rawQuery($query) ) - { - while($row = self::fetch($res)) - { - $ret[] = reset($row); - } - self::free($res); - } - return $ret; + return self::instance()->getCol($sql, ...$arg); } /** @@ -326,22 +185,12 @@ public static function getCol(string $sql, ...$arg): array * $data = $db->getAll("SELECT * FROM table LIMIT ?i,?i", $start, $rows); * * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query * @return array enumerated 2d array contains the resultset. Empty if no rows found. */ public static function getAll(string $sql, ...$arg): array { - $ret = array(); - $query = self::prepareQuery(func_get_args()); - if ( $res = self::rawQuery($query) ) - { - while($row = self::fetch($res)) - { - $ret[] = $row; - } - self::free($res); - } - return $ret; + return self::instance()->getAll($sql, ...$arg); } /** @@ -353,25 +202,12 @@ public static function getAll(string $sql, ...$arg): array * * @param string $ind - name of the field which value is used to index resulting array * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query * @return array - associative 2d array contains the resultset. Empty if no rows found. */ public static function getInd(string $ind, string $sql, ...$arg): array { - $args = func_get_args(); - $index = array_shift($args); - $query = self::prepareQuery($args); - - $ret = array(); - if ( $res = self::rawQuery($query) ) - { - while($row = self::fetch($res)) - { - $ret[$row[$index]] = $row; - } - self::free($res); - } - return $ret; + return self::instance()->getInd($ind, $sql, ...$arg); } /** @@ -382,27 +218,12 @@ public static function getInd(string $ind, string $sql, ...$arg): array * * @param string $ind - name of the field which value is used to index resulting array * @param string $sql - an SQL query with placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the query + * @param mixed $arg,... unlimited number of arguments to match placeholders in the query * @return array - associative array contains key=value pairs out of resultset. Empty if no rows found. */ public static function getIndCol(string $ind, string $sql, ...$arg): array { - $args = func_get_args(); - $index = array_shift($args); - $query = self::prepareQuery($args); - - $ret = array(); - if ( $res = self::rawQuery($query) ) - { - while($row = self::fetch($res)) - { - $key = $row[$index]; - unset($row[$index]); - $ret[$key] = reset($row); - } - self::free($res); - } - return $ret; + return self::instance()->getIndCol($ind, $sql, ...$arg); } /** @@ -424,12 +245,12 @@ public static function getIndCol(string $ind, string $sql, ...$arg): array * $data = $db->getAll("SELECT * FROM table WHERE bar=?s ?p", $bar, $qpart); * * @param string $sql - whatever expression contains placeholders - * @param mixed $arg,... unlimited number of arguments to match placeholders in the expression + * @param mixed $arg,... unlimited number of arguments to match placeholders in the expression * @return string - initial expression with placeholders substituted with data. */ public static function parse(string $sql, ...$arg): string { - return self::prepareQuery(func_get_args()); + return self::instance()->parse($sql, ...$arg); } /** @@ -447,15 +268,14 @@ public static function parse(string $sql, ...$arg): string * $sql = "SELECT * FROM table ORDER BY ?p ?p LIMIT ?i,?i" * $data = $db->getArr($sql, $order, $dir, $start, $per_page); * - * @param string $input - field name to test - * @param array $allowed - an array with allowed variants - * @param string $default - optional variable to set if no match found. Default to false. - * @return string|FALSE - either sanitized value or FALSE + * @param string $input - field name to test + * @param array $allowed - an array with allowed variants + * @param string $default - optional variable to set if no match found. Default to false. + * @return string|bool - either sanitized value or FALSE */ public static function whiteList(string $input, array $allowed, $default=false) { - $found = array_search($input, $allowed, false); - return ($found === false) ? $default : $allowed[$found]; + return self::instance()->whiteList($input, $allowed, $default); } /** @@ -470,20 +290,13 @@ public static function whiteList(string $input, array $allowed, $default=false) * $sql = "INSERT INTO ?n SET ?u"; * $db->query($sql,$table,$data); * - * @param array $input - source array - * @param array $allowed - an array with allowed field names + * @param array $input - source array + * @param array $allowed - an array with allowed field names * @return array filtered out source array */ public static function filterArray(array $input, array $allowed): array { - foreach(array_keys($input) as $key ) - { - if ( !in_array($key, $allowed, false) ) - { - unset($input[$key]); - } - } - return $input; + return self::instance()->filterArray($input, $allowed); } /** @@ -493,8 +306,7 @@ public static function filterArray(array $input, array $allowed): array */ public static function lastQuery() { - $last = end(self::$stats); - return $last['query']; + return self::instance()->lastQuery(); } /** @@ -504,273 +316,97 @@ public static function lastQuery() */ public static function getStats(): array { - return self::$stats; - } - - /** - * protected function which actually runs a query against Mysql server. - * also logs some stats like profiling info and error message - * - * @param string $query - a regular SQL query - * @return bool|mysqli_result resource or FALSE on error - */ - protected static function rawQuery(string $query) - { - self::ConnectBase(); - self::$last_query_time[self::$current_instance] = time(); - if (self::$log_sql) { - self::log_sql($query); - } - $start = microtime(TRUE); - $res = mysqli_query(self::instance(), $query); - $timer = microtime(TRUE) - $start; - - self::$stats[] = array( - 'query' => $query, - 'start' => $start, - 'timer' => $timer, - 'rows' => 0 - ); - end(self::$stats); - $key = key(self::$stats); - if (!$res) - { - $error = mysqli_error(self::instance()); - self::$stats[$key]['error'] = $error; - self::cutStats(); - - self::log_error($query); - self::ShowError(); - } else - { - self::$stats[$key]['rows'] = self::instance()->affected_rows; - } - self::cutStats(); - return $res; - } - - public static function prepareQuery($args):string - { - self::ConnectBase(); - $query = ''; - $raw = array_shift($args); - $array = preg_split('~(\?[nsiuap])~u',$raw,null,PREG_SPLIT_DELIM_CAPTURE); - $arguments_num = count($args); - $placeholders_num = (int)floor(count($array) / 2); - if ( $placeholders_num !== $arguments_num ) - { - self::ShowError("Number of args ($arguments_num) doesn't match number of placeholders ($placeholders_num) in [$raw]"); - die(); - } - - foreach ($array as $i => $part) - { - if ( ($i % 2) === 0 ) - { - $query .= $part; - continue; - } - - $value = array_shift($args); - switch ($part) - { - case '?n': - $part = self::escapeIdent($value); - break; - case '?s': - $part = self::escapeString($value); - break; - case '?i': - $part = self::escapeInt($value); - break; - case '?a': - $part = self::createIN($value); - break; - case '?u': - $part = self::createSET($value); - break; - case '?p': - $part = $value; - break; - } - $query .= $part; - } - return $query; - } - - protected static function escapeInt($value) - { - if ($value === NULL) - { - return 'NULL'; - } - if(!is_numeric($value)) - { - self::ShowError( "Integer (?i) placeholder expects numeric value, ".gettype($value)." given"); - die(); - } - if (is_float($value)) - { - $value = number_format($value, 0, '.', ''); // may lose precision on big numbers - } - return $value; + return self::instance()->getStats(); } public static function escapeString($value):string { - if ($value === NULL) - { - return 'NULL'; - } - self::ConnectBase(); - return "'".mysqli_real_escape_string(self::instance(),$value)."'"; - } - - protected static function escapeIdent($value): string - { - if ($value) - { - return '`' .str_replace('`', '``',$value). '`'; - } - self::ShowError('Empty value for identifier (?n) placeholder'); - return false; - } - - protected static function createIN($data):string - { - if (!is_array($data)) - { - self::ShowError('Value for IN (?a) placeholder should be array'); - } - if (!$data) - { - return 'NULL'; - } - $query = $comma = ''; - foreach ($data as $value) - { - $query .= $comma.self::escapeString($value); - $comma = ','; - } - return $query; + return self::instance()->escapeString($value); } - protected static function createSET($data):string + public static function GetDate(): string { - if (!is_array($data)) - { - self::ShowError( 'SET (?u) placeholder expects array, ' .gettype($data). ' given'); - } - if (!$data) - { - self::ShowError('Empty array for SET (?u) placeholder'); - } - $query = $comma = ''; - foreach ($data as $key => $value) - { - if (is_array($value)) { - $str = $value[0]; - } else { - $str = self::escapeString($value); - } - $query .= $comma.self::escapeIdent($key).'='.$str; - $comma = ','; - } - return $query; + return date('Y-m-d H:i:s'); } /** - * On a long run we can eat up too much memory with mere statistics - * Let's keep it at reasonable size, leaving only last 100 entries. + * Insert data array into table + * + * Example: + * + * $data = [ + * 'date' => DB::pure('now()'), + * 'name' => 'Test', + * 'value' => 123 + * ]; + * $db->insert('users', $data); + * + * @param string $table_name + * @param array $data + * @param string $db_name + * @return bool|mysqli_result */ - protected static function cutStats() - { - if ( count(self::$stats) > 500 ) - { - reset(self::$stats); - $first = key(self::$stats); - unset(self::$stats[$first]); - } - } - - public static function GetError() - { - if (self::instance()->connect_errno > 0) { - return self::instance()->connect_error; - } - if (self::instance()->errno > 0) { - return self::instance()->error; - } - return false; - } - - public static function GetDate(): string + public static function insert(string $table_name, array $data, string $db_name = '') { - return date('Y-m-d H:i:s'); + return self::instance()->insert($table_name, $data, $db_name); } - private static function ParseArgs($args): string + /** + * Update data array in table + * + * Example: + * + * $data = [ + * 'date' => DB::pure('now()'), + * 'name' => 'Test', + * 'value' => 123 + * ]; + * $db->update('users', $data, DB::cond('id', '>', 3)); + * + * @param string $table_name + * @param array $data + * @param string $where + * @param string $db_name + * @return bool|mysqli_result + */ + public static function update(string $table_name, array $data, string $where = '', string $db_name = '') { - $res = array(); - foreach ($args as $i => $val) - { - if (is_array($val)) { - $v = $i.': '.self::ParseArgs($val); - } else { - $v = $i.': '.print_r($val, true); - } - $res[] = $v; - } - return '{'.implode(',',$res).'}'; + return self::instance()->update($table_name, $data, $where, $db_name); } - private static function GetBacktraceInfo($max_id = 2, $skip_id = 2): string + /** + * This function used on data arrays to provide clean sql string without escaping as string + * + * @param string $sql + * @return stdClass + */ + public static function pure(string $sql): stdClass { - $bt = debug_backtrace(); - $l = ''; - foreach ($bt as $i => $val) - { - if ($i < $skip_id) { - continue; - } - if ($i > $max_id) { - break; - } - if (isset($val['file'])) { - $l .= sprintf('File: %s, Line: %s, Function: %s, Args: %s', $val['file'], $val['line'], $val['function'], self::ParseArgs($val['args'])) . "\n"; - } - } - return $l; + $res = new stdClass(); + $res->sql = $sql; + return $res; } - private static function log_sql($sql) + /** + * DONT'T USE - function in development + * Make condition for where/having and other parameters + * + * Example: + * + * $where = [ + * DB::cond('id', '>', 3), + * DB::cond('name', 'LIKE', 'Alex%') + * ]; + * + * @param string $field + * @param string $operator + * @param $value + * @return string + */ + public static function cond(string $field, string $operator, $value): string { - $dh = fopen (self::$log_sql, 'ab+'); - if ($dh) - { - fwrite($dh, self::GetDate().' '.$sql."\n"); - if (self::$log_sql_debug) - { - fwrite($dh, self::GetBacktraceInfo(2)); - fwrite($dh, "\n"); - } - fclose($dh); - } + $placeholder = is_numeric($value) ? '?i' : '?s'; + return self::instance()->parse('?n '.$operator.' '.$placeholder, $field, $value); } - private static function log_error($sql) - { - $msg = self::GetDate().' '.self::GetError()."\nQuery: ".$sql."\n".self::GetBacktraceInfo(10)."\n"; - if (self::$error_log === false) - { - error_log($msg); - return; - } - $dh = fopen (self::$error_log, 'ab+'); - if ($dh) - { - fwrite($dh, $msg); - fclose($dh); - } - } } diff --git a/src/mDB.php b/src/mDB.php index caa4eb4..f04a839 100644 --- a/src/mDB.php +++ b/src/mDB.php @@ -10,7 +10,11 @@ /** * Class mDB * @package Power - * This class is created for non static code using + * This class is created for non-static code using + * @method begin_transaction ($flags = 0 , $name = 0 ) + * @method rollback ($flags = 0 , $name = 0 ) + * @method commit ($flags = 0 , $name = 0 ) + * @method prepare (string $query) */ class mDB { @@ -49,6 +53,18 @@ class mDB */ private $error_log = false; + /** + * Call standard methods from mysqli instance + * + * @param string $name + * @param array $arguments + * @return false|mixed + */ + public function __call(string $name, array $arguments) + { + $this->ConnectBase(); + return call_user_func_array(array($this->getInstance(), $name), $arguments); + } public function SetLogSql(string $file_name, bool $add_debug = false): mDB { @@ -161,7 +177,7 @@ public function ShowError($msg = false ) * * @param string $sql - an SQL query with placeholders * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return mysqli_result whatever mysqli_query returns + * @return bool|mysqli_result whatever mysqli_query returns */ public function query(string $sql, ...$arg) { @@ -173,7 +189,7 @@ public function query(string $sql, ...$arg) * * @param bool|mysqli_result $result - mysqli result * @param int $mode - optional fetch mode - * @return array|false whatever mysqli_fetch_array returns + * @return array|bool whatever mysqli_fetch_array returns */ public function fetch($result, int $mode = MYSQLI_ASSOC) { @@ -219,7 +235,7 @@ public function free($result) * * @param string $sql - an SQL query with placeholders * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return string|FALSE either first column of the first row of resultset or FALSE if none found + * @return string|bool either first column of the first row of resultset or FALSE if none found */ public function getOne(string $sql, ...$arg) { @@ -251,7 +267,7 @@ public function getOne(string $sql, ...$arg) * * @param string $sql - an SQL query with placeholders * @param mixed $arg,... unlimited number of arguments to match placeholders in the query - * @return array|FALSE either associative array contains first row of resultset or FALSE if none found + * @return array|bool either associative array contains first row of resultset or FALSE if none found */ public function getRow(string $sql, ...$arg) { @@ -429,7 +445,7 @@ public function parse(string $sql, ...$arg): string * @param string $input - field name to test * @param array $allowed - an array with allowed variants * @param string $default - optional variable to set if no match found. Default to false. - * @return string|FALSE - either sanitized value or FALSE + * @return string|bool - either sanitized value or FALSE */ public function whiteList(string $input, array $allowed, $default=false) { @@ -437,9 +453,62 @@ public function whiteList(string $input, array $allowed, $default=false) return ($found === false) ? $default : $allowed[$found]; } + /** + * Insert data array into table + * + * Example: + * + * $data = [ + * 'date' => DB::pure('now()'), + * 'name' => 'Test', + * 'value' => 123 + * ]; + * $db->insert('users', $data); + * + * @param string $table_name + * @param array $data + * @param string $db_name + * @return bool|mysqli_result + */ + public function insert(string $table_name, array $data, string $db_name = '') + { + if ($db_name !== '') + { + return $this->query('INSERT INTO ?n.?n SET ?u', $db_name, $table_name, $data); + } + return $this->query('INSERT INTO ?n SET ?u', $table_name, $data); + } + + /** + * Update data array in table + * + * Example: + * + * $data = [ + * 'date' => DB::pure('now()'), + * 'name' => 'Test', + * 'value' => 123 + * ]; + * $db->update('users', $data, 'id>5'); + * + * @param string $table_name + * @param array $data + * @param string $where + * @param string $db_name + * @return bool|mysqli_result + */ + public function update(string $table_name, array $data, string $where = '', string $db_name = '') + { + if ($db_name !== '') + { + return $this->query('UPDATE ?n.?n SET ?u'.($where !== '' ? ' WHERE '.$where : ''), $db_name, $table_name, $data); + } + return $this->query('UPDATE ?n SET ?u'.($where !== '' ? ' WHERE '.$where : ''), $table_name, $data); + } + /** * function to filter out arrays, for the whitelisting purposes - * useful to pass entire superglobal to the INSERT or UPDATE query + * useful to pass entire super global to the INSERT or UPDATE query * OUGHT to be used for this purpose, * as there could be fields to which user should have no access to. * @@ -647,8 +716,9 @@ protected function createSET($data):string $query = $comma = ''; foreach ($data as $key => $value) { - if (is_array($value)) { - $str = $value[0]; + // Check if value is DB::pure object + if (is_object($value) && isset($value->sql)) { + $str = $value->sql; } else { $str = $this->escapeString($value); } @@ -664,7 +734,7 @@ protected function createSET($data):string */ protected function cutStats() { - if ( count($this->stats) > 500 ) + if ( count($this->stats) > 100 ) { reset($this->stats); $first = key($this->stats); @@ -707,13 +777,13 @@ private static function ParseArgs($args): string return '{'.implode(',',$res).'}'; } - private static function GetBacktraceInfo($max_id = 2, $skip_id = 2): string + private static function GetBacktraceInfo($max_id = 2): string { $bt = debug_backtrace(); $l = ''; foreach ($bt as $i => $val) { - if ($i < $skip_id) { + if ($i < 2) { continue; } if ($i > $max_id) { @@ -734,7 +804,7 @@ private function log_sql($sql) fwrite($dh, self::GetDate().' '.$sql."\n"); if ($this->log_sql_debug) { - fwrite($dh, self::GetBacktraceInfo(2)); + fwrite($dh, self::GetBacktraceInfo()); fwrite($dh, "\n"); } fclose($dh);