122

这是我的代码的快照:

$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 服务器版本相对应的手册,以在第 1 行的 ''15', 15' 附近使用正确的语法

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

这就是我在看的吗?自 2008 年 4 月以来,此错误已打开!在此期间我们应该做什么?

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

4

10 回答 10

172

我记得以前有这个问题。在将值传递给绑定函数之前将其转换为整数。我认为这可以解决它。

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
于 2010-02-16T00:35:34.717 回答
49

最简单的解决方案是关闭仿真模式。您只需添加以下行即可

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

此外,此模式可以在创建 PDO 连接时设置为构造函数参数。这可能是一个更好的解决方案,因为有些人报告他们的驱动程序不支持该setAttribute()功能。

它不仅可以解决您的绑定问题,还可以让您将值直接发送到execute()中,这将使您的代码大大缩短。假设已经设置了仿真模式,整个事情将需要多达六行代码

$skip = isset($_GET['skip']) ? (int)trim($_GET['skip']) : 0;
$sql  = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$stmt  = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);
于 2013-08-05T16:52:06.330 回答
18

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

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

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

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

于 2010-02-16T00:32:02.273 回答
11

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

  1. PDO::ATTR_EMULATE_PREPARES如上所述禁用。_

    这可以防止传递->execute([...])的值始终显示为字符串。

  2. 切换到手动->bindValue(..., ..., PDO::PARAM_INT)参数填充。

    然而,这不如 ->execute list[] 方便。

  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 子句中的表达式。还没有。

于 2015-01-14T19:22:35.070 回答
8

为了LIMIT :init, :end

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

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
于 2011-10-20T19:17:28.857 回答
2

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

  1. 用于filter_var($integer, FILTER_VALIDATE_NUMBER_INT)确保您传递的是整数。
  2. 正如其他人所说,使用intval()
  3. 铸造与(int)
  4. 检查它是否是一个整数is_int()

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

于 2016-01-27T23:00:00.583 回答
1

使用 PDO::PARAM_INT 的 bindValue 偏移和限制,它将起作用

于 2015-05-21T20:53:12.037 回答
-1

//BEFORE (当前错误) $query = " .... LIMIT :p1, 30;"; ... $stmt->bindParam(':p1', $limiteInferior);

//之后(错误更正) $query = " .... LIMIT :p1, 30;"; ... $limiteInferior = (int)$limiteInferior; $stmt->bindParam(':p1', $limiteInferior, PDO::PARAM_INT);

于 2017-08-15T06:51:20.037 回答
-1

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 ) {
    ...
}
于 2018-07-19T12:16:20.297 回答
-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 处理它”。

于 2020-02-26T23:59:56.823 回答