File: /www/exchange0old/exchange/Bin/Db.php
<?php
class Bin_Db {
static protected $instances = array();
/**
* @var mysqli
*
*/
protected $connection = null;
protected $configPrefix;
protected $saveQueries = false;
protected $queries = array();
protected $lastError = '';
protected $transactionStatus = false;
protected $transactionDepth = 0;
protected $escapeIdentifierCharacter = '`';
protected $escapeValueCharacter = "'";
/**
* @param null $group
* @return $this
*/
public static function connect($group = null) {
if (!isset(self::$instances[$group])) {
self::$instances[$group] = new self($group);
}
return self::$instances[$group];
}
protected function __construct($group = null) {
$this->configPrefix = 'database.' . ($group !== null ? $group . '.' : '');
$this->setSaveQueries($this->getConfig('saveQueries', false));
$this->initialize();
}
protected function initialize() {
$this->connection = mysqli_init();
$this->connection->real_connect(
$this->getConfig('hostname'),
$this->getConfig('username'),
$this->getConfig('password'),
$this->getConfig('database'));
}
public function getConfig($key, $default = null) {
return Bin_Config::get($this->configPrefix.$key, $default);
}
public function affectedRows() {
return $this->connection->affected_rows;
}
public function insertID() {
return $this->connection->insert_id;
}
public function prefixTable($table) {
return $this->getConfig('prefix') . $table;
}
public function getConnection() {
return $this->connection;
}
/**
* Executes the query against the database.
*
* @param $sql
*
* @return mixed
*/
public function execute($sql) {
return $this->connection->query($this->prepQuery($sql));
}
/**
* Prep the query
*
* If needed, each database adapter can prep the query string
*
* @param string $sql an SQL query
*
* @return string
*/
protected function prepQuery($sql) {
// mysqli_affected_rows() returns 0 for "DELETE FROM TABLE" queries. This hack
// modifies the query so that a proper number of affected rows is returned.
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $sql)) {
return trim($sql) . ' WHERE 1 = 1';
}
return $sql;
}
public function query($sql, array $binds = array()) {
$startTime = microtime(true);
// Replace bind parts
$sql = $binds ? strtr($sql, $binds) : $sql;
if (false === ($result = $this->execute($sql))) {
$this->lastError = $this->connection->error;
if ($this->saveQueries) {
$this->queries[] = $this->getStats($sql, $startTime, $startTime);
}
return false;
}
if ($this->saveQueries) {
$this->queries[] = $this->getStats($sql, $startTime);
}
// For create table, struct, etc queries
return new Bin_Db_Result($this, $result);
}
protected function getStats($query, $startTime, $endTime = null) {
if($endTime === null) {
$endTime = microtime(true);
}
return array(
'query' => $query,
'start' => $startTime,
'duration' => $endTime-$startTime);
}
protected function beginTransaction($mode = null) {
$this->connection->autocommit(false);
if ($mode && !$this->query("SET TRANSACTION ISOLATION LEVEL $mode")) {
throw new Exception($this->getLastError());
}
return (bool)$this->query('START TRANSACTION');
}
protected function finishTransaction() {
if ((bool) $this->query('COMMIT')) {
$this->connection->autocommit(true);
return true;
}
return false;
}
protected function cancelTransaction() {
if ((bool) $this->query('ROLLBACK')) {
$this->connection->autocommit(true);
return true;
}
return false;
}
public function transactionStart($mode = null) {
if ($this->transactionDepth === 0) {
$result = $this->beginTransaction($mode);
} else {
$result = (bool)$this->query("SAVEPOINT LEVEL{$this->transactionDepth}");
}
$this->transactionDepth++;
return $result;
}
public function transactionCommit() {
$this->transactionDepth--;
if ($this->transactionDepth === 0) {
return $this->finishTransaction();
}
return (bool)$this->query("RELEASE SAVEPOINT LEVEL{$this->transactionDepth}");
}
public function transactionRollback() {
$this->transactionDepth--;
if ($this->transactionDepth === 0) {
return $this->cancelTransaction();
}
return (bool)$this->query("ROLLBACK TO SAVEPOINT LEVEL{$this->transactionDepth}");
}
public function getLastError() {
return $this->lastError;
}
/**
* @return bool|mixed|null
*/
public function getSaveQueries() {
return $this->saveQueries;
}
/**
* @param bool $saveQueries
* @return $this
*/
public function setSaveQueries($saveQueries) {
$this->saveQueries = $saveQueries;
return $this;
}
public function getQueriesLog() {
return $this->queries;
}
/**
* Escapes special characters in a string for use in an SQL statement, taking into account the current charset of the connection
* @param $string
* @return string
*/
public function escapeString($string) {
return $this->connection->escape_string($string);
}
public function escapeValue($value) {
if ($value instanceof Bin_Db_Expr) {
return $value->getExpr();
}
if (is_bool($value)) {
$value = (int)$value;
}
if (is_null($value)) {
return 'NULL';
}
if (is_numeric($value)) {
return $value;
}
if ($value instanceof DateTime) {
$value = $value->format('Y-m-d H:i:s');
}
if (is_array($value)) {
return array_map(array($this, __FUNCTION__), $value);
}
return $this->escapeValueCharacter . $this->connection->escape_string($value) . $this->escapeValueCharacter;
}
public function escapeIdentifier($identifier) {
return $this->escapeIdentifierCharacter . $identifier . $this->escapeIdentifierCharacter;
}
public function builder() {
return new Bin_Db_Builder($this);
}
public function getTransactionDepth() {
return $this->transactionDepth;
}
}