Browse Source

We now can work with prepared statements

tags/3.5.2
Michael 3 years ago
parent
commit
63b0b05445
2 changed files with 226 additions and 15 deletions
  1. +221
    -15
      include/dba.php
  2. +5
    -0
      include/dbm.php

+ 221
- 15
include/dba.php View File

@@ -15,12 +15,15 @@ require_once('include/datetime.php');
class dba {

private $debug = 0;
private $db;
//private $db;
public $db;
private $result;
private $driver;
//private $driver;
public $driver;
public $connected = false;
public $error = false;
private $_server_info = '';
private static $dbo;

function __construct($server, $user, $pass, $db, $install = false) {
$a = get_app();
@@ -93,6 +96,8 @@ class dba {
}
}
$a->save_timestamp($stamp1, "network");

self::$dbo = $this;
}

/**
@@ -462,6 +467,26 @@ class dba {
return $id;
}

/**
* @brief Replaces ANY_VALUE() function by MIN() function,
* if the database server does not support ANY_VALUE().
*
* Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
* ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
* A standard fall-back is to use MIN().
*
* @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary.
*/
public function any_value_fallback($sql) {
$server_info = $this->server_info();
if (version_compare($server_info, '5.7.5', '<') ||
(stripos($server_info, 'MariaDB') !== false)) {
$sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
}
return $sql;
}

function __destruct() {
if ($this->db) {
switch ($this->driver) {
@@ -479,23 +504,204 @@ class dba {
}

/**
* @brief Replaces ANY_VALUE() function by MIN() function,
* if the database server does not support ANY_VALUE().
* @brief Executes a prepared statement
*
* Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5).
* ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html
* A standard fall-back is to use MIN().
* @param string $sql SQL statement
* @return object statement object
*/
static public function p($sql) {
$a = get_app();

$stamp1 = microtime(true);

$args = func_get_args();
unset($args[0]);

if (!self::$dbo OR !self::$dbo->connected) {
return false;
}

$sql = self::$dbo->any_value_fallback($sql);

$orig_sql = $sql;

if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
$sql = "/*".$a->callstack()." */ ".$sql;
}

switch (self::$dbo->driver) {
case 'pdo':
$stmt = self::$dbo->db->prepare($sql);

foreach ($args AS $param => $value) {
$stmt->bindParam($param, $args[$param]);
}

$success = $stmt->execute();

if ($success) {
$retval = $stmt;
} else {
$retval = false;
}

$errorInfo = self::$dbo->db->errorInfo();

if ($errorInfo) {
self::$dbo->error = $errorInfo[2];
self::$dbo->errorno = $errorInfo[1];
}

break;
case 'mysqli':
$stmt = self::$dbo->db->stmt_init();

if (!$stmt->prepare($sql)) {
self::$dbo->error = self::$dbo->db->error;
self::$dbo->errorno = self::$dbo->db->errno;
$retval = false;
break;
}

$params = '';
$values = array();
foreach ($args AS $param => $value) {
if (is_int($args[$param])) {
$params .= 'i';
} elseif (is_float($args[$param])) {
$params .= 'd';
} elseif (is_string($args[$param])) {
$params .= 's';
} else {
$params .= 'b';
}
$values[] = &$args[$param];
}

array_unshift($values, $params);

call_user_func_array(array($stmt, 'bind_param'), $values);

if (!$stmt->execute()) {
self::$dbo->error = self::$dbo->db->error;
self::$dbo->errorno = self::$dbo->db->errno;
$retval = false;
} elseif (method_exists($stmt, 'get_result')) {
// Is mysqlnd installed?
$retval = $stmt->get_result();
} else {
$retval = $stmt;
}
break;
case 'mysql':
// For the old "mysql" functions we cannot use prepared statements
foreach ($args AS $param => $value) {
if (is_int($args[$param]) OR is_float($args[$param])) {
$replace = intval($args[$param]);
} else {
$replace = "'".dbesc($args[$param])."'";
}

$pos = strpos($sql, '?');
if ($pos !== false) {
$sql = substr_replace($sql, $replace, $pos, 1);
}
}

$retval = mysql_query($sql, self::$dbo->db);
if (mysql_errno(self::$dbo->db)) {
self::$dbo->error = mysql_error(self::$dbo->db);
self::$dbo->errorno = mysql_errno(self::$dbo->db);
}
break;
}

$stamp2 = microtime(true);
$duration = (float)($stamp2 - $stamp1);

$a->save_timestamp($stamp1, 'database');

if (strtolower(substr($orig_sql, 0, 6)) != "select") {
$a->save_timestamp($stamp1, "database_write");
}

return $retval;
}

/**
* @brief Executes a prepared statement
*
* @param string $sql An SQL string without the values
* @return string The input SQL string modified if necessary.
* @param string $sql SQL statement
* @return boolean Was the query successfull?
*/
public function any_value_fallback($sql) {
$server_info = $this->server_info();
if (version_compare($server_info, '5.7.5', '<') ||
(stripos($server_info, 'MariaDB') !== false)) {
$sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql);
static public function e($sql) {
}

/**
* @brief Fetch a single row
*
* @param object $stmt statement object
* @return array current row
*/
static public function fetch($stmt) {
switch (self::$dbo->driver) {
case 'pdo':
return $stmt->fetch(PDO::FETCH_ASSOC);
case 'mysqli':
// When mysqlnd is installed, we can use a shortcut
if (method_exists($stmt, 'fetch_array')) {
return $stmt->fetch_array(MYSQLI_ASSOC);
}

// This code works, but is slow

// Bind the result to a result array
$cols = array();

$cols_num = array();
for ($x = 0; $x < $stmt->field_count; $x++) {
$cols[] = &$cols_num[$x];
}

call_user_func_array(array($stmt, 'bind_result'), $cols);

$success = $stmt->fetch();

if (!$success) {
return false;
}

// The slow part:
// We need to get the field names for the array keys
// It seems that there is no better way to do this.
$result = $stmt->result_metadata();

$columns = array();
foreach ($cols_num AS $col) {
$field = $result->fetch_field();
$columns[$field->name] = $col;
}
return $columns;
case 'mysql':
return mysql_fetch_array(self::$dbo->result, MYSQL_ASSOC);
}
}

/**
* @brief Closes the current statement
*
* @param object $stmt statement object
* @return boolean was the close successfull?
*/
static public function close($stmt) {
switch (self::$dbo->driver) {
case 'pdo':
return $stmt->closeCursor();
case 'mysqli':
return $stmt->close();
case 'mysql':
return mysql_free_result($stmt);
}
return $sql;
}
}



+ 5
- 0
include/dbm.php View File

@@ -47,6 +47,11 @@ class dbm {
if (is_bool($array)) {
return $array;
}

if (is_object($array)) {
return true;
}

return (is_array($array) && count($array) > 0);
}



Loading…
Cancel
Save