Задать вопрос

Как в PDO значение LIMIT при запросе сделать INT?

Как мне сделать значение LIMIT числовым INT?

Например запросы:
SELECT `id` FROM `users` WHERE BINARY `mail` = 'mail@mail.ru' AND `pass` = '12345'   LIMIT 1
SELECT `name` FROM `news` WHERE  `category` = 'Авто' AND `subcategory` = 'Легковые'   LIMIT 40

Т.е. я не знаю по скольким ячейкам будет вестись выборка...по 1, по 2, по 3 или по 7....

Например, по одной понятно...
$mail = 'mail@mail.ru';
$limit = '1';
$stmt = $this -> db -> prepare(SELECT `id` FROM `users` WHERE BINARY `mail` = ? LIMIT ?);
$stmt->bindParam(1, $mail);
$stmt->bindParam(2, $limit, PDO::PARAM_INT);
$stmt -> execute();
$row = $stmt -> fetchColumn();
$print_r($row);

А вот как сделать это со множеством?
Цикл с подстановкой в bindParam?
Я читал, что можно еще отключить режим эмуляции, но я не знаю, как это повлияет на безопасность и повлияет ли вообще...
  • Вопрос задан
  • 5960 просмотров
Подписаться 3 Оценить Комментировать
Решения вопроса 1
FanatPHP
@FanatPHP
Чебуратор тега РНР
Хороший вопрос. Как раз показывает убогость стандартной системы плейсхолдеров.
В моей библиотеке для работы c MySQL тип указывается самым простым и эффективным способом - прямо в плейсхолдере:
//первый запрос (лимит здесь не нужен)
$id = $db->getOne("SELECT id FROM users WHERE mail = ?s AND pass = ?s", $mail, $pass);
// второй запрос
$sql  = "SELECT id, name FROM news WHERE category = ?s AND subcategory = ?s LIMIT ?i";
$news = $db->getIndCol('id', $sql, $cat, $subcat, $limit);

Как видно из этих примеров, тип ставится в самом плейсхолдере и все входящие данные обрабатываются корректно.

Вернемся теперь к несчастному PDO.
Цикл с подстановкой в bindParam?

Цикл, увы, не поможет. Потому что мы не знаем, какой тип использовать для привязки. То есть, все сведется к дефолтному PARAM_STR и в итоге мы получим то же самое execute() с массивом, только в профиль.
Если вдруг возникнет идея определять тип по составу переменной, то делать это НИ В КОЕМ СЛУЧАЕ НЕЛЬЗЯ. Если бы мог, я бы выделил ещё большим шрифтом и красным цветом. Потому что практически каждый продвинутый пользователь похапе рано или поздно наступает на эти грабли. Если число, хранящееся в MySQL, всегда можно безопасно сравнивать со строкой, то наоборот - это будет катастрофа: MySQL будет пытаться привести содержимое поля к числу. То есть, если взять пример из вопроса, и пытаться определить тип привязки по содержимому переменной, то при введенном пароле 12345 ctype_digit() скажет нам использовать INT и в итоге пароль 12345 подойдет к любому паролю вида "12345буквы".
Так что цикл - не вариант.

Я читал, что можно еще отключить режим эмуляции, но я не знаю, как это повлияет на безопасность

Режим эмуляции на безопасность не влияет. Некоторые неграмотные разработчики считают, что отключение режима эмуляции повышает безопасность, но это неправда. В обоих вариантах PDO работает одинаково безопасно.
В любом случае, отключение эмуляции действительно решает проблему. Так что в данном случае это будет самое простое решение.

Так что либо в параметрах DSN, либо с помощью
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);

отключаем эмуляцию и наслаждается лимитом (правда, в этом случае перестанет работать такая фича, как несколько именованных плейсхолдеров с одним и тем же именем, но тут уж приходится выбирать).

И последнее замечание.
Функцию для выполнения запросов писать в общем-то нет смысла. PDO достаточно лаконичен и сам по себе. Единственное, что мешает писать однострочники - это дурацкая execute(), которая возвращает не себя, а булево значение. Но это легко исправить, и в итоге код получится ненамного длиннее, чем при вызове функции, но гораздо более гибким (ненавижу скроллинг, поэтому выношу параметры на другие строчки):
// с функцией
$sql   = 'SELECT `user_pass` FROM `users` WHERE `user_mail` = :mail LIMIT :lim';
$param = array(':mail' => 'vlad-dub1994@mail.ru', ':lim' => 1);
$data  = select($sql,$data);
// с патченым PDO
$data = DB::prepare($sql)->execute($data)->fetch();

// или другой вариант записи
$data = DB::prepare('SELECT user_pass FROM users WHERE user_mail = :mail LIMIT :lim')
	->execute([':mail' => 'vlad-dub1994@mail.ru', ':lim' => 1])
	->fetch();

Всего на пару слов больше, но зато можно исполнять любые запросы (INSERT к примеру):
$sql   = 'INSERT INTO users VALUES(?,?,?)';
$param = array(NULL, 'vlad-dub1994@mail.ru', 'pass');
DB::prepare($sql)->execute($data); //OK
select($sql,$data); // ошибка из-за fetch()

и использовать любые варианты получения данных, которые поддерживает PDO:
$sql = 'SELECT id FROM tree WHERE parent_id=?';
$subcat = DB::prepare($sql)->execute([$parent])->fetchAll(PDO::FETCH_COLUMN);
Ответ написан
Пригласить эксперта
Ответы на вопрос 3
Комментировать
alexclear
@alexclear
A cat
Сегодня день вопросов, которых я не понимаю.
Какой цикл с подстановкой? Цикл с подстановкой чего куда? LIMIT и так является значением типа Int.
Что именно Вы хотите с ним сделать? Из вопроса непонятно ничего.
Ответ написан
Я тоже не очень понял, вас случайно не это интересует? stackoverflow.com/questions/920353/can-i-bind-an-a...
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы