407 lines
9.0 KiB
PHP
407 lines
9.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Vvveb
|
|
*
|
|
* Copyright (C) 2022 Ziadin Givan
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
namespace Vvveb\System\Db;
|
|
|
|
use Vvveb\System\Event;
|
|
|
|
/*
|
|
Define and use mysqli_result if native is missing
|
|
*/
|
|
class mysqli_result {
|
|
private $stmt;
|
|
|
|
private $meta;
|
|
|
|
public function __construct($stmt) {
|
|
$this->stmt = $stmt;
|
|
}
|
|
|
|
public function fetch_all() {
|
|
return $this->fetch_assoc();
|
|
}
|
|
|
|
public function fetch_array($resulttype) {
|
|
return $this->fetch_assoc();
|
|
}
|
|
|
|
public function fetch_assoc() {
|
|
//$meta = $this->stmt->store_result();
|
|
$meta = $this->stmt->result_metadata();
|
|
$row = [];
|
|
|
|
while ($field = $meta->fetch_field()) {
|
|
$params[] = &$row[$field->name];
|
|
}
|
|
|
|
call_user_func_array([$this->stmt, 'bind_result'], $params);
|
|
|
|
while ($this->stmt->fetch()) {
|
|
$c = [];
|
|
|
|
foreach ($row as $key => $val) {
|
|
$c[$key] = $val;
|
|
}
|
|
$result[] = $c;
|
|
}
|
|
|
|
//$this->stmt->free_result();
|
|
//$this->stmt->close();
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function fetch_field() {
|
|
}
|
|
|
|
public function fetch_fields() {
|
|
}
|
|
|
|
public function fetchRow() {
|
|
return $this->fetch_assoc();
|
|
}
|
|
}
|
|
|
|
class Mysqli extends DBDriver {
|
|
private static $link = null;
|
|
|
|
//public $error;
|
|
|
|
private $stmt;
|
|
|
|
public $affected_rows = 0;
|
|
|
|
public $insert_id = null;
|
|
|
|
public $quote = '`';
|
|
|
|
public $prefix = ''; //'vv_';
|
|
|
|
public static function version() {
|
|
if (self :: $link) {
|
|
return mysqli_get_server_version(self :: $link);
|
|
}
|
|
}
|
|
|
|
public static function info() {
|
|
if (self :: $link) {
|
|
return mysqli_get_server_info(self :: $link);
|
|
}
|
|
}
|
|
|
|
public function error() {
|
|
if (self :: $link) {
|
|
return self :: $link->error;
|
|
}
|
|
}
|
|
|
|
public function errorCode() {
|
|
if (self :: $link) {
|
|
return self :: $link->errno;
|
|
}
|
|
}
|
|
|
|
public function get_result($stmt) {
|
|
$result = new mysqli_result($stmt);
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function __construct($host = DB_HOST, $dbname = DB_NAME, $user = DB_USER, $pass = DB_PASS, $port = DB_PORT, $prefix = DB_PREFIX) {
|
|
//return $this->connect($host, $dbname, $user, $pass, $port, $prefix);
|
|
}
|
|
|
|
public function connect($host = DB_HOST, $dbname = DB_NAME, $user = DB_USER, $pass = DB_PASS, $port = DB_PORT, $prefix = DB_PREFIX) {
|
|
//mysqli_report(MYSQLI_REPORT_OFF);
|
|
//connect to database
|
|
if (self :: $link) {
|
|
return self :: $link;
|
|
}
|
|
$this->prefix = $prefix;
|
|
|
|
\mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
|
|
|
try {
|
|
self :: $link = new \Mysqli(/*'p:' . */$host, $user, $pass, $dbname, $port);
|
|
//self :: $link = $this;
|
|
} catch (\mysqli_sql_exception $e) {
|
|
$errorMessage = str_replace($pass,'*****', $e->getMessage());
|
|
|
|
throw new \Exception($errorMessage, $e->getCode());
|
|
}
|
|
|
|
// check if a connection established
|
|
if (\mysqli_connect_errno()) {
|
|
$error = mysqli_connect_error();
|
|
$errorMessage = str_replace($pass,'*****', $error);
|
|
|
|
throw new \Exception($errorMessage, mysqli_connect_errno());
|
|
}
|
|
|
|
if ((! self :: $link instanceof \MySQLi)) {
|
|
throw new \Exception('Mysqli not an object', mysqli_connect_errno());
|
|
}
|
|
|
|
return self :: $link;
|
|
}
|
|
|
|
/*
|
|
* Get all columns for a table used for sanitizing input
|
|
*/
|
|
function getColumnsMeta($tableName, $comment = false) {
|
|
$sql =
|
|
'SELECT COLUMN_NAME as name, COLUMN_DEFAULT as d, IS_NULLABLE as n, DATA_TYPE as t, EXTRA as e, CHARACTER_MAXIMUM_LENGTH as l'
|
|
. ($comment ? ', COLUMN_COMMENT as c' : '') .
|
|
' FROM `INFORMATION_SCHEMA`.`COLUMNS`
|
|
WHERE `TABLE_SCHEMA`= "' . DB_NAME . '"
|
|
AND `TABLE_NAME`="' . $tableName . '"';
|
|
|
|
if ($result = $this->query($sql)) {
|
|
//$columns = $result->fetch_all(MYSQLI_ASSOC);
|
|
$columns = [];
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
$columns[$row['name']] = $row;
|
|
}
|
|
|
|
/* free result set */
|
|
$result->close();
|
|
|
|
return $columns;
|
|
} else {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function getTableNames($db = DB_NAME) {
|
|
$sql ="SELECT table_name as name
|
|
FROM information_schema.tables
|
|
WHERE table_schema = '$db' ORDER BY table_name";
|
|
|
|
if ($result = $this->query($sql)) {
|
|
//$names = $result->fetch_all(MYSQLI_ASSOC);
|
|
$names = [];
|
|
|
|
while ($row = $result->fetch_assoc()) {
|
|
$names[] = $row['name'];
|
|
}
|
|
|
|
/* free result set */
|
|
$result->close();
|
|
|
|
return $names;
|
|
} else {
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function select_db($db) {
|
|
return self :: $link->select_db($db);
|
|
}
|
|
|
|
public function query($sql) {
|
|
if (! self :: $link) {
|
|
$this->connect();
|
|
}
|
|
|
|
if (LOG_SQL_QUERIES) {
|
|
error_log($sql);
|
|
}
|
|
|
|
try {
|
|
$result = self :: $link->query($sql);
|
|
} catch (\mysqli_sql_exception $e) {
|
|
$message = $e->getMessage() . "\n" . $sql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message, $e->getCode());
|
|
}
|
|
|
|
if ($result) {
|
|
$this->affected_rows = self :: $link->affected_rows;
|
|
$this->insert_id = self :: $link->insert_id;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function multi_query($sql) {
|
|
if (! self :: $link) {
|
|
$this->connect();
|
|
}
|
|
|
|
$result = self :: $link->multi_query($sql);
|
|
|
|
if ($result) {
|
|
$this->affected_rows = self :: $link->affected_rows;
|
|
$this->insert_id = self :: $link->insert_id;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function escape($string) {
|
|
if (is_string($string)) {
|
|
return self :: $link->real_escape_string($string);
|
|
}
|
|
|
|
if (is_null($string)) {
|
|
return 'null';
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
public function escapeLiteral($string) {
|
|
return $this->escape($string);
|
|
}
|
|
|
|
public function sqlLimit($start, $limit) {
|
|
return "LIMIT $start, $limit";
|
|
}
|
|
|
|
public function fetchArray($stmt) {
|
|
$data = [];
|
|
$statement = (get_class($stmt) === 'mysqli_stmt');
|
|
|
|
if ($statement) {
|
|
$this->store_result();
|
|
$result = $stmt->get_result();
|
|
} else {
|
|
$result = $stmt;
|
|
}
|
|
|
|
if ($result) {
|
|
$data = $result->fetch_array(MYSQLI_ASSOC);
|
|
|
|
if ($statement && $stmt->more_results()) {
|
|
$stmt->next_result();
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function fetchAll($stmt) {
|
|
$data = [];
|
|
$statement = (get_class($stmt) === 'mysqli_stmt');
|
|
|
|
if ($statement) {
|
|
$this->store_result();
|
|
$result = $stmt->get_result();
|
|
} else {
|
|
$result = $stmt;
|
|
}
|
|
|
|
if ($result) {
|
|
$data = $result->fetch_all(MYSQLI_ASSOC);
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function store_result() {
|
|
return self :: $link->store_result();
|
|
}
|
|
|
|
public function more_results() {
|
|
return self :: $link->more_results();
|
|
}
|
|
|
|
public function next_result() {
|
|
return self :: $link->next_result();
|
|
}
|
|
|
|
public function close() {
|
|
if ((self :: $link instanceof \MySQLi)/* && self :: $link->ping()*/) {
|
|
return self :: $link->close();
|
|
}
|
|
}
|
|
|
|
// Prepare
|
|
public function execute($sql, $params = [], $paramTypes = []) {
|
|
list($sql, $params) = Event::trigger(__CLASS__,__FUNCTION__, $sql, $params);
|
|
//save orig sql for debugging info
|
|
$origSql = $sql;
|
|
|
|
if (! self :: $link) {
|
|
$this->connect();
|
|
}
|
|
|
|
list($parameters, $types) = $this->paramsToQmark($sql, $params, $paramTypes);
|
|
|
|
try {
|
|
$stmt = self::$link->prepare($sql);
|
|
} catch (\mysqli_sql_exception $e) {
|
|
$message = $e->getMessage() . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n - " . $origSql . "\n - " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message, $e->getCode());
|
|
}
|
|
|
|
if ($stmt && ! empty($types)) {
|
|
array_unshift($parameters, $types);
|
|
|
|
//hack for php 7.x bind_param "expected to be a reference, value given" stupid warning
|
|
$referenceArray = [];
|
|
|
|
foreach ($parameters as $key => $value) {
|
|
$referenceArray[$key] = &$parameters[$key];
|
|
}
|
|
|
|
@call_user_func_array([$stmt, 'bind_param'], $referenceArray);
|
|
}
|
|
|
|
if (LOG_SQL_QUERIES) {
|
|
error_log($this->debugSql($origSql, $params, $paramTypes));
|
|
}
|
|
|
|
if ($stmt) {
|
|
try {
|
|
if ($stmt->execute()) {
|
|
$this->affected_rows = self :: $link->affected_rows;
|
|
$this->insert_id = self :: $link->insert_id;
|
|
|
|
return $stmt;
|
|
} else {
|
|
error_log(print_r($stmt, 1));
|
|
error_log($this->debugSql($sql, $params, $paramTypes));
|
|
}
|
|
} catch (\mysqli_sql_exception $e) {
|
|
$message = $e->getMessage() . "\n" . $origSql . "\n" . $this->debugSql($origSql, $params, $paramTypes) . "\n" . print_r($parameters, 1) . $types . "\n" . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1), true);
|
|
|
|
throw new \Exception($message, $e->getCode());
|
|
}
|
|
} else {
|
|
error_log(print_r($stmt, 1));
|
|
error_log($this->debugSql($origSql, $params, $paramTypes));
|
|
}
|
|
|
|
return $stmt;
|
|
}
|
|
|
|
// Bind
|
|
public function bind($param, $value, $type = null) {
|
|
$this->stmt->bindValue($param, $value, $type);
|
|
}
|
|
}
|