Как применить метод связыванияValue в предложении LIMIT?
Вопрос
Вот снимок моего кода:
$fetchPictures = $PDO->prepare("SELECT *
FROM pictures
WHERE album = :albumId
ORDER BY id ASC
LIMIT :skip, :max");
$fetchPictures->bindValue(':albumId', $_GET['albumid'], PDO::PARAM_INT);
if(isset($_GET['skip'])) {
$fetchPictures->bindValue(':skip', trim($_GET['skip']), PDO::PARAM_INT);
} else {
$fetchPictures->bindValue(':skip', 0, PDO::PARAM_INT);
}
$fetchPictures->bindValue(':max', $max, PDO::PARAM_INT);
$fetchPictures->execute() or die(print_r($fetchPictures->errorInfo()));
$pictures = $fetchPictures->fetchAll(PDO::FETCH_ASSOC);
я получил
У вас есть ошибка в синтаксисе SQL;Проверьте руководство, которое соответствует вашей версии MySQL Server для правильного синтаксиса, чтобы использовать около '15', 15 'в строке 1
Кажется, что PDO добавляет одинарные кавычки к моим переменным в части LIMIT кода SQL.Я посмотрел и нашел эту ошибку, которая, я думаю, связана с:http://bugs.php.net/bug.php?id=44639
Это то, на что я смотрю?Этот баг открыт с апреля 2008 года!Что нам тем временем делать?
Мне нужно создать разбиение на страницы и убедиться, что данные чисты и безопасны для инъекций sql, прежде чем отправлять оператор sql.
Решение
Я помню, что у меня была эта проблема раньше.Приведите значение к целому числу, прежде чем передавать его в функцию привязки.Я думаю, это решает проблему.
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
Другие советы
Самым простым решением было бы выключи режим эмуляции.Вы можете сделать это либо как вариант подключения, либо просто добавив следующую строку
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Это не только решит вашу проблему с параметром привязки, но и позволит вам отправлять значения в методе выполнения(), что сделает ваш код намного короче.
$skip = $_GET['skip'] ?: 0;
$sql = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$stmt = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);
Глядя на отчет об ошибке, можно сказать, что следующее может сработать:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
но уверены ли вы, что ваши входящие данные верны?Поскольку в сообщении об ошибке, кажется, есть только один кавычка после числа (в отличие от целого числа, заключенного в кавычки).Это также может быть ошибкой входящих данных.Можете ли вы сделать print_r($_GET);
выяснить?
Это просто как резюме.
Существует четыре варианта параметризации значений LIMIT/OFFSET:
Запрещать
PDO::ATTR_EMULATE_PREPARES
как уже упоминалось выше.Что предотвращает передачу значений
->execute([...])
всегда отображаться в виде строк.Переключиться на ручной режим
->bindValue(..., ..., PDO::PARAM_INT)
популяция параметров.Однако это менее удобно, чем ->execute list[].
Просто сделайте здесь исключение и просто интерполируйте простые целые числа при подготовке SQL-запроса.
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
Кастинг важен.Чаще всего вы видите
->prepare(sprintf("SELECT ... LIMIT %d", $num))
используется для таких целей.Если вы используете не MySQL, а, например, SQLite или Postgres;вы также можете привести связанные параметры непосредственно в SQL.
SELECT * FROM tbl LIMIT (1 * :limit)
Опять же, MySQL/MariaDB не поддерживает выражения в предложении LIMIT.Еще нет.
для LIMIT :init, :end
Вам нужно связать таким образом.если бы у тебя было что-то вроде $req->execute(Array());
это не сработает, так как будет кастовать PDO::PARAM_STR
ко всем переменным в массиве и для LIMIT
вам абсолютно необходимо целое число.bindValue или BindParam по вашему желанию.
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
Поскольку никто не объяснил, почему это происходит, добавляю ответ.Причина такого поведения заключается в том, что вы используете trim()
.Если вы посмотрите руководство PHP для trim
, тип возвращаемого значения string
.Затем вы пытаетесь передать это как PDO::PARAM_INT
.Вот несколько способов обойти это:
- Использовать
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
чтобы убедиться, что вы передаете целое число. - Как говорили другие, используя
intval()
- Кастинг с
(int)
- Проверяем, является ли оно целым числом с
is_int()
Есть еще много способов, но это, по сути, основная причина.
Смещение и ограничениеbindValue с помощью PDO::PARAM_INT, и это будет работать
// до (настоящая ошибка) $ Query = "....ПРЕДЕЛ :p1, 30;";...$stmt->bindParam(':p1', $limiteInferior);
// после (исправленная ошибка) $ Query = "....ПРЕДЕЛ :p1, 30;";...$limiteInferior = (int)$limiteInferior;$stmt->bindParam(':p1', $limiteInferior, PDO::PARAM_INT);
PDO::ATTR_EMULATE_PREPARES
дал мне
Драйвер не поддерживает эту функцию:Этот драйвер не поддерживает ошибку атрибутов настройки.
Мой обходной путь заключался в том, чтобы установить $limit
переменную как строку, затем объедините ее в операторе подготовки, как показано в следующем примере:
$limit = ' LIMIT ' . $from . ', ' . $max_results;
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE company_id = :cid ORDER BY name ASC' . $limit . ';' );
try {
$stmt->execute( array( ':cid' => $company_id ) );
...
}
catch ( Exception $e ) {
...
}