Класс для безопасной работы с MySQL

Фанат
Вот тебе ещё несколько идей для развития класса:

1. Имплементировать Serializable интерфейс (чтоб можно было спокойно сериализовывать настроенный экземпляр класса).
2. Коннектиться к БД не сразу, а в момент первого запроса.
3. Прикрутить логирование выполняемых запросов, ну или подумать как его будешь делать. Желательно иметь возможность программно получать список запросов, которые выполнялись, возможно с некоторыми характеристиками типа "время", "кол-во возвращённых строк".

Сможешь занять себе ещё на недельку вместо работы :D
 
Поддерживаю фиксера, плюс чтоб не было войны, плейсхолдеры заменять тока по условию, вызов больше чем с одним параметром заменяем плейсхолдеры, параметр тока один запрос - выполняем как есть.
Вообще проверку сколько плейсхолдеров, и сколько параметров переданно - считаю лишней. Ну выдаст ошибку база query error SELECT * FROM table WHERE x = 1 LIMIT 10,?i - ну понятно ж будет что забыл добавить в запрос.
Плюс возможность вот такого типа не будет лишней
PHP:
$data = $db->getAll("SELECT * FROM table WHERE ?q LIMIT ?i,?i", array ("one = ?s",$one), $start, $per_page);

function parse ($q) {
    $a = func_get_args ();
    if (count ($a) == 1) return $q;
    return $this->prepareQuery ($a);
}
    case '?q':
        if (is_array ($value)) call_user_func_array (array ($this, 'prepareQuery'), $value);
        break;
 
1. Имплементировать Serializable интерфейс (чтоб можно было спокойно сериализовывать настроенный экземпляр класса).
Честно говоря, я не вижу, что там сериализовать. Состояния у него, как такового, нету. А если логирование вводить, то тогда логи будут смешиваться. Честно говоря, я не вижу юзкейса. У тебя есть на примете какой-нибудь наглядный?
2. Коннектиться к БД не сразу, а в момент первого запроса.
Хорошая фича, но не первостепенная. Особенно для ЦА.
3. Прикрутить логирование выполняемых запросов, ну или подумать как его будешь делать. Желательно иметь возможность программно получать список запросов, которые выполнялись, возможно с некоторыми характеристиками типа "время", "кол-во возвращённых строк".
А вот логирование с профайлингом - как раз в ближайших планах, да. Я его выпилил из родительского класса, чтобы пока не мешало - оно там завязано на внутренние механизьмы.
Сможешь занять себе ещё на недельку вместо работы :D
Ну, впереди - пьяная неделя в январе. Хотя написание документации - куда более трудоемкий процесс :)
 
Честно говоря, я не вижу, что там сериализовать. Состояния у него, как такового, нету. А если логирование вводить, то тогда логи будут смешиваться. Честно говоря, я не вижу юзкейса. У тебя есть на примете какой-нибудь наглядный?
У меня, например, кешируется экзмпляр класса настроенного фреймворка на продакшене. Ну шоб быстрее было, шоб каждый раз не настраивать - выигрыш в основном получается за счёт отсутствия подгрузки конфигурационных данных. Экземпляр класса для работы с БД может быть свойством, которое должно будет сериализоваться. При сериализации нужно предусмотреть чтоб в строку не попадал ресурс коннекта к БД.
 
если честно, то я нихрена не понял.
Буду пробовать ещё
1) плейсхолдер ?q с подстановкой as-is
2) метод rawQuery, который - а не query() - теперь дергается во всех get() - и он не выполняет парсинг а просто выполняет данный ему запрос - иначе парсинг вызывается 2 раза всегда для get*-функций (хз как оно вообще у тебя работало ;) видимо ? вообще в данных не встречался)
 
1) плейсхолдер ?q с подстановкой as-is
2) метод rawQuery, который - а не query() - теперь дергается во всех get() - и он не выполняет парсинг а просто выполняет данный ему запрос - иначе парсинг вызывается 2 раза всегда для get*-функций
Так не будет работа половина функционала - query сама по себе парсит будьздоров - в этом вся фишка.
(хз как оно вообще у тебя работало ;) видимо ? вообще в данных не встречался)
Не-не-не, Девид Блейн!
в обычных условиях двойного парсинга не происходит. parse() никогда не вызывается для выполнения запроса
Смотри, у нас, грубо говоря, три функции
1. prepareQuery() - она парсит.
2. parse() - вызывает prepareQuery() и вызывает текст. Вызывается только для части запроса.
3. query() - вызывает prepareQuery() и исполняет запрос.

Если мы дёргаем query(), она дергает prepareQuery(), получает запрос и исполняет. Никакого двойного парсинга нет.
поэтому-то я и напихал в фак кучу примеров с кодом - том простой запрос с плейсхолдером ?u

а двойной парсинг - случай достаотчно редкий, применяемый только для сборных запросов. потому и не вылезало до сих пор.

Но смотри, что предложел C - это ж гениально! parse() искейпит знак вопроса - и всё! Так мы разом перебили всех зайцев.
единственный минус - портится запрос (с вопросами), который вывели через $parse для дебага. Ну так это не жалко.
 
