如何在LIMIT子句中应用bindValue方法?

Nat*_*n H 112 php mysql sql pdo bindvalue

这是我的代码的快照:

$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);
Run Code Online (Sandbox Code Playgroud)

我明白了

您的SQL语法有错误; 检查与MySQL服务器版本对应的手册,以便在第1行的"15",15'附近使用正确的语法

似乎PDO在SQL代码的LIMIT部分中为我的变量添加单引号.我查了一下我发现了这个我认为相关的错误:http: //bugs.php.net/bug.php?id = 44639

这就是我在看的东西吗?这个bug自2008年4月开始!在此期间我们应该做什么?

我需要构建一些分页,并且需要在发送sql语句之前确保数据是干净的,sql注入安全的.

Ste*_*ran 159

我记得之前遇到过这个问题.在将值传递给bind函数之前将值转换为整数.我认为这解决了它.

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
Run Code Online (Sandbox Code Playgroud)

  • 谢谢!但是在PHP 5.3中,上面的代码引发了一个错误,说"致命错误:不能通过引用传递参数2".它不喜欢在那里输入int.而不是`(int)trim($ _ GET ['skip'])`,尝试`intval(trim($ _ GET ['skip']))`. (37认同)
  • 这仅在**模拟预准备语句启用时才有效**.如果它被禁用它将失败(它应该被禁用!) (6认同)
  • 如果有人从设计/安全(或其他)角度提供解释原因,那将会很酷. (5认同)
  • @Ross我无法专门回答这个问题 - 但是我可以指出LIMIT和OFFSET是所有这些PHP/MYSQL/PDO疯狂袭击开发电路后粘在一起的功能......事实上,我相信这是Lerdorf本人的监督几年前LIMIT实施.不,它没有回答这个问题,但它确实表明它是一个售后市场附加产品,而且你知道它们有时可以解决这个问题.... (4认同)
  • @Ross PDO不允许绑定值 - 而是变量.如果你尝试bindParam(':something',2)你会有一个错误,因为PDO使用一个指向变量的指针,一个数字不能有(如果$ i是2,你可以有一个指向$ i但不指向2号). (2认同)

You*_*nse 42

最简单的解决方案是关闭仿真模式.您可以将其作为连接选项或仅添加以下行

$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Run Code Online (Sandbox Code Playgroud)

它不仅可以解决你的bind param的问题,而且还可以让你在execute()中发送值,这将使你的代码显着射击

$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);
Run Code Online (Sandbox Code Playgroud)


Pek*_*ica 15

查看错误报告,以下内容可能有效:

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

$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);  
Run Code Online (Sandbox Code Playgroud)

但您确定您的传入数据是否正确?因为在错误消息中,数字后面似乎只有一个引号(而不是整数被括在引号中).这也可能是您输入数据的错误.你print_r($_GET);能找到一个吗?


mar*_*rio 8

这只是总结.
参数化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}");
    
    Run Code Online (Sandbox Code Playgroud)

    铸造很重要.更常见的是,您会看到->prepare(sprintf("SELECT ... LIMIT %d", $num))用于此类目的.

  4. 如果您不使用MySQL,例如SQLite或Postgres; 您也可以直接在SQL中转换绑定参数.

     SELECT * FROM tbl LIMIT (1 * :limit)
    
    Run Code Online (Sandbox Code Playgroud)

    同样,MySQL/MariaDB不支持LIMIT子句中的表达式.还没.


Nic*_*ini 7

对于 LIMIT :init, :end

你需要绑定那种方式.如果你有类似的东西$req->execute(Array());不会工作,因为它将PDO::PARAM_STR转换为数组中的所有变量,因为LIMIT你绝对需要一个整数.你想要的bindValue或BindParam.

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
Run Code Online (Sandbox Code Playgroud)