如何在 LIMIT 子句中应用 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 语法有错误; check the manual that corresponds to 的 MySQL 服务器版本 在“15’,15’处使用正确的语法 1号线

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

我看到的是这个吗? 这个 bug 从2008年4月就打开了! 这期间我们该做什么?

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

66879 次浏览

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

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


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

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

我记得以前遇到过这个问题。在将该值传递给 bind 函数之前,将其强制转换为整数。我觉得这能解决问题。

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

LIMIT :init, :end

你需要这样捆绑。如果你有类似于 $req->execute(Array());的东西,它不会工作,因为它会将 PDO::PARAM_STR强制转换为数组中的所有变量,而对于 LIMIT,你绝对需要一个整数。 BindValue 或者 BindParam 都可以。

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

最简单的解决方案是关闭模拟模式。只需添加以下代码行即可完成此操作

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

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

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

$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);

这只是总结。
There are four options to parameterize LIMIT/OFFSET values:

  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)
    

    Again, MySQL/MariaDB don't support expressions in the LIMIT clause. Not yet.

使用 PDO: : PARAM _ INT 进行 bindValue 偏移量和限制,它将工作

由于没有人解释为什么会发生这种情况,我要补充一个答案。之所以会出现这种情况,是因为您正在使用 trim()。如果查看 trim的 PHP 手册,返回类型是 string。然后尝试将其作为 PDO::PARAM_INT传递。解决这个问题的方法有:

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

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

//之前(当前错误) $query = “ ... . LIMIT: p1,30;”; ... $stmt-> bindParam (’: p1’,$Limited 劣质) ;

//AFTER (Error corrected) $query = " .... LIMIT :p1, 30;"; ... $limiteInferior = (int)$limiteInferior; $stmt-> bindParam (’: p1’,$Limited 劣质,PDO: : PARAM _ INT) ;

PDO::ATTR_EMULATE_PREPARES给了我

驱动程序不支持此功能: 此驱动程序不支持 设置属性的错误。

我的解决办法是将 $limit变量设置为字符串,然后像下面的示例那样将它组合到 ready 语句中:

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

在不同版本的 PHP 和奇怪的 PDO 之间发生了很多事情。 我在这里尝试了3或4种方法,但无法让 LIMIT 工作。
我的建议是使用字符串格式化/连接与 Intval ()过滤器:

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

It is very important to use intval() to prevent SQL injection, particularly if you are getting your limit from $_GET or the like. If you do that this is the easiest way to get LIMIT working.

There is alot of talk about 'The problem with LIMIT in PDO' but my thought here is that PDO params were never ment to be used for LIMIT since they will alway be integers a quick filter works. Still, it is a bit misleading since the philosophy has always been to not do any SQL injection filtering yourself but rather 'Have PDO handle it'.

试试以下方法:

$sql2 = "SELECT  * FROM tab ORDER BY id DESC limit 2