Не, это то я все понял.
Дело в том, что если вызывать не query(), а скажем getAll() то prepareQuery() вызовется 2 раза - в самой getAll() и потом в query().
Вот этот двойной вызов я и убрал. И все работает, и не надо никаких эскейпов.
Вообще конечно надо бы юнит тестами покрыть, но тогда бы еще коннект вынести в отдельную функцию, а то мокать сложно.
 
Не, это то я все понял.
Дело в том, что если вызывать не query(), а скажем getAll() то prepareQuery() вызовется 2 раза - в самой getAll() и потом в query().
Вот этот двойной вызов я и убрал. И все работает, и не надо никаких эскейпов.
Вообще конечно надо бы юнит тестами покрыть, но тогда бы еще коннект вынести в отдельную функцию, а то мокать сложно.
ЁЖ ТЫ Ж МОЁ. и правда.
тогда я тоже не понимаю, как оно раньше работало.
 
вот черт.
в оригинальном классе всё нормально
а вынося в отдельный, я решил оптимизировать, дебил, заменив start() и getRes() одной - query().
 
К вопросу о рефакторинге без тестов =)

А raw-плейсхолдер нужен, иначе невозможно отличить, где мы подставляем уже распарсенный результат, а где собираем сам шаблон из кусочков (это ведь тоже может понадобиться). Эскейпить все плейсхолдеры - это извращение, мне кажется: типизированные плейсхолдеры для того и нужны, чтобы четко сказать, чего мы хотим, а не надеяться на какую-то магию внутри.
 
а где собираем сам шаблон из кусочков
Если собираем шаблон - то и не парсим - не?
или я не понял задачу. Приведи пример?

Эскейпить все плейсхолдеры
Мы искейпим не плейсхолдеры.
Мы тупо искейпим результат парсинга, в котором может встретиться знак вопроса

PHP:
$one = 'some text with ?s placeholders';
$where = $db->parse("one = ?s",$one); // one = 'some text with ??s placeholders'
$sql  = $db->getRow("SELECT * FROM table WHERE $where");
Вуаля - ноль ошибок!

или я опять тебя не понял?
 
edited: туплю, вроде все нормально получается

да, можно так )
 
Чота я не понял.. Это создается DbSimple с расширенными плейсхолдерами (?d, ?s etc..)? Я для "безопасности" лет семь назад дальше пошел - выносил шаблоны запросов вообще за пределы кода, где они вызываются. То есть запросы у меня были типа $db->fetchAll('path.to.file.with.any.queries.my_query',$a,$b,$c);

Имхо, провальная затея.

Хомо-саппиенс-неразумный даже услышав совет использовать эту библиотеку, будет в случае проблем гуглить и юзать найденные запросы с mysql_query

А разумному будет реально все-равно, составлять по документации запросы в конструкторе в рамках фреймворка, эскейпить переменные при вставке в mysql_query или юзать любую нормальную библиотеку

Или я что-то недопонял?
 
Crys разумному тимлиду будет реально НЕ все-равно во что ткнуть носом свою стаю обезьян
мне нравятся типизированные плейсхолдеры
 
Вуаля - ноль ошибок!
Нет, тогда в обычном запросе знаки вопроса удвоятся.

PHP:
$one = 'some text with ?s placeholders';
$sql  = $db->getRow("SELECT * FROM table WHERE one = ?s", $one); //SELECT * FROM table WHERE one =  'some text with ??s placeholders'

Можно разбить на две функции: одна для сборки части запроса, а другая - для сборки запроса целиком. Но тогда нужно будет каждый раз думать, какую из них вызывать. Особенно интересно станет, когда понадобится собрать большой и сложный запрос, а не тривиальный select. Ведь один кусочек может входить в другой кусочек.
 
Нет, тогда в обычном запросе знаки вопроса удвоятся.
Это-то нам и надо!
У нас вопрос изначально должен быть удвоен - parseQuery оставляет от удвоенного один.
Так что кругом профит!

В то, что сам знак вопроса нужно экранировать, меня ткнул носом Вурдалак ещё в прошлом топике, полтора года назад.
То есть, правила, на самом деле, ТРЕБУЮТ, чтобы в шаблоне запросе все вопросительные знаки были удвоены.
поскольку parse() добавляет ШАБЛОНУ - то она и должна удваивать. А она этого не делала.
Так что теперь всё сошлось, все кусочки пазла
 
Пример:
PHP:
$one = 'some text with ?s placeholders';

$where = $db->parse('one = ?s', $one);                           //some text with ??s placeholders
$sub_query = $db->parse('SELECT * FROM table WHERE ' . $where);  //some text with ????s placeholders
$db->getRow("SELECT * FROM ($sub_query) a WHERE 1");             //some text with ??s placeholders

Т.е. с каждым последовательным вызовом parse() вопросики будут размножаться.
А их stip будет происходить только один раз.

Хорошо, если я ошибаюсь, и проблемы реально нет.
Commit it. Код лучше тысячи слов. :)
 
Можно разбить на две функции: одна для сборки части запроса, а другая - для сборки запроса целиком.
Ну, собственно, Фикс всё и сделал своим патчем, только дошло это до меня сильно не сразу. Но оно уже тогда работало :)
raw-плейсхолдер и является той самой "функцией для разборки частей запроса"!
В итоге код http://www.phpfaq.ru/examples#multiparse отрабатывает корректно!
 
Сверху