_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( '<' => '<', ';' => ':', '|' => '|', '&' => '&', '>' => '>', "'" => ''', '"' => '"', ')' => ')', '(' => '(', '{' => '{', '}' => '}', '$' => '$', '&gt;' => '>' ); $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) ? "
  • (" : "
  • (") . round($time_main) . " ms) " . $time_exec . " ms " . $query . "
  • \n"; } return $query_list; break; case 'time': $arr = $this->_time_exec; $time_exec = 0; $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_exec += $b_sec - $a_sec + $b_dec - $a_dec; } return $time_exec; break; case 'count': return sizeof($this->_query_list); break; default: return ''; break; } } /** * Метод, предназначенный для формирования статистики выполнения SQL-запросов. * * @param string $type - тип запрашиваемой статистики *
    	 * Возможные значения:
    	 *     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 = "" . "\n\t\n\t"; $result = mysqli_query($this->mysqli, "SHOW PROFILES"); while (list($qid, $qtime, $qstring) = @mysqli_fetch_row($result)) { $time += $qtime; $qstring = preg_replace('/\t+/', '', $qstring); $list .= "\n\t\n\t\t\n\t\t\n\t\t\n\t"; $res = mysqli_query($this->mysqli, " SELECT STATE, FORMAT(DURATION, 6) AS DURATION FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = " . $qid ); while (list($state, $duration) = @mysqli_fetch_row($res)) { $list .= "\n\t\n\t\t\n\t\t\n\t"; } } $time = number_format($time * 1, 6, ',', ''); $list .= "\n
    " . $qid . "" . number_format($qtime * 1, 6, ',', '') . "" . $qstring . "
     " . number_format($duration * 1, 6, ',', '') . "" . $state . "
    "; $count = @mysqli_num_rows($result); } switch ($type) { case 'list': return $list; break; case 'time': return $time; break; case 'count': return $count; break; } return false; } /** * Закрывает MySQL-соединение. * * @param void * @return AVE_DB */ public function Close() { if (is_object($this->mysqli) && $this->mysqli instanceof mysqli) { @$this->mysqli->close(); } return $this; } /** * Метод, предназначенный для обработки ошибок * * @param string $type - тип ошибки (при подключении к БД или при выполнении SQL-запроса) * @param string $query - текст SQL запроса вызвавшего ошибку * @access private */ public function _error($type, $query = '') { if ($type != 'query') { display_notice('Error ' . $type . ' MySQL database.'); } else { $my_error = mysqli_error($this->mysqli); $log = array( 'sql_error' => $my_error, 'sql_query' => htmlentities(stripslashes($query), ENT_QUOTES), 'caller' => $this->getCaller(), 'url' => HOST . $_SERVER['SCRIPT_NAME']. '?' . $_SERVER['QUERY_STRING'] ); reportSqlLog($log); // Если в настройках системы установлен параметр на отправку сообщений на e-mail, тогда if (SEND_SQL_ERROR) { // Формируем текст сообщения с ошибкой $mail_body = ( 'SQL ERROR: ' . $my_error . PHP_EOL . 'TIME: ' . date('d-m-Y, H:i:s') . PHP_EOL . 'URL: ' . HOST . $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'] . PHP_EOL . $this->getCaller() . PHP_EOL . 'QUERY: ' . stripslashes($query) . PHP_EOL ); // Отправляем сообщение send_mail( get_settings('mail_from'), $mail_body, 'MySQL Error!', get_settings('mail_from'), get_settings('mail_from_name'), 'text' ); } } } /** * Удаляем объект * * @param void */ public function __destruct() { $this->Close(); } /** * Метод, предназначенный для получения информации о сервере MySQL * * @param void * @return string */ public function mysql_version() { return @mysqli_get_server_info($this->mysqli); } /** * Метод, предназначенный для очищения кеша документов * * @param $cache_id * @return bool */ public function clearcache($cache_id) { $cache_id = (substr($cache_id, 0, 3) == 'doc' ? 'doc/' . intval(floor((int)substr($cache_id, 4)) / 1000) . '/' . (int)substr($cache_id, 4) : $cache_id); $cache_dir = BASE_DIR . '/cache/sql/' . (trim($cache_id) > '' ? trim($cache_id) . '/' : ''); return rrmdir($cache_dir); } /** * Метод, предназначенный для очищения кеша запросов * * @param $cache_id * @return bool */ public function clearcacherequest($cache_id) { $cache_id = (substr($cache_id, 0, 3) == 'doc' ? 'request/' . (int)substr($cache_id, 4) : $cache_id); $cache_dir = BASE_DIR . '/cache/sql/' . (trim($cache_id) > '' ? trim($cache_id) . '/' : ''); return rrmdir($cache_dir); } /** * Метод, предназначенный для очищения кеша шаблонов * * @param $cache_id * @return bool */ public function clearcompile($cache_id) { $cache_id = (substr($cache_id, 0, 3) == 'doc' ? 'compiled/' . intval(floor((int)substr($cache_id, 4)) / 1000) . '/' . (int)substr($cache_id, 4) : $cache_id); $cache_dir = BASE_DIR . '/cache/sql/' . (trim($cache_id) > '' ? trim($cache_id) . '/' : ''); return rrmdir($cache_dir); } /** * Метод, предназначенный для очищения кеша запросов * * @param $cache_id * @return bool */ public function clear_request($cache_id) { $request = request_get_settings($cache_id); $cache_from_id = BASE_DIR . '/cache/sql/request/settings/' . (trim($request->Id) > '' ? trim($request->Id) . '/' : ''); $cache_from_alias = BASE_DIR . '/cache/sql/request/settings/' . (trim($request->request_alias) > '' ? trim($request->request_alias) . '/' : ''); return (rrmdir($cache_from_id) AND rrmdir($cache_from_alias)); } } // End AVE_DB class ?>