Пятница. Говнокод. Парсинг плейсхолдеров.

Фанат

oncle terrible
Команда форума
это проблема когда надо сделать
PHP:
'field2'=>$param? : 'NOW()',
и хез что тут придумать
Вообще, идеология класса такова, что для любых исключительных случаев, которые встречаются раз в сто лет, мы не класс усложняем для каждого из таких случаев, а пишем небольшую программку на РНР:
PHP:
if ($param) {
   $field2 = $db->parse("field2 = ?",$param);
} else {
   $field2 = "field2 = now()";
}
$sql = "INSERT INTO ?n SET ?p, ?u";
Оно, конечно, говнокод, но, на мой взгляд, это тот случай, когда от оправдан
 

hell0w0rd

Продвинутый новичок
стоит разделить либу для генерации запироса и парсер: либа может ограничивать разработчика ради безопасности всего решения, а парсер все-таки ограничивать не должен,
как минимум DI для парсера в либе сделать стоит, чтобы парсер можно было переопределить

меня злит, когда я вижу в движке код вида return new CDBDriver(); и должен переписать пол-фреймворка чтобы добавить поддержку репликации
ЭЭ)) Это простая либа для новичков, или очень простых проектов зачем ее так усложнять?)
Ну и не особо понимаю, почему не хочется юзать замыкания для нестандартного парсинга, по хорошему инициализация библиотечки должна происходить в одном месте и если кому-то непонятно почему плейсхолдер парсится именно так - идет и смотрит в это место
 
  • Like
Реакции: AmdY

Фанат

oncle terrible
Команда форума
ЭЭ)) Это простая либа для новичков, или очень простых проектов зачем ее так усложнять?)
Идея как раз в том, чтобы реализовать принцип типизованных плейсхолдеров (который сам по себе гениален и является единственным вариантом сделать работу с SQL безопасной и удобной) в виде либы для не новичков. И вот как раз Григорий и предлагает свое видение такой либы.

Я, правда, так и не понял, как разделить парсер и либу. что передавать из парсера? Объект? Как это всё соединять потом?
 

hell0w0rd

Продвинутый новичок
Фанат
PHP:
$query = $this->get('parser')->parse($query);
var_dump($query->getStatement());
var_dump($query->getArgs());
А вообще вот тут pdo выступает в роли парсера, который отдает инфу о типах плейсхолдеров statement-у, а тот при execute их подставляет
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
ЭЭ)) Это простая либа для новичков, или очень простых проектов зачем ее так усложнять?)
нет, это будущая часть фреймворка
Ну и не особо понимаю, почему не хочется юзать замыкания для нестандартного парсинга, по хорошему инициализация библиотечки должна происходить в одном месте и если кому-то непонятно почему плейсхолдер парсится именно так - идет и смотрит в это место
взаимоисключающие фразы: возможны или замыкания, или инициализация в одном месте, потому как поиск замыканий по приложению - это писец котенку
 

hell0w0rd

Продвинутый новичок
grigori
Роуты в микрофреймворках тоже можно просунуть из любой точки приложения, но никто этим же не занимается?) Да и все идет через 1 функцию, если нужно отследить - делается элементарно
 

riff

Новичок
У Фаната(автора) есть защита
* от несовпадений кол-ва неименованных параметров,
* от отсутствия именованного параметра,
* от несовпадения типа, указанного в плэйсхолдере(число) и переданного параметра,
* от смешивания именованных и неименованных плэйсхолдеров.
(это то, что мне помогало реально)

Нет защиты от таково: "WHERE bar = '"it's a trap?" OR bar = ?". т.е. когда один из плейхолдеров таким не является.
(Я лично считаю, что и не надо. Пишешь на плейсхолдерах, ну и пиши на них полностью)
А, ну или, как сказали ниже, эскейпить ("it's a trap\?"). В парсер в регулярку надо будет добавить условие "[^\\]..." А потом ещё скажут "а вдруг "\" это часть строки, надо и её эскейпить.

Тип плэйсхолдера указывается сразу в тексте, в отличии от этого чудища
'SELECT * FROM Foo WHERE foo IN (?, ?, ?)',
array(1, 2, 3),
array(\PDO: PARAM_INT, \PDO: PARAM_INT, \PDO: PARAM_INT)

"SELECT * FROM Foo WHERE foo IN ( :foo) OR bar = :bar OR baz = :baz",
array('foo' => array(1, 2), 'bar' => 'bar', 'baz' => 'baz'),
array('foo' => Connection: PARAM_INT_ARRAY, 'baz' => 'string'),
 
