517 lines
12 KiB
PHP
517 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Vn\Db;
|
|
|
|
use Vn\Lib\Type;
|
|
|
|
class Connection {
|
|
private $conn = NULL;
|
|
private $isOpen = FALSE;
|
|
var $enableDebug = FALSE;
|
|
|
|
function __construct() {
|
|
$this->initHandler();
|
|
}
|
|
|
|
/**
|
|
* Opens a new connection to the database.
|
|
*
|
|
* @param string $host The host name
|
|
* @param string $user The user name to authenticate
|
|
* @param string $pass The user password
|
|
* @param string $name The default schema name
|
|
*
|
|
* @return boolean %TRUE on success, %FALSE otherwise
|
|
*/
|
|
function open($host, $user, $pass, $name, $port = NULL) {
|
|
$conn = $this->initHandler();
|
|
$conn->options(MYSQLI_OPT_LOCAL_INFILE, TRUE);
|
|
$conn->real_connect($host, $user, $pass, $name, $port);
|
|
|
|
if (mysqli_connect_errno()) {
|
|
sleep(3);
|
|
throw new Exception(mysqli_connect_errno(), mysqli_connect_error());
|
|
return FALSE;
|
|
}
|
|
|
|
$this->isOpen = TRUE;
|
|
$this->query('SET CHARACTER SET utf8');
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Closes the current connection, if it's closed does nothing.
|
|
*/
|
|
function close() {
|
|
if ($this->isOpen) {
|
|
$this->conn->close();
|
|
$this->conn = NULL;
|
|
}
|
|
|
|
$this->isOpen = FALSE;
|
|
}
|
|
|
|
/**
|
|
* Initializes the internal database connection handler.
|
|
*
|
|
* @return Objetct The connection handler
|
|
*/
|
|
function initHandler() {
|
|
if (!$this->conn)
|
|
$this->conn = new \mysqli();
|
|
|
|
return $this->conn;
|
|
}
|
|
|
|
/**
|
|
* Returns the internal database connection handler. This function shoud be
|
|
* used to set options for specific databases that could not be set using
|
|
* the Connection class methods.
|
|
*
|
|
* @return Objetct The connection handler
|
|
*/
|
|
function getHandler() {
|
|
return $this->conn;
|
|
}
|
|
|
|
/**
|
|
* Changes the default schema for the current connection.
|
|
*
|
|
* @param string $schema The schema name
|
|
* @return boolean %TRUE if success, %FALSE otherwise
|
|
*/
|
|
function selectDb($dbName) {
|
|
return $this->conn->select_db($dbName);
|
|
}
|
|
|
|
/**
|
|
* Executes the query and gets the result.
|
|
*
|
|
* @param string $query The SQL query
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function query($query, $params = NULL) {
|
|
$result = $this->conn->query($this->renderDebug($query, $params));
|
|
|
|
if (!$result)
|
|
$this->checkError();
|
|
else
|
|
while ($this->moreResults())
|
|
$this->nextResult();
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Checks whether the connection is open.
|
|
*
|
|
* @return boolean %TRUE if connection is open, %FALSE otherwise
|
|
*/
|
|
function isOpen() {
|
|
return $this->isOpen;
|
|
}
|
|
|
|
/**
|
|
* Gets the first row value of the first column from a result.
|
|
*
|
|
* @param resource $result The database result
|
|
*
|
|
* @return mixed[] An associative array with the first row, %NULL if error
|
|
*/
|
|
function getRowFromResult($result) {
|
|
if ($result) {
|
|
$row = $result->fetch_assoc();
|
|
$result->free();
|
|
return $row;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Gets the first row value of the first column from a result.
|
|
*
|
|
* @param resource $result The database result
|
|
*
|
|
* @return object An object with the first row, %NULL if error
|
|
*/
|
|
function getObjectFromResult($result) {
|
|
if ($result) {
|
|
$row = $result->fetch_object();
|
|
$result->free();
|
|
return $row;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Gets the first row value of the first column from a result.
|
|
*
|
|
* @param resource $result The database result
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function getValueFromResult($result) {
|
|
$value = NULL;
|
|
|
|
if ($result) {
|
|
$row = $result->fetch_row();
|
|
|
|
if ($row && count($row) > 0)
|
|
$value = $row[0];
|
|
|
|
$result->free();
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* Executes the query and gets it's first row.
|
|
*
|
|
* @param string $query The SQL query
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed[] An associative array with the first row, %NULL if error
|
|
*/
|
|
function getRow($query, $params = NULL) {
|
|
$result = $this->query($query, $params);
|
|
return $this->getRowFromResult($result);
|
|
}
|
|
|
|
/**
|
|
* Executes the query and gets it's first row.
|
|
*
|
|
* @param string $query The SQL query
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return object An object with the first row, %NULL if error
|
|
*/
|
|
function getObject($query, $params = NULL) {
|
|
$result = $this->query($query, $params);
|
|
return $this->getObjectFromResult($result);
|
|
}
|
|
|
|
/**
|
|
* Executes the query and gets the first value of the first row.
|
|
*
|
|
* @param string $query The SQL query
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function getValue($query, $params = NULL) {
|
|
$result = $this->query($query, $params);
|
|
return $this->getValueFromResult($result);
|
|
}
|
|
|
|
/**
|
|
* Loads a query from a file and renders it.
|
|
*
|
|
* @param string $file The file path
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The query string
|
|
*/
|
|
function loadFromFile($file, $params = NULL) {
|
|
$query = file_get_contents($file .'.sql');
|
|
|
|
if ($query === FALSE)
|
|
throw new Exception(NULL, 'Can not load query from file');
|
|
|
|
return $this->render($query, $params);
|
|
}
|
|
|
|
/**
|
|
* Executes the query stored in the specified file and gets the result.
|
|
*
|
|
* @param string $file The file path
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function queryFromFile($file, $params = NULL) {
|
|
$query = $this->loadFromFile($file, $params);
|
|
|
|
if ($query)
|
|
return $this->query($query);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Executes the file query and gets it's first row.
|
|
*
|
|
* @param string $file The file path
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed[] An associative array with the first row, %NULL if error
|
|
*/
|
|
function getRowFromFile($file, $params = NULL) {
|
|
$result = $this->queryFromFile($file, $params);
|
|
return $this->getRowFromResult($result);
|
|
}
|
|
|
|
/**
|
|
* Executes the file query and gets the first value of the first row.
|
|
*
|
|
* @param string $file The file path
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function getValueFromFile($file, $params = NULL) {
|
|
$result = $this->queryFromFile($file, $params);
|
|
return $this->getValueFromResult($result);
|
|
}
|
|
|
|
/**
|
|
* Execute multiple queries separated by semicolons.
|
|
*
|
|
* @param string $query The SQL query
|
|
* @param mixed[] $params The query parameters
|
|
*
|
|
* @return mixed The value or %NULL if error
|
|
*/
|
|
function multiQuery($query, $params = NULL) {
|
|
try {
|
|
$success = $this->conn->multi_query($this->renderDebug($query, $params));
|
|
} catch (\Exception $err) {
|
|
$this->catchError($err);
|
|
}
|
|
if (!$success) $this->checkError();
|
|
return $success;
|
|
}
|
|
|
|
/**
|
|
* Prepare an SQL statement for execution.
|
|
*
|
|
* @param string $query The SQL query
|
|
*
|
|
* @return mixed The statement object or %FALSE if an error occurred
|
|
*/
|
|
function prepare($query) {
|
|
return $this->conn->prepare($query);
|
|
}
|
|
|
|
function storeResult() {
|
|
try {
|
|
$result = $this->conn->store_result();
|
|
} catch (\Exception $err) {
|
|
$this->catchError($err);
|
|
}
|
|
if (!$result) $this->checkError();
|
|
return $result;
|
|
}
|
|
|
|
function moreResults() {
|
|
return $this->conn->more_results();
|
|
}
|
|
|
|
function nextResult() {
|
|
try {
|
|
$hasNext = $this->conn->next_result();
|
|
} catch (\Exception $err) {
|
|
$this->catchError($err);
|
|
}
|
|
$this->checkError();
|
|
return $hasNext;
|
|
}
|
|
|
|
function catchError($err) {
|
|
throw new Exception($err->getCode(), $err->getMessage());
|
|
}
|
|
|
|
/**
|
|
* Check if there has been an error in the last query or multiquery,
|
|
* throwing a @Exception exception if any.
|
|
*/
|
|
function checkError() {
|
|
if ($this->conn->errno)
|
|
throw new Exception($this->conn->errno, $this->conn->error);
|
|
}
|
|
|
|
/**
|
|
* Check if there have been warnings in the last query.
|
|
*
|
|
* @return boolean %TRUE if there have been warnings %FALSE otherwise
|
|
*/
|
|
function checkWarnings() {
|
|
return $this->conn->warning_count > 0;
|
|
}
|
|
|
|
/**
|
|
* Renders an SQL string using the given parameters.
|
|
*
|
|
* @param string $query The SQL string
|
|
* @param mixed[] $paramsMap The query parameters
|
|
*
|
|
* @return string The rendered SQL string
|
|
*/
|
|
function render($query, $paramsMap = NULL) {
|
|
if (isset($paramsMap) && is_array($paramsMap) && count($paramsMap) > 0) {
|
|
$i = 0;
|
|
$params = [];
|
|
|
|
foreach ($paramsMap as $key => $value)
|
|
$params[$key] = $this->renderValue($value);
|
|
|
|
$replaceFunc = function($matches) use(&$params, &$i) {
|
|
$key = substr($matches[0], 1);
|
|
|
|
if (strlen($key) == 0)
|
|
$key = $i++;
|
|
if (isset($params[$key]))
|
|
return $params[$key];
|
|
|
|
return '#'. $key;
|
|
};
|
|
|
|
return preg_replace_callback('/#\w*/', $replaceFunc, $query);
|
|
}
|
|
else
|
|
return $query;
|
|
}
|
|
|
|
/**
|
|
* Gets an SQL respresentation from a PHP value.
|
|
*
|
|
* @param mixed $value The value
|
|
*
|
|
* @return string The SQL value
|
|
*/
|
|
function renderValue($value) {
|
|
if ($value !== NULL)
|
|
switch (Type::get($value)) {
|
|
case Type::BOOLEAN:
|
|
return($value) ? 'TRUE' : 'FALSE';
|
|
case Type::STRING:
|
|
return '\'' . $this->escapeString($value) . '\'';
|
|
case Type::DATE:
|
|
return strftime('\'%Y-%m-%d\'', $value->getTimestamp());
|
|
case Type::TIME:
|
|
return strftime('\'%T\'', $value->getTimestamp());
|
|
case Type::DATE_TIME:
|
|
return strftime('\'%Y-%m-%d %T\'', $value->getTimestamp());
|
|
default:
|
|
return '\'' . $this->escapeString($value) . '\'';
|
|
}
|
|
else
|
|
return 'NULL';
|
|
}
|
|
|
|
/**
|
|
* Escapes an string, escaping special characters when necessary.
|
|
*
|
|
* @param string $string The string
|
|
* @return string The escaped string
|
|
*/
|
|
function escapeString($string) {
|
|
return $this->conn->real_escape_string($string);
|
|
}
|
|
|
|
/**
|
|
* Quotes an identifier, escaping special characters when necessary.
|
|
*
|
|
* @param string $identifier The identifier without quotes
|
|
* @return string The quoted identifier
|
|
*/
|
|
function quote($identifier) {
|
|
return "`". str_replace("`", "``", $identifier) ."`";
|
|
}
|
|
|
|
/**
|
|
* Renders an SQL string using the given parameters, also debugs the
|
|
* rendered string if $enableDebug property is set to %true.
|
|
*
|
|
* @param string $query The SQL string
|
|
* @param mixed[] $paramsMap The query parameters
|
|
*
|
|
* @return string The rendered SQL string
|
|
*/
|
|
function renderDebug($query, $params = NULL) {
|
|
$renderedQuery = $this->render($query, $params);
|
|
|
|
if ($this->enableDebug)
|
|
error_log($renderedQuery);
|
|
|
|
return $renderedQuery;
|
|
}
|
|
|
|
function commit() {
|
|
return $this->query('COMMIT');
|
|
}
|
|
|
|
function rollback() {
|
|
return $this->query('ROLLBACK');
|
|
}
|
|
|
|
function startTransaction() {
|
|
return $this->query('START TRANSACTION');
|
|
}
|
|
|
|
function lastInsertId() {
|
|
return $this->getValue('SELECT LAST_INSERT_ID()');
|
|
}
|
|
|
|
function call($procName, $values) {
|
|
return $this->query(
|
|
"CALL {$this->quote($procName)}({$this->renderList($values)})"
|
|
);
|
|
}
|
|
|
|
function insert($table, $values) {
|
|
return $this->query("INSERT INTO {$this->quote($table)}
|
|
SET {$this->renderFields($values)}");
|
|
}
|
|
|
|
function update($table, $values, $where) {
|
|
return $this->query("UPDATE {$this->quote($table)}
|
|
SET {$this->renderFields($values)}
|
|
WHERE {$this->renderWhere($where)}");
|
|
}
|
|
|
|
function renderFields($values) {
|
|
return $this->renderAssoc($values, ', ');
|
|
}
|
|
|
|
function renderWhere($operands) {
|
|
return $this->renderAssoc($operands, ' AND ');
|
|
}
|
|
|
|
function renderAssoc($list, $separator) {
|
|
$fields = '';
|
|
$isFirst = true;
|
|
|
|
foreach ($list as $field => $value) {
|
|
if ($isFirst)
|
|
$isFirst = false;
|
|
else
|
|
$fields .= $separator;
|
|
|
|
$fields .= $this->quote($field) .' = '. $this->renderValue($value);
|
|
}
|
|
|
|
return $fields;
|
|
}
|
|
|
|
function renderList($list, $separator = ', ') {
|
|
$values = '';
|
|
$isFirst = true;
|
|
|
|
foreach ($list as $value) {
|
|
if ($isFirst)
|
|
$isFirst = false;
|
|
else
|
|
$values .= $separator;
|
|
|
|
$values .= $this->renderValue($value);
|
|
}
|
|
|
|
return $values;
|
|
}
|
|
}
|