如何在 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 语法有错误;查看与您的MySQL Server版本相对应的手册,以获取合适的语法,以接近“ 15”,15'在第1行
看来 PDO 正在向 SQL 代码的 LIMIT 部分中的变量添加单引号。我查了一下,发现了这个我认为相关的错误:http://bugs.php.net/bug.php?id=44639
这就是我正在看的吗?这个bug从2008年4月就已经开放了!在此期间我们应该做什么?
我需要构建一些分页,并且需要在发送 sql 语句之前确保数据干净、sql 注入安全。
解决方案
记得之前有这个问题。它传递给bind函数前的值强制转换为整数。我觉得这个解决它。
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
其他提示
的最简单的解决方案将是切换仿真模式关闭。你可以做到这一点无论是作为一个连接选项或简单地通过将下面的行
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
这不仅解决绑定PARAM您的问题,也让你在执行()送价值,这将使你的代码muchshorter
$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);
综观错误报告,以下可能的工作:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
但你确定你输入的数据是正确的?因为在错误消息中,似乎有只一个引用的数目后(相对于整个数被封装在引号)。这也可能是您的输入数据错误。你可以做一个print_r($_GET);
找出来吗?
这只是作为总结。
有四个选项可参数化 LIMIT/OFFSET 值:
禁用
PDO::ATTR_EMULATE_PREPARES
如上所述 多于.这可以防止每个值传递
->execute([...])
始终显示为字符串。切换至手动
->bindValue(..., ..., PDO::PARAM_INT)
参数总体。然而,这不如 ->execute list[] 方便。
只需在此处例外,并在准备 SQL 查询时插入纯整数即可。
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
选角很重要。更常见的是你看到
->prepare(sprintf("SELECT ... LIMIT %d", $num))
用于此类目的。如果您不使用 MySQL,而是使用 SQLite 或 Postgres;您还可以直接在 SQL 中转换绑定参数。
SELECT * FROM tbl LIMIT (1 * :limit)
同样,MySQL/MariaDB 不支持 LIMIT 子句中的表达式。还没有。
有LIMIT :init, :end
您需要这种方式进行绑定。如果你有这样的事情$req->execute(Array());
它不会工作,因为它会在数组中,并为PDO::PARAM_STR
投LIMIT
所有瓦尔你绝对需要一个整数。
bindValue或BindParam你想要的。
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
由于没有人解释为什么会发生这种情况,所以我添加一个答案。它这样做的原因是因为您正在使用 trim()
. 。如果您查看 PHP 手册 trim
, ,返回类型为 string
. 。然后你试图将其传递为 PDO::PARAM_INT
. 。解决这个问题的几种方法是:
- 使用
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
以确保您传递的是整数。 - 正如其他人所说,使用
intval()
- 铸造用
(int)
- 检查它是否是一个整数
is_int()
还有很多方法,但这基本上是根本原因。
bindValue偏移和使用PDO :: PARAM_INT限制,它会工作
// BEFORE(当前误差) $查询= “.... LIMIT:P1,30;”; ... $ stmt-> bindParam( ':P1',$ limiteInferior);
// AFTER(错误校正) $查询= “.... LIMIT:P1,30;”; ... $ limiteInferior =(int)的$ limiteInferior; $ stmt-> bindParam( ':P1',$ limiteInferior,PDO :: PARAM_INT);
PDO::ATTR_EMULATE_PREPARES
给我的
驱动程序不支持此功能:该驱动程序不支持 设置属性错误。
我的解决方法是设置一个$limit
变量作为一个字符串,然后在准备声明结合起来,如以下示例:
$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 ) {
...
}