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

时间:2010-02-16 00:11:16

标签: 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);

我得到了

  

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

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

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

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

11 个答案:

答案 0 :(得分:153)

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

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

答案 1 :(得分:40)

最简单的解决方案是关闭emulation mode。您可以将其作为连接选项或仅通过添加以下行

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

它不仅可以解决你的bind param的问题,而且还可以让你在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);

答案 2 :(得分:15)

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

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

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

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

答案 3 :(得分:7)

这就是总结。
参数化LIMIT / OFFSET值有四个选项:

  1. 如上所述above禁用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子句中的表达式。还没有。

答案 4 :(得分:6)

代表LIMIT :init, :end

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

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

答案 5 :(得分:2)

由于没有人解释为什么会这样,我正在添加一个答案。它表现的原因是因为您使用的是trim()。如果查看trim的PHP手册,返回类型为string。然后,您尝试将其作为PDO::PARAM_INT传递。解决这个问题的几种方法是:

  1. 使用filter_var($integer, FILTER_VALIDATE_NUMBER_INT)确保传递整数。
  2. 正如其他人所说,使用intval()
  3. 使用(int)
  4. 进行投射
  5. 检查它是否为is_int()
  6. 的整数

    还有很多方法,但这基本上是根本原因。

答案 6 :(得分:1)

使用PDO :: PARAM_INT

bindValue偏移量和限制,它将起作用

答案 7 :(得分:0)

// BEFORE(目前的错误) $ query =“.... LIMIT:p1,30;”; ... $ stmt-> bindParam(':p1',$ limiteInferior);

// AFTER(纠错) $ query =“.... LIMIT:p1,30;”; ... $ limiteInferior =(int)$ limiteInferior; $ stmt-> bindParam(':p1',$ limiteInferior,PDO :: PARAM_INT);

答案 8 :(得分:0)

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 ) {
    ...
}

答案 9 :(得分:-1)

PHP的不同版本和PDO的不同之处之间经常发生很多事情。 我在这里尝试了3或4种方法,但无法使用LIMIT。
我的建议是使用带有intval()过滤器的字符串格式化/合并:

$sql = 'SELECT * FROM `table` LIMIT ' . intval($limitstart) . ' , ' . intval($num).';';

使用intval()防止SQL注入非常重要,特别是如果您是从$ _GET等获取限制的话。如果这样做,这是获得LIMIT的最简单方法工作。

关于“ PDO中的LIMIT问题”的讨论很多,但我的想法是PDO参数永远不会被用于LIMIT,因为它们始终是整数,可以快速使用过滤器。不过,这仍然有些误导,因为其哲学一直是不自己进行任何SQL注入过滤,而是“让PDO处理它”。

答案 10 :(得分:-3)

我在我的上面做了以下操作,其中$ 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 );
    }

这部分基于塞巴斯的代码。