Класс Quoter
предназначен для безопасного конструирования SQL-запросов из шаблонов с использованием типизированных меток-плейсхолдеров (placeholders), далее — просто «меток».
Класс Db
является наследником стандартного класса PDO
и расширяет его несколькими сервисными функциями. В этих функциях используются шаблоны с типизированными метками.
Метка — это место в шаблоне SQL-запроса, на место которой будут вставлены реальные данные. Например, в шаблоне select * from users where id = ? or login = ?
символы ?
является метками. Реальный SQL-запрос из этого шаблона будет конструироваться, например, так $sql = $quoter->format("select * from users where id = ? or login = ?", array(42, "admin"));
. Результатом этого вызова будет строка select * from users where id = 42 or login='admin'
.
SQL-шаблоны с метками нужны для того, чтобы правильно форматировать данные перед вставкой в SQL-запрос. Например, в примере выше число (42) вставляется в запрос без дополнительной обработки, а строка ("admin") заключается в одинарные кавычки. Для других типов данных есть другие правила преобразования. Часто определение типа производится автоматически, на основании типа входных данных. Это определение не всегда происходит правильно (например, из GET- и POST-параметров всегда приходят только строки). Поскольку SQL — типизированный язык, важно соблидать тип данных и не сравнивать, например, строку с логической величиной. Для этого и придуманы типизированные метки, которые в самом шаблоне указывают, каким должен быть тип данных в это месте.
С типизированными метками шаблон, приведённый выше, выглядит так: select * from users where id = ?i or login = ?s
. Метка ?i
означает, что в шаблон надо вставить целое число, метка ?s
означает строку. Теперь можно написать так: $sql = $quoter->format("select * from users where id = ? or login = ?", array("42", "admin"));
и всё равно получить корректный результат.
Подробно об идее и теории типизированных меток можно прочесть в статьях http://habrahabr.ru/post/148701/ и http://dklab.ru/lib/DbSimple/manual.html.
Все метки в данной библиотеке основаны на символе ?
, после которого следует тип метки. Если нужно просто вставить символ ?
в запрос, без интерпретации его как метки, то используйте удвоение: ??
.
Скалярные метки предназначены для вставки в запрос скалярных величин — строк, чисел и т. д.
?
— автотип — форматирование осуществляется на основании типа входных данных;
?s
— строка;
?i
— целое число;
?f
— дробное число;
?b
— логическое значение;
?t
— строка-идентификатор объекта базы данных (имя таблицы, колонки и т. п.);
?!
— raw-метка — значение вставляется в запрос как есть, без форматирования. Полезно для вставки уже отформатированных SQL-фрагментов.
Пример шаблона, использующего скалярные метки: select * from ?t where id = ?i and ?b
.
Если в качестве данных передано некорректное для данного типа значение (например, нечисловая строка передана в метку с типом ?i
), то используется «нулевое» значение данного типа (пустая строка, ноль, false). Если в шаблон передан null
, он вставляется в результат как SQL-значение NULL
или UNKNOWN
.
Исключением являются типы ?t
и ?!
— первый требует на входе строго непустые строки, в противном случае выбрасывается исключение, а второй любое значение приводит к строке и вставляет без изменения.
У значения NULL
(UNKNOWN
) есть особенность — в SQL его нельзя сравнивать при помощи оператора =
или <>
. Поэтому при вставке NULL-а проверяется предшествующий ему оператор, и =
заменяется на IS
, а <>
— на IS NOT
. Таким образом, a = ?
заменяется на a IS NULL
.
Метки-массивы нужны для вставки в запросы сложных структур — массивов и хэшей.
?@[тип]
— типизированный массив для операторов IN
/ NOT IN
. Массив предполагается однородным — заполненным величинами одного типа, этот тип указывается после символа @. Если тип не указан, то он выводится из типа входных данных. Пример: ?@i
— метка для массива целых чисел.
Если переданный массив не пуст, то в запрос вставляются значения из массива, разделённые запятыми. Если массив пуст или передан не массив, то вставляется NULL
.
Пример: $quoter->format("… where id in (?@i)", array(array(1, 2, 3)));
вернёт … where id in (1, 2, 3)
.
?#v
— хэш значений для оператора INSERT. Тип ключей всегда ?t
, тип значений выводится автоматически. Если передан хэш array(k1 => v1, k2 => v2, …)
, то в запрос вставится (k1, k2, …) values (v1, v2, …)
. Если хэш пуст, вставляется строка default values
, если значение некорректно, то выбрасывется исключение.
?#u
— хэш значений для оператора UPDATE. Тип ключей всегда ?t
, тип значений выводится автоматически. Если передан хэш array(k1 => v1, k2 => v2, …)
, то в запрос вставится k1=v1, k2=v2, …
. Если хэш пуст или значение некорректно, то выбрасывется исключение.
Примеры: insert into users ?#v
, update users set ?#u where id = ?i
.
SQL-запрос формируется из строки-шаблона и массива параметров, значения которых вставляются в места меток шаблона. Массив параметров может быть ассоциативным, тогда для работы с ним следует использовать именованные метки. Имя метки совпадает с ключом в массиве параметров и записывается перед символом метки (?
) без пробела. Допустимые имена меток имеют формат [a-zA-Z_][a-zA-Z0-9_]*
. Пример:
$quoter->format("select * from users where id = userId?i", array("userId" => 42));
Если массив параметров не ассоциативный, то вместо имени метки можно использовать порядковый номер параметра в массиве (нумерация с единицы). Например:
$quoter->format("select * from users where id = 1?i", array(42));
Это может быть удобным, если один параметр используется в запросе несколько раз в разных местах. В этом случае можно не дублировать его в массиве параметров, а просто сослаться из шаблона на нужную позицию в массиве.
Пустое имя/номер метки обрабатывается как автоматически увеличивающийся номер параметра с числовым ключом. То есть, первая (по ходу шаблона) метка с пустым именем соответствует параметру на позиции 1, вторая — 2 и т. д.
Если в параметрах нет какого-либо из требуемых ключей или есть лишние, то выбрасывается исключение.
\Yaff\db\Quoter
— абстрактный класс, имеющий (пока что) две реализации: QuoterSQL
— минимальный стандартный SQL-синтаксис и QuoterPostgres
— синтаксис PostgreSQL.
Основным методом Quoter
-а является метод format($query, array $args = array())
. Аргумент $query
— это шаблон запроса с типизированными метками, а $args
— массив аргументов, которые будут подставлены на место меток. Метод возвращает собранную из шаблона и аргументов SQL-строку.
$quoter = new \Yaff\db\QuoterSQL();
$sql = $quoter->format("select * from users where id = ?i", array(42));
Результатом будет строка select * from users where id = 42
.
Класс \Yaff\db\Db
предназначен для упрощения наиболее частых операций с базой данных. Он является наследником библиотечного класса PDO
и использует внутри себя Quoter
для подстановки данных в шаблоны. Класс предлагает следующие дополнительные сервисные методы (почти все они принимают SQL-шаблон $sqlTpl
и массив аргументов $args
):
perform($sqlTpl, array $args = array())
— выполнить запрос, не возвращая результат;
performInsert($tableName, array $values = array())
— выполнить INSERT-запрос в таблицу $tableName
;
getRow($sqlTpl, array $args = array())
— вернуть первую строку результата запроса как ассоциативный массив;
getAll($sqlTpl, array $args = array())
— вернуть все строки результата запроса как массив ассоциативных массивов;
getOne($sqlTpl, array $args = array())
— вернуть значение первой колонки первой строки результата запроса;
getCol($sqlTpl, array $args = array(), $n = 0)
— вернуть $n-ю колонку из результатов запроса в виде массива;
getAssoc($sqlTpl, array $args = array(), $forceArray = false)
— вернуть результаты запроса в виде ассоциативного массива, в котором первая колонка каждой строки является ключом, а последующие — значением (массивом). Если в результате всего две колонки, то значение не преобразуется в массив, это можно изменить, выставив последний параметр метода в TRUE.