_result = $_result; } /** * Метод, предназначенный для обработки результата запроса. * Возвращает как ассоциативный, так и численный массив. * * @return array */ public function FetchArray() { if (is_array($this->_result)) { $a = current($this->_result); next($this->_result); $b = array(); if (! is_array($a)) return false; foreach($a as $k => $v) $b[] = $v; return array_merge($b, $a); } return mysqli_fetch_array($this->_result); } /** * Метод, предназначенный для обработки результата запроса. * Возвращает только ассоциативный массив. * * @return array */ public function FetchAssocArray() { if (is_array($this->_result)) { $a = current($this->_result); next($this->_result); return $a; } return mysqli_fetch_assoc($this->_result); } /** * Метод, предназначенный для обработки результата запроса, возвращая данные в виде объекта. * * @return object */ public function FetchRow() { if (is_array($this->_result)) { $a = $this->FetchAssocArray(); return array2object($a); } return mysqli_fetch_object($this->_result); } /** * Метод, предназначенный для возвращения данных результата запроса * * @return mixed */ public function GetCell() { if (is_array($this->_result)) { $a = current($this->_result); if (is_array($a)) return current($a); else return false; } if ($this->NumRows()) { $a = mysqli_fetch_row($this->_result); return $a[0]; } return false; } /** * Метод, предназначенный для перемещения внутреннего указателя в результате запроса * * @param int $id - номер ряда результатов запроса * @return bool */ public function DataSeek($id = 0) { if(is_array($this->_result)) { //не нашел как переместить указатель в массиве на конкретный reset($this->_result); for($x = 0; $x == $id; $x++) next($this->_result); return $id; //эээ а что вернуть то надо было? } return mysqli_data_seek($this->_result, $id); } /** * Метод, предназначенный для получения количества рядов результата запроса * * @return int */ public function NumRows() { if (is_array($this->_result)) { return (int)count($this->_result); } return (int)mysqli_num_rows($this->_result); } /** * Метод, предназначенный для получения количества полей результата запроса * * @return int */ public function NumFields() { if (is_array($this->_result)) { $a = current($this->_result); return count($a); } return (int)mysqli_num_fields($this->_result); } /** * Метод, предназначенный для получения названия указанной колонки результата запроса * * @param int $i - индекс колонки * @return string */ public function FieldName($i) { if(is_array($this->_result)){ $a = current($this->_result); $b = array_keys($a); return($b[$i]); } mysqli_field_seek($this->_result, $i); $field = mysqli_fetch_field($this->_result); return $field->name; } /** * Метод, предназначенный для освобождения памяти от результата запроса * * @return bool */ public function Close() { if (! is_array($this->_result)) @mysqli_free_result($this->_result); return true; } /** * Возвращает объект результата _result. * * @internal param $void * @return resource */ public function getResult() { return $this->_result; } /** * Удаляем объект */ public function __destruct() { $this->Close(); } } /************************************************************** * * Класс, предназначенный для работы непосредственно с MySQL БД * **************************************************************/ class AVE_DB { /** * Свойства класса */ /** * Хост * * @var string */ protected $db_host; /** * Имя пользователя * * @var string */ protected $db_user; /** * Пароль * * @var string */ protected $db_pass; /** * Номер порта * * @var int */ protected $db_port; /** * Сокет * * @var int */ protected $db_socket; /** * Имя текущей БД. * * @var string */ protected $db_name; /** * Префикс БД. * * @var string */ protected $db_prefix; /** * Стандартный объект соединения сервером MySQL. * * @var mysqli */ protected $mysqli; /** * Список выполненных запросов * * @var array */ public $_query_list; /** * Метки времени до и после выполнения SQL-запроса * * @var array */ public $_time_exec; /** * Последний запрос SQL-запроса * * @var array */ public $_last_query; /** * Конструктор * * @param $db * * @throws AVE_DB_Exception * @return \AVE_DB AVE_DB - объект */ private function __construct($db) { $this->db_host = $db['dbhost']; $this->db_user = $db['dbuser']; $this->db_password = $db['dbpass']; $this->db_prefix = $db['dbpref']; if(!isset($db['dbport'])) $this->db_port = ini_get ('mysqli.default_port'); else $this->db_port = (isset($db['dbport']) ? $db['dbport'] : null); if(!isset($db['dbsock'])) $this->db_socket = ini_get ('mysqli.default_socket'); else $this->db_port = (isset($db['dbsock']) ? $db['dbsock'] : null); $this->Connect(); // Определяем профилирование if (defined('SQL_PROFILING') && SQL_PROFILING) { // mysqli_query($this->mysqli, "QUERY_CACHE_TYPE = OFF"); // mysqli_query($this->mysqli, "FLUSH TABLES"); if (mysqli_query($this->mysqli, "SET PROFILING_HISTORY_SIZE = 100")) { mysqli_query($this->mysqli,"SET PROFILING = 1"); } } } /** * Устанавливает соеденение с базой данных. * * @throws AVE_DB_Exception * @internal param void * @return void */ private function Connect() { if (!is_object($this->mysqli) || !$this->mysqli instanceof mysqli) { $this->mysqli = @new mysqli($this->db_host, $this->db_user, $this->db_password, null, $this->db_port, $this->db_socket); if ($this->mysqli->connect_error) { throw new AVE_DB_Exception(__METHOD__ . ': ' . $this->mysqli->connect_error); } } } /** * Задает набор символов по умолчанию. * * @param string $charset * * @throws AVE_DB_Exception * @return AVE_DB */ public function setCharset($charset) { if (!$this->mysqli->set_charset($charset)) { throw new AVE_DB_Exception(__METHOD__ . ': ' . $this->mysqli->error); } return $this; } /** * Устанавливает имя используемой СУБД. * * @param string $database_name - имя базы данных * @throws AVE_DB_Exception * @return AVE_DB */ public function setDatabaseName($database_name) { if (!$database_name) { throw new AVE_DB_Exception(__METHOD__ . ': Не указано имя базы данных'); } $this->db_name = $database_name; if (!$this->mysqli->select_db($this->db_name)) { throw new AVE_DB_Exception(__METHOD__ . ': ' . $this->mysqli->error); } return $this; } /** * Создает инстанс данного класса. * * @uses $AVE_DB = AVE_DB::getInstance($server, $username, $password, $port, $socket); * @param $db * @return object возвращает инстанс данного класса. */ public static function getInstance($db = array()) { return new self($db); } /** * Возвращает префикс БД. * * @param void * @return string */ public function getPrefix() { return $this->db_prefix; } /** * Возвращает кодировку по умолчанию, установленную для соединения с БД. * * @param void * @return string */ public function getCharset() { return $this->mysqli->character_set_name(); } /** * Возвращает имя текущей БД. * * @param void * @return string */ public function getDatabaseName() { return $this->db_name; } /** * Получает количество рядов, задействованных в предыдущей MySQL-операции. * Возвращает количество рядов, задействованных в последнем запросе INSERT, UPDATE или DELETE. * Если последним запросом был DELETE без оператора WHERE, * все записи таблицы будут удалены, но функция возвратит ноль. * * @see mysqli_affected_rows * @param void * @return int */ public function getAffectedRows() { return $this->mysqli->affected_rows; } /** * Возвращает последний выполненный MySQL-запрос. * * @param void * @return string */ public function getQueryString() { return $this->_last_query; } /** * Возвращает массив со всеми исполненными SQL-запросами в рамках текущего объекта. * * @param void * @return array */ public function getQueries() { return $this->_query_list; } /** * Возвращает id, сгенерированный предыдущей операцией INSERT. * * @see mysqli_insert_id * @param void * @return int */ public function getLastInsertId() { return $this->mysqli->insert_id; } /** * Метод, предназначенный для возвращения ID записи, сгенерированной при последнем INSERT-запросе * * @return int */ public function InsertId() { return (int)mysqli_insert_id($this->mysqli); } /** * Метод, предназначенный для получения функции из которой пришел запрос с ошибкой * * @return string */ public function getCaller() { if (! function_exists('debug_backtrace')) return ''; $stack = debug_backtrace(); $stack = array_reverse($stack); $caller = array(); foreach ((array)$stack as $call) { if (@$call['class'] == __CLASS__) continue; $function = $call['function']; if (isset($call['class'])) { $function = $call['class'] . "->$function"; } $caller[] = (array ( 'call_file' => (isset($call['file']) ? $call['file'] : 'Unknown'), 'call_func' => $function, 'call_line' => (isset($call['line']) ? $call['line'] : 'Unknown') )); } return $caller; } /************************* Внешние методы класса *************************/ /** * Метод, предназначенный для выполнения запроса к MySQL * * @param string $query - текст SQL-запроса * @param bool $log - записать ошибки в лог? по умолчанию включено * @return object/bool - объект с указателем на результат выполнения запроса */ public function Real_Query($query, $log = true) { $result = @mysqli_query($this->mysqli, $query); // Запоминаем последний запрос $this->_last_query = $query; // Если стоит в настройках, запоминать все запросы if (SQL_PROFILING) { $this->_query_list[] = $query; } // Если нет результата и стоит выводить логи, выводим лог ошибки if (! $result && $log) $this->_error('query', $query); if (is_object($result) && $result instanceof mysqli_result) { return new AVE_DB_Result($result); } return $result; } /** * Метод, предназначенный для выполнения запроса к MySQL и возвращение результата в виде асоциативного массива с поддержкой кеша * * @param string $query - текст SQL-запроса * @param integer $TTL - время жизни кеша (-1 безусловный кеш) * @param string $cache_id - Id файла кеша * @param bool $log - записать ошибки в лог? по умолчанию включено * @return array - асоциативный массив с результом запроса */ public function Query($query, $TTL = null, $cache_id = '', $log = true) { // Если это документ, то меняем расположение if (substr($cache_id, 0, 3) == 'doc') { $cache_id = (int)str_replace('doc_', '', $cache_id); $cache_id = 'doc/' . (floor($cache_id / 1000)) . '/' . $cache_id; } // Принудительная фильтрация запроса //$query = filter_var($query, FILTER_SANITIZE_STRING); $result = array(); // Если это SELECT - то отслеживаем кеширование $TTL = strtoupper(substr(trim($query), 0, 6)) == 'SELECT' ? $TTL : null; // Если включен DEV MODE, то отключаем кеширование запросов if (defined('DEV_MODE') AND DEV_MODE) $TTL = null; if ($TTL && ($TTL != 'nocache' AND $TTL != null)) { $cache_file = md5($query); $cache_dir = BASE_DIR . '/cache/sql/' . (trim($cache_id) > '' ? trim($cache_id) . '/' : substr($cache_file, 0, 2) . '/' . substr($cache_file, 2, 2) . '/' . substr($cache_file, 4, 2) . '/'); if (! file_exists($cache_dir)) mkdir($cache_dir, 0777, true); if (! (file_exists($cache_dir . $cache_file) && ($TTL == -1 ? true : time() - filemtime($cache_dir . $cache_file) < $TTL))) { $res = $this->Real_Query($query, $log); while ($mfa = $res->FetchAssocArray()) $result[] = $mfa; file_put_contents($cache_dir . $cache_file, serialize($result)); } else { $result = unserialize(file_get_contents($cache_dir . $cache_file)); } return new AVE_DB_Result($result); } else return $this->Real_Query($query, $log); } /** * This method is needed for prepared statements. They require * the data type of the field to be bound with "i" s", etc. * This function takes the input, determines what type it is, * and then updates the param_type. * * @param mixed $item Input to determine the type. * * @return string The joined parameter types. */ protected function DetermineType($item) { switch (gettype($item)) { case 'NULL': case 'string': return 's'; break; case 'boolean': case 'integer': return 'i'; break; case 'blob': return 'b'; break; case 'double': return 'd'; break; } return ''; } /** * Метод, предназначенный для экранирования специальных символов в строках для использования в выражениях SQL * * @param mixed $value - обрабатываемое значение * @return mixed */ public function Escape($value) { if (! is_numeric($value)) { $value = mysqli_real_escape_string($this->mysqli, $value); } return $value; } /** * Метод, предназначенный для экранирования специальных символов в строках для использования в выражениях SQL * * @param mixed $value - обрабатываемое значение * @return mixed - возвращает строку запроса вычещенной */ public function EscStr($value) { $value = htmlspecialchars($value); $search = array( '<' => '<', ';' => ':', '|' => '|', '&' => '&', '>' => '>', "'" => ''', '"' => '"', ')' => ')', '(' => '(', '{' => '{', '}' => '}', '$' => '$', '>' => '>' ); $value = str_replace(array_keys($search), array_values($search), $value); $value = str_ireplace('%3Cscript', '', $value); $value = filter_var($value, FILTER_SANITIZE_STRING); if (! is_array($value)) { $value = $this->mysqli->real_escape_string($value); } else { $value = array_map(array($this, 'escape'), $value); } return $value; } /** * Метод, предназначенный для возвращения количества всех найденных записей (после запроса) * * @return int */ public function GetFoundRows() { $result = $this->Query('SELECT FOUND_ROWS();'); $strRow = $result->FetchArray(); return (int)$strRow[0]; } /** * Метод, предназначенный для возвращения количества всех найденных записей (после запроса типа "SELECT SQL_CALC_FOUND_ROWS * ...") * * @param $query * @param null $TTL * @param string $cache_id * @return int */ public function NumAllRows($query, $TTL = null, $cache_id = '') { // Если включен DEV MODE, то отключаем кеширование запросов if (defined('DEV_MODE') AND DEV_MODE) $TTL = null; if ($TTL AND ($TTL != "nocache" AND $TTL != null)) { // Кол-во $cache_file = md5($query) . '.count'; $cache_dir = BASE_DIR . '/cache/sql/' . (trim($cache_id) > '' ? trim($cache_id) . '/' : substr($cache_file, 0, 2) . '/'. substr($cache_file, 2, 2) . '/' . substr($cache_file, 4, 2) . '/'); if (! file_exists($cache_dir)) mkdir($cache_dir, 0777, true); if (! (file_exists($cache_dir . $cache_file) && ($TTL == -1 ? true : time() - filemtime($cache_dir . $cache_file) < $TTL))) { if ($query <> $this->_last_query) { $res = $this->Real_Query($query); } else { $res = (int)$this->Query("SELECT FOUND_ROWS()")->GetCell(); file_put_contents($cache_dir . $cache_file, $res); } return $res; } else { return file_get_contents($cache_dir . $cache_file); } } return (int)$this->Query("SELECT FOUND_ROWS()")->GetCell(); } /** * Метод, предназначенный для формирования статистики выполнения SQL-запросов. * * @param string $type - тип запрашиваемой статистики *
* Возможные значения: * list - список выполненых зпаросов * time - время исполнения зпросов * count - количество выполненных запросов ** @return mixed */ public function DBStatisticGet($type = '') { switch ($type) { case 'list': list($s_dec, $s_sec) = explode(' ', $GLOBALS['start_time']); $query_list = ''; $nq = 0; //$time_exec = 0; $arr = $this->_time_exec; $co = sizeof($arr); for ($it = 0; $it < $co;) { list($a_dec, $a_sec) = explode(' ', $arr[$it++]); list($b_dec, $b_sec) = explode(' ', $arr[$it++]); $time_main = ($a_sec - $s_sec + $a_dec - $s_dec)*1000; $time_exec = ($b_sec - $a_sec + $b_dec - $a_dec)*1000; $query = sizeof(array_keys($this->_query_list, $this->_query_list[$nq])) > 1 ? "" . $this->_query_list[$nq++] . "" : $this->_query_list[$nq++]; $query_list .= (($time_exec > 1) ? "
* Возможные значения: * list - список выполненых зпаросов * time - время исполнения зпросов * count - количество выполненных запросов ** @return mixed */ public function DBProfilesGet($type = '') { static $result, $list, $time, $count; if (! defined('SQL_PROFILING') OR ! SQL_PROFILING) return false; if (! $result) { $list = "
" . $qid . " | \n\t\t" . number_format($qtime * 1, 6, ',', '') . " | \n\t\t" . $qstring . " | \n\t
" . number_format($duration * 1, 6, ',', '') . " | \n\t\t" . $state . " | \n\t