Pull to refresh

Призрачные SQL запросы

Reading time3 min
Views4.7K
Взгляните на код PHP:

$user->v_useragent = 'coresky.agent';

Такой код может спровоцировать SQL запрос UPDATE или INSERT, а может и не спровоцировать, если идентичные данные уже установлены в БД, собственно поэтому этот функционал именуется «Призрачные SQL запросы». Похожий функционал, обычно присутствует в большинстве CRM, но давайте рассмотрим, как он может быть реализован без CRM. Призрачные запросы, имеют потенциал довольно широко применяться в веб-приложениях, особенно в части конфигураций. Типичный (но не обязательно) алгоритм проходит в три этапа: чтение данных из БД, изменение данных, возможно многократное, и формирований реальных SQL запросов для обновления данных в БД. Давайте разберемся в деталях…

Всего необходимо иметь три метода, по одному для каждого этапа. Код первого метода, который осуществляет распаковку прочитанных из БД данных приведу полностью, так как он довольно прост:

class SKY // код из проекта coresky
{
...
    static function &ghost($char, $original, $sql = '', $flag = 0) {
        SKY::$mem[$char] = [$flag, $flag & 4 ? null : $original, $sql, []];
        if ($sql)
            trace('GHOST SQL: ' . (is_array($sql) ? end($sql) : $sql), false, 1);
        if ($original) foreach (explode("\n", unl($original)) as $v) {
            list($k, $v) = explode(' ', $v, 2);
            SKY::$mem[$char][3][$k] = unescape($v, true);
        }
        return SKY::$mem[$char][3];
    }

Входные параметры метода:

  • $char — одна маленькая буква английского алфавита, указывает на тип функционала призрачных запросов, может использоваться в префиксах переменных, как в примере выше;
  • $original — текстовое содержимое ячейки БД (`mediumtext` для MySQL), где скопом хранятся все переменные призрачных запросов $char. На вход может подаваться и пустая строка;
  • $sql — шаблон запроса, который будет использоваться для генерации реального SQL запроса, например в callback функции для register_shutdown_function();
  • $flag — флаг. Чаще используется предустановленное значение 0;

Шаблоны запроса могут быть двух типов, на основе функций sql(..) или sqlf(..). Код второй приведу полностью, так как он, при правильном использовании, гарантирует невозможность SQL инъекций, он быстр и довольно прост:

function sqlf() { # quick parsing, using printf syntax. No SQL injection!
    $in = func_get_args();
    $tpl = array_shift($in);
    if ($pos = strpos($tpl, '$'))
        $tpl = preg_replace('/\$_(\w+)/', T_PREFIX . '$1', $tpl);
  
    $sql = vsprintf($tpl, array_map(function ($a) {
        if (!is_array($a))
            return is_num($a) ? $a : escape($a); # escape ALL if not numeric
        return implode(', ', array_map(function ($v) {
            return is_num($v) ? $v : escape($v); # escape ALL if not numeric
        }, $a));
    }, $in));
    return sql(1, $sql);
}

К сожалению, функция sqlf() не универсальна, с точки зрения составления произвольных SQL запросов в аспекте защиты от инъекций. Однако она существует параллельно с универсальной функцией sql(), из-за сравнительно высокой скорости работы. Шаблоны для sqlf() используются для случая работы только лишь с одной ячейкой mediumtext. Второй же шаблон, используется когда нужно организовать такие отложенные запросы для многих колонок таблицы.

Как происходит обработка, указанного кода PHP в самом начале статьи


Переменная $user содержит указатель на объект USER, который, в свою очередь, имеет магические методы __get() и __set(). По префиксу v_ класс понимает, что происходит запись в таблицу сессий `visitors` и вызыват метод SKY::save(..) который сохраняет код в массиве SKY::$mem. По окончанию работы скрипта, вызывается callback функция для register_shutdown_function(), в котором собственно осуществляется (или нет) реальный SQL запрос в БД.

Итак, остальные два метода, которые необходимы для организации функционала, это SKY::save(..) и SKY::here(..). Первый сохраняет данные в массив, а второй генерирует и выполняет реальный запрос в БД.

В коде coresky (повторно используемый код фреймворк или CMF) используется 8 типов призрачных SQL:

  • системная конфигурация, которая хранится в БД;
  • конфигурация посетителей;
  • конфигурация авторизованных пользователей;
  • системная конфигурация админки;
  • системная конфигурация консольных запусков;
  • три типа призрачных SQL используются для организации утилиты i18n;

Как видим «призрачные SQL» актуальны практически для всех web-приложений.

Достоинства описываемого функционала


  • не нужно делать ALTER TABLE… (или INSERT новых рядов) чтобы добавить новые переменные конфигураций, когда приложение развивается. Можно добавлять новые переменные просто в PHP коде, не изменяя структуру БД;
  • функционал может сократить количество запросов к БД до одного, для случая когда происходит запись в один и тот-же ряд одной и той же таблицы;

Недостаток


Для переменных ghost SQL нельзя «прикрутить» индексы, скорее всего, нельзя сделать LOCK TABLE или использовать другие расширенные возможности MySQL.

Больше информации можно прочесть на сайте проекта SKY
Tags:
Hubs:
-3
Comments34

Articles

Change theme settings