[recovery mode] Призрачные SQL запросы

habr.png

Взгляните на код PHP:

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


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

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); # just escape ALL parameters if not numeric
    return implode(', ', array_map(function ($v) {
      return is_num($v) ? $v : escape($v); # just escape ALL parameters 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-приложений.

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


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


Недостаток


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

Больше информации можно прочесть на сайте проекта SKY

© Habrahabr.ru