Разность в скорости здесь не имеет значения. Я тут как раз тестировал на скорую руку, отвечая на один вопрос. Очень примерная разница составила 3 десятитысячных доли секунды. Положа руку на сердце — это не тот участок который надо оптимизировать. Но если так хочется, то prepared statements работают чуть медленнее, поскольку им приходится в среднем по два раза обращаться к базе на один запрос. Но я в своем классе их не использую не поэтому.
Самый безопасный вариант для PDO я привел выше.
Лучше, качественнее и безопаснее будет работать вариант на SafeMySQL. Поскольку он делает всё сам, не полагаясь на программиста. В этом и состоит суть безопасного составления запросов вообще, и моего класса в частности :)
А приведённый запрос не будет работать. Как минимум, по трём причинам.
Ой, это я с огромным удовольствием.
Скажем, пользователь выбрал в чекбоксами разделы новостей, которые он хочет смотреть. Они лежат в массиве $_GET['themes'] (массив чисел)
Плюс выбрал сортировку по количеству комментариев, она лежит в переменной $_GET['order']
На SafeMySQL это будет две строчки
$sql = "SELECT * FROM news WHERE theme IN(?a) ORDER BY ?n";
$data = $db->getAll($sql,$_GET['themes'],$_GET['order']);
На PDO что-то вроде
$in = trim(str_repeat('?,',count($_GET['themes'])).",");
$order = str_replace('`','``',$_GET['order']);
$stmt = $db->prepare("SELECT * FROM news WHERE theme IN($in) ORDER BY `$order`");
$stmt->execute($_GET['themes']);
$data = $stmt->fetchAll();
Ну, в общем, конечно, кода тут не на экран, но попахивает он конкретно. Плюс — здравствуй ручное экранирование! — от которого PDO, вроде бы, должен бы избавить.
Всякий раз, когда я смотрю на мучения посетителей стаковерфлоя с динамическими инсертами — у меня сердце кровью обливается.
Ну, API всегда будет «гибче» абстракции.
Но, как говорилось выше, API — это набор «сделай сам». Слишком многое приходится делать руками.
Те же подстановки имени поля, массивов для IN и SET. Чтобы INSERT с динамическим числом полей, приходится очень сильно попотеть. Я бы не назвал разницу между одной строкой и экраном кода словом «немного».
При этом я как-то не вижу какой-то особой «негибкости» у DbSimple. Есть конкретный пример того, что не смогла она, но смог чистый PDO?
Черт возьми, жаль, что я не знал об этой библиотеке раньше. Отлично сделано.
Придраться можно только к двум вещам — довольно замороченный парсер с колбэками — у меня сделано гораздо проще.
И, конечно, слишком перегруженная функция query() — её сам бог велел разбить на несколько специализированных методов.
Предположу, что это рудимент с тех времён, когда класс ещё был функцией — судя по всему, был такой период в его биографии :)
Параметр, который определяет поведение функции, а не её результат, нужен только при процедурном подходе. При объектом удобнее будет разделить функционал на несколько методов.
Автоматом определить не получится.
Строка может быть как строкой, так и идентификатором.
Массив может быть передан как для in, так и для set.
К выбору людей, которые предпочитают построитель запросов я отношусь уважительно. Это отличная штука, и многие вещи позволяет сделать гораздо красивее, чем мой класс. Лично же для себя мнехочется сократить максимальную близость к SQL. Мне кажется, оба подхода имеют право на существование.
В данный момент так и происходит — запрос, правда, выполняется, но возвращает пустой результат.
Для IN это логично — если мы передаём в IN пустой массив, то хотим получить строки, в которых есть соответствие переданным в массиве значениям. А если массив пустой, то и возвращать нечего.
SELECT * FROM id IN (NULL) так и поступает.
Но вот для запроса
SELECT * FROM id NOT IN (NULL) логично ожидать обратного поведения, но он тоже возвращает ноль строк.
Просто по пустому массиву вываливаться нельзя — запрос может быть «SELECT * FROM id IN (NULL) OR что-то» и прекрасно строки возвращать…
Не стоит столь скептически относиться ко всему, что кажется вам банальным.
В любом деле развитие идёт по спирали, и со временем выходит на новый уровень.
Здесь ключевой момент — наличие плейсхолдеров для любого типа данных, который только может придти в запрос, а не только для самых ходовых, как в других решениях.
Куда менее обидно было бы услышать предметную критику, чем ленивый зевок «Знаем, плавали».
В таком случае обёртка должна дублировать 100% функционала SQL. Всякие sqlExpression не подойдут — туда точно так же руками чего угодно напихать можно. Поэтому я сильно сомневаюсь как в принципиальной достижимости этой цели, так и в осмысленности задачи «Написать SQL на РНР».
Но с критикой моего решения ты, пожалуй, прав. Не понимая принципа раздельного форматирования, невозможно и применять его. А это непонимание, похоже — и есть основная проблема.
В том-то и дело, что это не yet another DAL. А точнее — не столько. В первую очередь это попытка окончательно решить проблему инъекций. Покрыв при этом 100% случаев. В отличие от существующих решений, у которых всё гладко только на бумаге. И в примитивных примерах.
А как дело доходит до реальной жизни, то все good practices почему-то всегда куда-то испряются из кода. И SQL шпарится напрямую, с подстановками переменных, из шаблонов обращаются к базе данных (а если шаблон не нативный — значит куча HTML-а пишется в контроллере), и так далее.
Возможно, это перфекционизм, но я последовательно копаю некоторые темы в веб-разработке, пытаясь покрыть 100% применения, а не только самые ходовые случаи.
В данном случае мне это удалось (во всяком случае, пока никто не доказал обратного). Но я провалил задачу донести результат до окружающих.
На тот момент ещё не вышла, а вот только сегодня: habrahabr.ru/post/165069/
Спасибо за этот комментарий, он заставил меня активизировать работу по доведению класса до публичного состояния :)
Сравнение с популярными движками есть в этой статье — выше приводятся примеры кода, который нам бы понадобился для PDO и Mysqli. А в новой статье есть примеры того, как это делается в моей библиотеке.
Mysql поддерживает, оба продвигаемых экстеншена PHP — PDO и Mysqli — тоже.
dbsimple, похоже, для mysql не умеет — надо писать драйвер для mysqli.
Но, это, по сути, отдельные запросы. А речь именно о сборке единого.
Плюс, к примеру, в prepared statement невозможно подставить динамически имя поля.
Это не моя идея — она много где используется. Например, в PEAR::DB.
Но без поддержки различных типов данных она теряет половину своей прелести.
Поэтому главная идея — плейсхолдеры для всех встречающихся типов данных, а не только для строк и чисел. PDO не предоставляет никаких методов динамически добавить в запрос имя поля.
а что-то более приближённое к реальности — какой-нибудь order by if(...), ON DUPLICATE…
Я вижу, там заявлена поддержка prepared statements — значит, кроме скаляров больше ничего от инъекций не защищается.
Видел, и даже написал об этом. У dbsimple очень неоднозначный синтаксис, каждый элемент имеет по 2-3 дначения, в зависимости от контекста.
В моей практике чаще приходится добавлять элементы в запрос, чем вырезать их. Например:
Непонятно, что имеется в виду под заводским великом.
Если один из стардартных API — PDO или Mysqli, то это не велик, а набор запчастей «сделай сам». На них, конечно, можно ездить, но только на одной передаче и очень часто крутя педалями. И руль заклинен в положении «прямо».
Database access wrapper писать в любом случае нужно, с голым API жить невозможно.
Если имеются в виду DAL и квери-билдеры различных фреймворков, то это тоже не фабричные велосипеды. И я бы с удовольствием с ними погонялся.
Ещё две остались :)
Самый безопасный вариант для PDO я привел выше.
Лучше, качественнее и безопаснее будет работать вариант на SafeMySQL. Поскольку он делает всё сам, не полагаясь на программиста. В этом и состоит суть безопасного составления запросов вообще, и моего класса в частности :)
А приведённый запрос не будет работать. Как минимум, по трём причинам.
я ж этот же вариант и нарисовал :)
Скажем, пользователь выбрал в чекбоксами разделы новостей, которые он хочет смотреть. Они лежат в массиве $_GET['themes'] (массив чисел)
Плюс выбрал сортировку по количеству комментариев, она лежит в переменной $_GET['order']
На SafeMySQL это будет две строчки
На PDO что-то вроде
Ну, в общем, конечно, кода тут не на экран, но попахивает он конкретно. Плюс — здравствуй ручное экранирование! — от которого PDO, вроде бы, должен бы избавить.
Всякий раз, когда я смотрю на мучения посетителей стаковерфлоя с динамическими инсертами — у меня сердце кровью обливается.
Но, как говорилось выше, API — это набор «сделай сам». Слишком многое приходится делать руками.
Те же подстановки имени поля, массивов для IN и SET. Чтобы INSERT с динамическим числом полей, приходится очень сильно попотеть. Я бы не назвал разницу между одной строкой и экраном кода словом «немного».
При этом я как-то не вижу какой-то особой «негибкости» у DbSimple. Есть конкретный пример того, что не смогла она, но смог чистый PDO?
Придраться можно только к двум вещам — довольно замороченный парсер с колбэками — у меня сделано гораздо проще.
И, конечно, слишком перегруженная функция query() — её сам бог велел разбить на несколько специализированных методов.
Предположу, что это рудимент с тех времён, когда класс ещё был функцией — судя по всему, был такой период в его биографии :)
Параметр, который определяет поведение функции, а не её результат, нужен только при процедурном подходе. При объектом удобнее будет разделить функционал на несколько методов.
Строка может быть как строкой, так и идентификатором.
Массив может быть передан как для in, так и для set.
К выбору людей, которые предпочитают построитель запросов я отношусь уважительно. Это отличная штука, и многие вещи позволяет сделать гораздо красивее, чем мой класс. Лично же для себя мнехочется сократить максимальную близость к SQL. Мне кажется, оба подхода имеют право на существование.
Для IN это логично — если мы передаём в IN пустой массив, то хотим получить строки, в которых есть соответствие переданным в массиве значениям. А если массив пустой, то и возвращать нечего.
SELECT * FROM id IN (NULL) так и поступает.
Но вот для запроса
SELECT * FROM id NOT IN (NULL) логично ожидать обратного поведения, но он тоже возвращает ноль строк.
Просто по пустому массиву вываливаться нельзя — запрос может быть «SELECT * FROM id IN (NULL) OR что-то» и прекрасно строки возвращать…
Действительно, хотелось бы разные.
Но у меня что-то пока никаких идей, как это реализовать…
Не стоит столь скептически относиться ко всему, что кажется вам банальным.
В любом деле развитие идёт по спирали, и со временем выходит на новый уровень.
Здесь ключевой момент — наличие плейсхолдеров для любого типа данных, который только может придти в запрос, а не только для самых ходовых, как в других решениях.
Куда менее обидно было бы услышать предметную критику, чем ленивый зевок «Знаем, плавали».
Но с критикой моего решения ты, пожалуй, прав. Не понимая принципа раздельного форматирования, невозможно и применять его. А это непонимание, похоже — и есть основная проблема.
А как дело доходит до реальной жизни, то все good practices почему-то всегда куда-то испряются из кода. И SQL шпарится напрямую, с подстановками переменных, из шаблонов обращаются к базе данных (а если шаблон не нативный — значит куча HTML-а пишется в контроллере), и так далее.
Возможно, это перфекционизм, но я последовательно копаю некоторые темы в веб-разработке, пытаясь покрыть 100% применения, а не только самые ходовые случаи.
В данном случае мне это удалось (во всяком случае, пока никто не доказал обратного). Но я провалил задачу донести результат до окружающих.
Спасибо за этот комментарий, он заставил меня активизировать работу по доведению класса до публичного состояния :)
Сравнение с популярными движками есть в этой статье — выше приводятся примеры кода, который нам бы понадобился для PDO и Mysqli. А в новой статье есть примеры того, как это делается в моей библиотеке.
dbsimple, похоже, для mysql не умеет — надо писать драйвер для mysqli.
Но, это, по сути, отдельные запросы. А речь именно о сборке единого.
Плюс, к примеру, в prepared statement невозможно подставить динамически имя поля.
У Котерова, пожалуй, понагляднее получается.
А как у него с множественным инсертом? :)
Но без поддержки различных типов данных она теряет половину своей прелести.
Поэтому главная идея — плейсхолдеры для всех встречающихся типов данных, а не только для строк и чисел. PDO не предоставляет никаких методов динамически добавить в запрос имя поля.
Idiorm — ORM. SQL, написанный на PHP.
Для них меня всегда интересуют не банальные примеры типа
а что-то более приближённое к реальности — какой-нибудь order by if(...), ON DUPLICATE…
Я вижу, там заявлена поддержка prepared statements — значит, кроме скаляров больше ничего от инъекций не защищается.
В моей практике чаще приходится добавлять элементы в запрос, чем вырезать их. Например:
Если один из стардартных API — PDO или Mysqli, то это не велик, а набор запчастей «сделай сам». На них, конечно, можно ездить, но только на одной передаче и очень часто крутя педалями. И руль заклинен в положении «прямо».
Database access wrapper писать в любом случае нужно, с голым API жить невозможно.
Если имеются в виду DAL и квери-билдеры различных фреймворков, то это тоже не фабричные велосипеды. И я бы с удовольствием с ними погонялся.