Как применить метод bindValue в предложении LIMIT? - PullRequest
108 голосов
/ 16 февраля 2010

Вот снимок моего кода:

$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 для правильный синтаксис для использования около '' 15 ', 15' в строка 1

Похоже, что PDO добавляет одинарные кавычки к моим переменным в части LIMIT кода SQL. Я искал это, я нашел эту ошибку, которая, я думаю, связана: http://bugs.php.net/bug.php?id=44639

Это то, на что я смотрю? Эта ошибка была открыта с апреля 2008 года! Что мы должны делать в это время?

Мне нужно создать несколько страниц, и перед отправкой оператора sql необходимо убедиться, что данные чистые и безопасны для инъекций SQL.

Ответы [ 10 ]

154 голосов
/ 16 февраля 2010

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

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
41 голосов
/ 05 августа 2013

Самое простое решение - отключить режим эмуляции . Вы можете сделать это либо как вариант подключения, либо просто добавив следующую строку

$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );

Это не только решит вашу проблему с параметром связывания, но и позволит вам отправлять значения в execute (), что сделает ваш код значительно стреляющим

$skip = (isset($_GET['skip'])):$_GET['skip']:0;
$sql  = "SELECT * FROM pictures WHERE album = ? ORDER BY id ASC LIMIT ?, ?";
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$stm  = $PDO->prepare($sql);
$stm->execute(array($_GET['albumid'],$skip,$max));
$pictures = $stm->fetchAll(PDO::FETCH_ASSOC);
15 голосов
/ 16 февраля 2010

Глядя на отчет об ошибке, может сработать следующее:

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);

$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);  

но вы уверены, что ваши входные данные верны?Потому что в сообщении об ошибке, похоже, после номера стоит только одна (в отличие от целого числа, заключенного в кавычки).Это также может быть ошибка с вашими входящими данными.Можете ли вы сделать print_r($_GET);, чтобы узнать?

8 голосов
/ 14 января 2015

Это так же, как резюме.
Существует четыре варианта параметризации значений LIMIT / OFFSET:

  1. Отключить PDO::ATTR_EMULATE_PREPARES как указано выше .

    Что не позволяет значениям, переданным в ->execute([...]), всегда отображаться как строки.

  2. Переключение на ручной ->bindValue(..., ..., PDO::PARAM_INT) заполнение параметра.

    Что, однако, менее удобно, чем -> список выполнения [].

  3. Просто сделайте исключение и просто интерполируйте простые целые числа при подготовке запроса SQL.

     $limit = intval($limit);
     $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
    

    Кастинг важен. Чаще всего вы видите ->prepare(sprintf("SELECT ... LIMIT %d", $num)), используемый для таких целей.

  4. Если вы используете не MySQL, а, например, SQLite или Postgres; Вы также можете привести связанные параметры непосредственно в SQL.

     SELECT * FROM tbl LIMIT (1 * :limit)
    

    Опять же, MySQL / MariaDB не поддерживают выражения в предложении LIMIT. Еще нет.

7 голосов
/ 20 октября 2011

для LIMIT :init, :end

Тебе нужно связать таким образом. если у вас есть что-то вроде $req->execute(Array());, оно не будет работать, так как оно приведёт PDO::PARAM_STR ко всем переменным в массиве, а для LIMIT вам обязательно понадобится Integer. bindValue или BindParam, как вы хотите.

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
2 голосов
/ 28 января 2016

Поскольку никто не объяснил, почему это происходит, я добавляю ответ.Это происходит потому, что вы используете trim().Если вы посмотрите руководство по PHP для trim, тип возвращаемого значения - string.Затем вы пытаетесь передать это как PDO::PARAM_INT.Вот несколько способов обойти это:

  1. Используйте filter_var($integer, FILTER_VALIDATE_NUMBER_INT), чтобы убедиться, что вы передаете целое число.
  2. Как говорили другие, используйте intval()
  3. Приведение с (int)
  4. Проверка, является ли оно целым числом с is_int()

Есть еще много способов, но это в основном основная причина.

1 голос
/ 21 мая 2015

смещение и ограничение bindValue с использованием PDO :: PARAM_INT, и оно будет работать

0 голосов
/ 19 июля 2018

PDO::ATTR_EMULATE_PREPARES дал мне

Драйвер не поддерживает эту функцию: этот драйвер не поддерживает ошибка установки атрибутов.

Мой обходной путь - установить переменную $limit в виде строки, затем объединить ее в операторе prepare, как в следующем примере:

$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 ) {
    ...
}
0 голосов
/ 15 августа 2017

// ДО (текущая ошибка) $ query = ".... LIMIT: p1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior);

// ПОСЛЕ (ошибка исправлена) $ query = ".... LIMIT: p1, 30;"; ... $ limiteInferior = (int) $ limiteInferior; $ stmt-> bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);

0 голосов
/ 05 августа 2013

Я сделал следующее для себя, где $ info - это мой массив, содержащий мои связанные параметры:

    preg_match( '/LIMIT :(?P<limit>[0-9a-zA-Z]*)/', $sql, $matches );
    if (count($matches)) {
        //print_r($matches);
        $sql = str_replace( $matches[0], 'LIMIT ' . intval( $info[':' . $matches['limit']] ), $sql );
    }       
    // LIMIT #, :limit#
    preg_match( '/LIMIT (?P<limit1>[0-9]*),\s?:(?P<limit2>[0-9a-zA-Z]*)/', $sql, $matches );
    if (count($matches)) {
        //print_r($matches);
        $sql = str_replace( $matches[0], 'LIMIT ' . $matches['limit1'] . ',' . intval( $info[':' . $matches['limit2']] ), $sql );
    }

Это частично основано на коде Себаса.

...