/**
* Выполняет SQL-запрос.
*
* Результатом будет либо возвращённое булево значение,
* либо объект класса Krugozor_DB_MysqlStatement.
*
* Метод принимает обязательный параметр - SQL-запрос и, в случае наличия,
* любое количество аргументов - значения placeholder.
* Метод использует технологию placeholders - для вставки данных в строку
* SQL-запроса используются специальные маркеры, а сами данные передаются "позже".
* Данные, прошедшие через систему placeholders,
* экранируются специальными функциями экранирования,
* в зависимости от типа сравнения (см. далее). Т.е. вам нет
* необходимости заключать переменные в функции
* экранирования типа {@link mysql_escape_string()}.
*
* Существует несколько видов маркеров:
* ?s и ? - маркер типа string, данные экранируются как строка
* ?S - маркер типа string, данные экранируются как строка для подстановки в LIKE
* ?i - маркер типа int, данные приводятся к типу int
* ?a - маркер хеш-массива.
*
* @param string строка SQL-запроса
* @param mixed подставляемые значения для placeholder-ов.
* @return mixed Возвращает либо bool, либо объект класса Krugozor_DB_MysqlStatement.
*/
public function query()
{
$this->connect();
// Если аргументов в функцию, не переданно, возвращаем FALSE.
if (!$c = func_num_args()) {
return false;
}
$arg_list = func_get_args();
// Ссылка на запрос.
$q = &$arg_list[0];
// Экранируем символ, т.к. этого требует sprintf
$q = str_replace('%', '%%', $q);
// Массмв, в который будем записывать placeholder's
// в порядке появления их в строке.
$ph = array();
// Новый запрос, после обработки нижестоящим кодом.
$nq = '';
$strlen = strlen($q);
// Парсим запрос, по символам.
for ($i=0; $i<$strlen; $i++)
{
// Обнаруживаем метку-заполнитель `?`
if ($q[$i] === '?')
{
if ($q[$i+1] === '?')
{
$nq .= $q[$i];
}
else if ($q[$i+1] === 's' && isset($q[$i+1]) && $q[$i+1] !== '?')
{
$nq .= '%s';
$ph[] = '?s';
}
else if ($q[$i+1] === 'S' && isset($q[$i+1]) && $q[$i+1] !== '?')
{
$nq .= '%s';
$ph[] = '?S';
}
else if ($q[$i+1] === 'i' && isset($q[$i+1]) && $q[$i+1] !== '?')
{
$nq .= '%s';
$ph[] = '?i';
}
else if ($q[$i+1] === 'a' && isset($q[$i+1]) && $q[$i+1] !== '?')
{
$nq .= '?a';
$ph[] = '?a';
}
else if ($q[$i+1] === 'n' && isset($q[$i+1]) && $q[$i+1] !== '?')
{
$nq .= '%s';
$ph[] = '?n';
}
$i++;
}
// это обычный символ.
else {
$nq .= $q[$i];
}
}
// SQL-запрос
$q = $nq; // тут в действительности присваиваем значение элементу $arg_list[0]
// Смотрим, объявленны ли маркеры массива
while (($index = array_search('?a', $ph)) !== FALSE)
{
$index += 1;
// Создаем строку SQL-запроса и добавляем в аргументы значения из массива
if (is_array($arg_list[$index]))
{
$array_keys = array_keys($arg_list[$index]);
$array_values = array_values($arg_list[$index]);
unset($arg_list[$index]);
$arg_list = self::array_push_before($arg_list, $array_values, $index);
$sql_array = '';
foreach ($array_keys as $value)
{
$sql_array .= " `$value` = \"%s\",";
$pls[] = '?s';
}
$sql_array = trim($sql_array, ',');
unset($ph[$index-1]);
$ph = self::array_push_before($ph, $pls, $index-1);
$q = self::str_replace_once('?a', $sql_array, $q);
}
}
$w = 0;
foreach ($arg_list as $k => $v)
{
// k = 0 - это всегда SQL-запрос, поэтому эскейпить его не нужно!
if ($k === 0) {
continue;
}
// Если значение пусто, то никаких escape не делаем.
if ($v === '') {
$w++; // добавил 25 мая
continue;
}
if ($ph[$w] === '?s') {
$arg_list[$k] = mysql_real_escape_string($v, $this->lnk);
}
else if ($ph[$w] === '?S') {
$arg_list[$k] = $this->escape_like($v);
}
else if ($ph[$w] === '?i') {
$arg_list[$k] = filter_var($v, FILTER_SANITIZE_NUMBER_INT);
}
else if ($ph[$w] === '?n')
{
if ($v !== null)
{
throw new Exception('Попытка записать NULL в базу при значении '.print_r($v, true));
}
$arg_list[$k] = 'NULL';
}
$w++;
}
// Формируем строку SQL-запроса.
$this->query = @call_user_func_array('sprintf', $arg_list);
if (FALSE === $this->query)
{
$err = error_get_last();
throw new Krugozor_Db_Mysql_Exception(
'Ошибка парсера sprintf: '. $err['message'], '', print_r($arg_list, true)
);
}
$this->result = mysql_query($this->query, $this->lnk);
self::$queries[] = $this->query;
if (!$this->result)
{
throw new Krugozor_Db_Mysql_Exception('Ошибка в запросе: ' . mysql_error());
}
if (is_resource($this->result))
{
return new Krugozor_Db_Mysql_Statement($this->result);
}
return $this->result;
}