Неплохо. Уже второй раз встречаю такой подход — добавить в плейсхолдер информацию о повторении [через запятую].
В общем, идея типизованных плейсхолдеров идёт в массы, и это не может не радовать. Может, через годик-другой увидим в мажорных фреймоворках, а там, глядишь, и до официальных API недалеко :)
Здесь ужасен не код, а подход.
Все эти кульбиты с НТТР заголовками имеют смысла чуть менее, чем нисколько.
Прочтите статью до конца.
Прочтите ссылку на phpfaq.ru, что дана выше.
Прочтите, наконец, статью в стиле «Скандалы, интриги, расследования», если простые объяснения не помогают: habrahabr.ru/post/158417/
Рассказывая о прелестях prepared statements, стоит сразу же рассказать, что делать, если в $_GET['sync3'] лежит массив id, которые должны быть подставлены в оператор IN(), и заодно — что делать, если с клиента задаётся имя поля для сортировки запроса.
В принципе, я согласен с этой позицией.
Здесь, скорее, уже стремление к перфекционизму. «Неаккуратненько» получается, и хочется исправить :)
Но спасибо за поддержку. Я, пожалуй, подожду ваять какие-то сильно усложняющие код варианты ради такого, прямо скажем, исключительного случая.
Удивительно! Действительно, очень совпадает.
Это уже третья библиотека, использующая подобный подход, на которую указали в комментах — и я не видел до этого ни одной. Статью стоило писать только для этого :)
У меня класс тоже сначала был функцией, как там :)
Но это, всё-таки, совсем неудобно. Хотя по сравнению с ручной сборкой запросов это всё равно огромный шаг вперёд
В принципе да, так можно, конечно. Но я бы не выделял работу с подзапросами отдельно.
Идея в том, чтобы таким образом обрабатывать любые элементы SQL — запросы, подзапросы, дополнительные условия.
Если придётся изучать — обязательно спрашивайте — подробно отвечу, как лучше сделать!
Я согласен с тем, что некоторые вещи на билдере выглядят элегантнее. Главное отличие в том, что доступ к элементам запроса произвольный, а не последовательный, как при сборке запроса руками.
Можно приготовить объект заранее, а потом менять ему условия. Это здорово удобно. Вот это как раз безоговорочное преимущество билдеров, которое я не оспариваю.
Но в случае достаточно сложных запросов код у них становится совершенно нечитабельным, и большинство известных мне разработчиков в таких случаях отказываются от билдера и пишут запрос вручную.
Кстати, PDO по умолчанию тоже использует не prepared statements, а форматирование на клиенте. Так что любые упрёки в небезопасности данного подхода предлагаю адресовать сначала им :)
Спасибо за развёрнутый комментарий. Он требует времени на написание развернутого ответа, поэтому пока только по нескольким пункту:
Для pg — вот ниже привели ссылку на библиотеку, реализующую тот же самый принцип — pyha.ru/go/godb/
в которой есть драйвер для Постгре. Но в любом случае, если появится драйвер (потребуется переопределить функции, взаимодействующие с API и методы искейпинга), то это будет просто замечательно.
идея типизованных плейсхолдеров приходит нам в голову ровно в тот момент, когда мы осознаём, что типов форматирования элементов запроса оказывается сильно больше одного (а в PDO по умолчанию таки и используется один — строковый, когда мы тупо закидываем в execute() массив, без ручной привязки). Так что очень рекомендую :)
По каждому пункту могу высказать совершенно обратное, поэтому без аргументов это всего лишь слова…
Да что тут можно высказать-то?
Мне казалось, это очевидно из моих слов.
Пользу не приносят потому, что в 99.99% случаев у нас нет повторяющихся запросов в скриптах — значит, зря по два раза дергаем базу. Затрудняют отладку — опять же я выше уже писал аргумент — получить канонический запрос для отладки весьма проблематично.
При этом корректно отформатированное значение ничуть не менее безопасно, чем prepared statement. Следовательно, никаких преимуществ перед плейхолдером, обрабатываемым на клиенте, у prepared statements нет. А минусы — есть.
Если не согласны — приведите конкретный пример.
Единственный случай, когда prepared могут проявить себя — это микроскопический прирост в скорости, когда нам надо выполнить 100500 одинаковых запросов в консольном скрипте. Случай настолько исключительный, что лично я предпочитаю им пренебречь. И тот факт, что столь высоконагруженный проект как Badoo, не использует prepared statements, говорит нам о том, что слухи об увеличенной производительности несколько преувеличены.
Ну вот вам пример с датами: в базе поле datetime а вы в скрипте оперируете unix_timestamp-ом
Ради бога. Какое это имеет отношение к database layer?
Вы переворачиваете всё сног на голову. Вопрос был, как подставить значение в запрос. Ну так значение подставляется через плейсхолдер. Хотим подставить дату — ?s. Хотим добавить целое число секунд — ?i. Никаких проблем такие подстановки не вызывают, и более того — являются единственно возможным способом форматирования данных для соответствующих типов полей.
Отдельный «плейсхолдер для дат», разумеется, не нужен. Исходя из приведённого примера, он может понадобиться разве что для магии, чтобы волшебным образом догадаться, что из int-а надо сделать строку. Это неправильный подход. Но даже если он кому-то и нравится, то к работе с SQL это не имеет ни малейшего отношения.
Тут есть только один нюанс.
По-хорошему, вайтлистить надо только операторы — AND, DESC и прочее.
Поскольку не существует других методов их валидации.
В то время как идентификатор можно правильно отформатировать (что и делает приведенный мной код).
Заниматься же проверкой на валидность значения, должен не уровень работы с БД, а уровень работы с внешними данными — контроллер.
Задача же драйвера БД — только синтаксически корректно оформить запрос.
Поэтому проверка идентификаторов по белому списку на этапе составления запроса — это костыль, на случай, если это не было сделано раньше.
А, понятно. Вы говорите о билдере.
Это было очевидно с самого начала, но я был занят своими мыслями. Прошу прощения.
Да, для билдера, наверное, тип указывать не обязательно.
Я там выше сказал, что билдер — отдельный вариант, который имеет своих приверженцев, против которых я ничего не имею.
При этом необходимость указывать тип данных не считаю такой уж большой проблемой, особенно если взамен получаю чистый и наглядный SQL, который я уже знаю и который является lingua franca для разработчиков — его понимают все, в отличие от отдельно взятого билдера, который понимают только его адепты.
но почему теперь пишете
А, тут все просто. Prepared statement — не синоним плейсхолдера — вот тут я как раз специально написал об этом. Так что я не использую prepared statements, но очень активно использую плейсхолдеры — они являются основой всего класса.
По поводу типов: а как же enum, datetime, timestamp
А что с ними не так? Для них вполне подходит ?s. Можете привести пример обратного?
Да и вообще тут надо было просто использовать mysql_escape_string.
Это такое чудовищное заблуждение, что я даже комментировать не буду.
Чудовищное потому, что в это верят миллионы пользователей пхп.
(и, кстати, по этой же самой причине они считают мой подход бессмысленной поделкой)
Тут тоже вижу выход.
Ну разумеется, есть выход. Всё делается, с помощью кувалды и такой-то матери. Выше я привел пример как можно обойтись ещё меньшим кодом.
Изначально-то речь шла о том, что PDO никак не помогает в таких задачах. Ну, а дальше вылезло ваше непонимание того, как работают prepared statements — что произвольный кусок запроса в них запихнуть нельзя, только отдельный литерал.
чтобы подставить в конец строки правильный order by
Это не извращение, а как раз очень правильный подход.
Фильтрация по белым спискам — единственно правильное решение.
Мой код выше, хоть и не пропустит инъекцию, но вызовет ошибку запроса. Чтобы её не было, надо как раз проверять поле по заранее прописанному списку.
Вот здесь phpfaq.ru/examples#whitelist как раз показаны примеры правильного подхода к фильтрации.
БД простила мне запрос с ORDER BY '`comments`'
а вот это меня тоже удивило. Но, в любом случае, от инъекции это не спасёт.
Честно говоря, я не очень люблю тесты скорости. На моей памяти DAL ещё никогда не был узким местом.
Но, пожалуй, надо будет сделать. Как раз, сделать нормальный тест.
Небольшую страничку, на которой выполняется штук 5 стандартных запросов. Юзера там авторизовать, новости вывести.
И натравить на неё siege.
И, хоть я и уверен, что статистически значимое различие получить не удастся, цифры в любом случае будут красноречивее слов.
Всё-таки, при нынешних ценах на процессоры затраты на парсинг ничтожны.
Недавно сравнивал PDO в режиме эмуляции, и без. Разница составила 3 десятитысячные доли секунды на запрос. Три миллисекунды на страницу. При том, что затраты на исполнение самих запросов на порядок выше. Поэтому я буду оптимизировать запросы, а не обертку для их запуска.
Вообще, такие вещи лучше смотреть на живой базе, оно получается нагляднее.
С другой стороны, отладка запросов в PDO — ад, и винить разработчика за то, что он не может тупо посмотреть сгенерированный запрос, нельзя.
Поэтому будем экстраполировать сами
SELECT * FROM `news` WHERE `theme` IN('1,2,3') ORDER BY `'comments'`
В приведённом коде две пары лишних кавычек. Поэтому сначала база выдаст ошибку, что нет такого поля — 'comments' (прямо с кавычками), а потом, если решить эту проблему (решение есть в моем коде) — найдет новости только из первой категории.
Чтобы кошерно составить запрос по правилам PDO, надо делать плейсхолдер на кадждое подставляемое значение, чтобы запрос выглядел
SELECT * FROM `news` WHERE `theme` IN(?,?,?) ORDER BY `comments`
В общем, идея типизованных плейсхолдеров идёт в массы, и это не может не радовать. Может, через годик-другой увидим в мажорных фреймоворках, а там, глядишь, и до официальных API недалеко :)
Все эти кульбиты с НТТР заголовками имеют смысла чуть менее, чем нисколько.
Прочтите статью до конца.
Прочтите ссылку на phpfaq.ru, что дана выше.
Прочтите, наконец, статью в стиле «Скандалы, интриги, расследования», если простые объяснения не помогают: habrahabr.ru/post/158417/
ковырять в носуиспользовать globalЛично мне sprintf здесь — не пришей кобыле хвост.
Да и синтаксис пхп не худо бы соблюдать.
В словаре плейсхолдеров нет, а глаз у меня замылился. Отлично получилось, буду вводить в обиход :-)
Здесь, скорее, уже стремление к перфекционизму. «Неаккуратненько» получается, и хочется исправить :)
Но спасибо за поддержку. Я, пожалуй, подожду ваять какие-то сильно усложняющие код варианты ради такого, прямо скажем, исключительного случая.
Это уже третья библиотека, использующая подобный подход, на которую указали в комментах — и я не видел до этого ни одной. Статью стоило писать только для этого :)
У меня класс тоже сначала был функцией, как там :)
Но это, всё-таки, совсем неудобно. Хотя по сравнению с ручной сборкой запросов это всё равно огромный шаг вперёд
Идея в том, чтобы таким образом обрабатывать любые элементы SQL — запросы, подзапросы, дополнительные условия.
Если придётся изучать — обязательно спрашивайте — подробно отвечу, как лучше сделать!
Ведь речь не только о числах. но и о любых данных. и пустая строка вполне может быть законным значением…
Можно приготовить объект заранее, а потом менять ему условия. Это здорово удобно. Вот это как раз безоговорочное преимущество билдеров, которое я не оспариваю.
Но в случае достаточно сложных запросов код у них становится совершенно нечитабельным, и большинство известных мне разработчиков в таких случаях отказываются от билдера и пишут запрос вручную.
Для pg — вот ниже привели ссылку на библиотеку, реализующую тот же самый принцип — pyha.ru/go/godb/
в которой есть драйвер для Постгре. Но в любом случае, если появится драйвер (потребуется переопределить функции, взаимодействующие с API и методы искейпинга), то это будет просто замечательно.
идея типизованных плейсхолдеров приходит нам в голову ровно в тот момент, когда мы осознаём, что типов форматирования элементов запроса оказывается сильно больше одного (а в PDO по умолчанию таки и используется один — строковый, когда мы тупо закидываем в execute() массив, без ручной привязки). Так что очень рекомендую :)
Да что тут можно высказать-то?
Мне казалось, это очевидно из моих слов.
Пользу не приносят потому, что в 99.99% случаев у нас нет повторяющихся запросов в скриптах — значит, зря по два раза дергаем базу. Затрудняют отладку — опять же я выше уже писал аргумент — получить канонический запрос для отладки весьма проблематично.
При этом корректно отформатированное значение ничуть не менее безопасно, чем prepared statement. Следовательно, никаких преимуществ перед плейхолдером, обрабатываемым на клиенте, у prepared statements нет. А минусы — есть.
Если не согласны — приведите конкретный пример.
Единственный случай, когда prepared могут проявить себя — это микроскопический прирост в скорости, когда нам надо выполнить 100500 одинаковых запросов в консольном скрипте. Случай настолько исключительный, что лично я предпочитаю им пренебречь. И тот факт, что столь высоконагруженный проект как Badoo, не использует prepared statements, говорит нам о том, что слухи об увеличенной производительности несколько преувеличены.
Ради бога. Какое это имеет отношение к database layer?
Вы переворачиваете всё сног на голову. Вопрос был, как подставить значение в запрос. Ну так значение подставляется через плейсхолдер. Хотим подставить дату — ?s. Хотим добавить целое число секунд — ?i. Никаких проблем такие подстановки не вызывают, и более того — являются единственно возможным способом форматирования данных для соответствующих типов полей.
Отдельный «плейсхолдер для дат», разумеется, не нужен. Исходя из приведённого примера, он может понадобиться разве что для магии, чтобы волшебным образом догадаться, что из int-а надо сделать строку. Это неправильный подход. Но даже если он кому-то и нравится, то к работе с SQL это не имеет ни малейшего отношения.
По-хорошему, вайтлистить надо только операторы — AND, DESC и прочее.
Поскольку не существует других методов их валидации.
В то время как идентификатор можно правильно отформатировать (что и делает приведенный мной код).
Заниматься же проверкой на валидность значения, должен не уровень работы с БД, а уровень работы с внешними данными — контроллер.
Задача же драйвера БД — только синтаксически корректно оформить запрос.
Поэтому проверка идентификаторов по белому списку на этапе составления запроса — это костыль, на случай, если это не было сделано раньше.
Это было очевидно с самого начала, но я был занят своими мыслями. Прошу прощения.
Да, для билдера, наверное, тип указывать не обязательно.
Я там выше сказал, что билдер — отдельный вариант, который имеет своих приверженцев, против которых я ничего не имею.
При этом необходимость указывать тип данных не считаю такой уж большой проблемой, особенно если взамен получаю чистый и наглядный SQL, который я уже знаю и который является lingua franca для разработчиков — его понимают все, в отличие от отдельно взятого билдера, который понимают только его адепты.
А, тут все просто. Prepared statement — не синоним плейсхолдера — вот тут я как раз специально написал об этом. Так что я не использую prepared statements, но очень активно использую плейсхолдеры — они являются основой всего класса.
А что с ними не так? Для них вполне подходит ?s. Можете привести пример обратного?
Это такое чудовищное заблуждение, что я даже комментировать не буду.
Чудовищное потому, что в это верят миллионы пользователей пхп.
(и, кстати, по этой же самой причине они считают мой подход бессмысленной поделкой)
Ну разумеется, есть выход. Всё делается, с помощью кувалды и такой-то матери. Выше я привел пример как можно обойтись ещё меньшим кодом.
Изначально-то речь шла о том, что PDO никак не помогает в таких задачах. Ну, а дальше вылезло ваше непонимание того, как работают prepared statements — что произвольный кусок запроса в них запихнуть нельзя, только отдельный литерал.
Это не извращение, а как раз очень правильный подход.
Фильтрация по белым спискам — единственно правильное решение.
Мой код выше, хоть и не пропустит инъекцию, но вызовет ошибку запроса. Чтобы её не было, надо как раз проверять поле по заранее прописанному списку.
Вот здесь phpfaq.ru/examples#whitelist как раз показаны примеры правильного подхода к фильтрации.
а вот это меня тоже удивило. Но, в любом случае, от инъекции это не спасёт.
Честно говоря, я не очень люблю тесты скорости. На моей памяти DAL ещё никогда не был узким местом.
Но, пожалуй, надо будет сделать. Как раз, сделать нормальный тест.
Небольшую страничку, на которой выполняется штук 5 стандартных запросов. Юзера там авторизовать, новости вывести.
И натравить на неё siege.
И, хоть я и уверен, что статистически значимое различие получить не удастся, цифры в любом случае будут красноречивее слов.
Всё-таки, при нынешних ценах на процессоры затраты на парсинг ничтожны.
Недавно сравнивал PDO в режиме эмуляции, и без. Разница составила 3 десятитысячные доли секунды на запрос. Три миллисекунды на страницу. При том, что затраты на исполнение самих запросов на порядок выше. Поэтому я буду оптимизировать запросы, а не обертку для их запуска.
С другой стороны, отладка запросов в PDO — ад, и винить разработчика за то, что он не может тупо посмотреть сгенерированный запрос, нельзя.
Поэтому будем экстраполировать сами
В приведённом коде две пары лишних кавычек. Поэтому сначала база выдаст ошибку, что нет такого поля — 'comments' (прямо с кавычками), а потом, если решить эту проблему (решение есть в моем коде) — найдет новости только из первой категории.
Чтобы кошерно составить запрос по правилам PDO, надо делать плейсхолдер на кадждое подставляемое значение, чтобы запрос выглядел
Именно это и делает код, который я привел выше