<?php
namespace ZF;
use \ZF\Mysql\Exception as MysqlException;
class Mysql {
public static $instance = NULL;
protected $h;
protected $last_result = NULL;
protected $transaction_level = 0;
protected $flags = 0;
protected $connection_data = array();
public $queries = array();
public $last_mysql_check = 0;
public $mysql_check_period = 10; // 5 sec
const LOGGING_QUERIES = 1;
public function __construct() {
ini_set('mysqli.reconnect', 1);
}
public function ping() {
if (time() - $this->last_mysql_check > $this->mysql_check_period) {
if (!$this->h->ping()) {
if ($this->inTransaction())
throw new MysqlException("Connection lost in open transaction!!!");
@$this->h->close();
call_user_func_array(array($this, 'connect'), $this->connection_data);
}
}
}
public function setLoggingQueries($flag) {
$this->flags = $flag ? ($this->flags | self::LOGGING_QUERIES) : ($this->flags & ~self::LOGGING_QUERIES);
return $this;
}
public function getQueriesList() {
return $this->queries;
}
public function connect($host, $username, $password, $database, $port) {
$this->connection_data = func_get_args();
$this->h = new \mysqli;
if (!$this->h->real_connect($host, $username, $password, $database, $port))
throw new MysqlException("Mysql connect error: ".$this->h->error);
$this->h->options(MYSQLI_OPT_CONNECT_TIMEOUT, 3600 * 24 * 31 * 12);
$this->h->set_charset('utf8');
$this->query("SET sql_mode='STRICT_TRANS_TABLES'");
}
public function fetchAssoc($res = NULL) {
if (!$res)
$res = $this->last_result;
return $res->fetch_assoc();
}
public function fetchArray($res = NULL, $resulttype = MYSQLI_BOTH) {
if (!$res)
$res = $this->last_result;
return $res->fetch_array($resulttype);
}
public function fetchRow($res = NULL) {
if (!$res)
$res = $this->last_result;
return $res->fetch_row();
}
public function &fetchAll($res = NULL, $resulttype = MYSQLI_ASSOC) {
if (!$res)
$res = $this->last_result;
$rows = array();
while ($row = $res->fetch_array($resulttype))
$rows[] = $row;
return $rows;
}
public function numRows($res = NULL) {
if (!$res)
$res = $this->last_result;
return $res->num_rows;
}
public function result($res = NULL) {
if (!$res)
$res = $this->last_result;
$row = $res->fetch_row();
return $row[0];
}
public function free($res = NULL) {
if (!$res)
$res = $this->last_result;
return $res->free();
}
public function escape($str) {
return $this->h->real_escape_string($str);
}
public function transaction($function, $on_success = NULL, $on_error = NULL, $args = NULL) {
if (!$this->beginTransaction())
throw new MysqlException("Can't open mysql transaction!");
try {
$function($this, $args);
} catch (Exception $e) {
$this->h->rollback();
$on_error($this, $args);
throw $e; // Прокидываем дальше
}
if (!$this->h->commit()) {
$this->rollback();
$on_error($this, $args);
throw new MysqlException("Can't open mysql transaction!");
}
$on_error($this, $args);
}
public function beginTransaction() {
$this->ping();
if (!$this->transaction_level++)
return $this->h->begin_transaction();
return true;
}
public function commit() {
$this->ping();
if (!$this->transaction_level > 0)
throw new MysqlException("Active transaction not found!");
if (!--$this->transaction_level)
return $this->h->commit();
return true;
}
public function rollback() {
$this->ping();
if (!$this->transaction_level > 0)
throw new MysqlException("Active transaction not found!");
if (!--$this->transaction_level)
return $this->h->rollback();
return true;
}
public function inTransaction() {
return $this->transaction_level > 0;
}
public function affectedRows() {
return $this->h->affected_rows;
}
public function getInfo() {
$info = array(
'records' => 0,
'duplicates' => 0,
'warnings' => 0,
'changed' => 0,
'matched' => 0,
);
if (preg_match("/Records: (\d+)|Duplicates: (\d+)|Warnings: (\d+)|Changed: (\d+)|Rows matched: (\d+)/", $this->h->info, $m) > 0) {
if ($m[1])
$info['records'] = $m[1];
if ($m[2])
$info['duplicates'] = $m[2];
if ($m[3])
$info['warnings'] = $m[3];
if ($m[4])
$info['changed'] = $m[4];
if ($m[5])
$info['matched'] = $m[5];
}
return $info;
}
public function query($query, $argv = []) {
$this->ping();
$this->last_result = NULL;
$params = NULL;
if ($argv) {
$argc = count($argv);
$len = strlen($query);
$pos = 0; $arg_n = 0;
while (($pos = strpos($query, '?', $pos)) !== false) {
// if (is_numeric($argv[$arg_n]) && $argv[$arg_n][0] != '0' && $argv[$arg_n][0] != '+')
// $value = $argv[$arg_n];
// else
if (is_null($argv[$arg_n]))
$value = "NULL";
else
$value = "'".$this->h->real_escape_string($argv[$arg_n])."'";
$query = substr_replace($query, $value, $pos, 1);
$pos += strlen($value) - 1;
++$arg_n;
if ($arg_n > $argc)
break;
}
}
$start_time = microtime(true);
$req = $this->h->query($query);
$end_time = microtime(true);
if ($this->flags & self::LOGGING_QUERIES)
$this->queries[] = array($query, $params, $end_time - $start_time);
if ($req === false)
throw new MysqlException("Can't execute query (".$query."): ".$this->h->error);
$this->last_result = $req;
return $req;
}
public function insertId() {
return $this->h->insert_id;
}
public function __destruct() {
if ($this->transaction_level > 0)
throw new MysqlException("Not closed transactions found! (".$this->transaction_level.")");
}
public static function instance() {
if (!self::$instance)
self::$instance = new Mysql;
return self::$instance;
}
public function close() {
$this->h->close();
}
}