Последнее редактирование:

Breeze

goshogun
Команда форума
Партнер клуба
Я лично считаю, что и не надо. Пишешь на плейсхолдерах, ну и пиши на них полностью
тем более, что эскейпинг значимых символов никто не отменял
 

Koc

Новичок
У Фаната(автора) есть защита
* от несовпадений кол-ва неименованных параметров,
* от отсутствия именованного параметра,
* от несовпадения типа, указанного в плэйсхолдере(число) и переданного параметра,
* от смешивания именованных и неименованных плэйсхолдеров.
(это то, что мне помогало реально)
это все оочень легко реализовать. Другое дело, что б те тесты корректно отрабатывали.

ps: да, это убого отдельными параметрами передавать тип подставляемого значения. И я даже сам когда-то писал такие парсеры и 2.5 года назад предлагал добавить это в доктрину http://www.doctrine-project.org/jira/browse/DBAL-99 ... Но вопрос не в этом, а в том, проходит ли предложенный парсер те тесты - нет, не проходит.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
Господа, а вот такие тесты https://github.com/doctrine/dbal/blob/master/tests/Doctrine/Tests/DBAL/SQLParserUtilsTest.php#L16 ваши парсеры плейсхолдеров проходят?
Мой скорее всего пройдёт, но он является портом лексера и (небольшой части) парсера грамматики PostgreSQL на похапэ. И тип для типизированного плейсхолдера там можно задавать как
Код:
cast($1 as integer[]), $2::point
что, во-первых, правильно понимается самим Postgres'ом, во-вторых, будет преобразовано парсером в конструкцию типа
PHP:
new TypecastExpression(
    new Parameter(1),
    new Typename(...)
),
из которой можно получить всю необходимую информацию для обработки на стороне похапэ.
 

Фанат

oncle terrible
Команда форума
Кос-у я ответить не готов, а вот на это
тип для типизированного плейсхолдера там можно задавать как
ответить просто.
тип нужен не для скаляров, которым, в общем, и так хорошо, а для сложных и комплексных типов
PHP:
cast($1 as identifier)
ведь не сделаешь...
А весь смысл пипизованных именно в том, что на самом деле число типов попадающих в запрос элементов довольно велико.
 

Sad Spirit

мизантроп (Старожил PHPClub)
Команда форума
тип нужен не для скаляров, которым, в общем, и так хорошо, а для сложных и комплексных типов
так у меня в примере как раз integer[], что следует читать как "массив значений типа integer", и point, состоящий из двух координат.

Есессно, это только половина проблемы, вторая половина --- генерация строковых литералов для этих типов. Но на эту часть уже есть даже готовые решения.

PHP:
cast($1 as identifier)
ведь не сделаешь...
что такое здесь identifier? Имя сложного типа, созданного через CREATE TYPE? Имя таблицы? Запросто сделаешь.

А весь смысл пипизованных именно в том, что на самом деле число типов попадающих в запрос элементов довольно велико.
дык.

что касается тестов, предложенных Кос'ом, то для них парсер-то не особо нужен. Но полноценный лексер написать, всё же, стоит.
 

Фанат

oncle terrible
Команда форума
Господа, а вот такие тесты https://github.com/doctrine/dbal/blob/master/tests/Doctrine/Tests/DBAL/SQLParserUtilsTest.php#L16 ваши парсеры плейсхолдеров проходят?
Ну, поскольку именованные плейсхолдеры поддерживают только форки, а основная либа - нет, то и большая часть тестов неприменима.
Разве что вот с этим вопросом.
по-хорошему надо конечно сделать.
Другое дело - как часто строка с вопросом встречается в запросах?..
 

Вурдалак

Продвинутый новичок
Код:
SELECT ':param',
очень легко обработать, preg_split() регуляркой, захватывающая строковой литерал и делать замену плейсхолдеров только в чётных (нечётных?) элементах полученного массива.
 

Фанат

oncle terrible
Команда форума
Код:
SELECT ':param',
очень легко обработать, preg_split() регуляркой, захватывающая строковой литерал и делать замену плейсхолдеров только в чётных (нечётных?)
Кто-то мне даже её писал, я сегодня искал, но безуспешно.
В нее ещё по-хорошему быктики добавить - и совсем никто не подкопается. вроде
 
Сверху