如何在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服务器版本的手册,在第1行的'15',15'附近使用正确的语法
看来PDO在SQL代码的LIMIT部分中将单引号添加到我的variables中。 我查了一下,我发现这个bug,我认为是相关的: http : //bugs.php.net/bug.php?id=44639
那是我在看什么? 这个错误已经从2008年4月开始! 我们在这期间应该做什么?
我需要build立一些分页,并且在发送sql语句之前需要确保数据是干净的,sql注入安全的。
我记得以前有这个问题。 在将值传递给绑定函数之前将其转换为整数。 我认为这解决了它。
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
最简单的解决scheme是closures仿真模式 。 您可以将其作为连接选项或简单地添加以下行来完成
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
它不仅可以解决你的问题与绑定参数,但也让你发送执行(),这将使你的代码戏剧性的
$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);
看看这个错误报告,以下内容可能会起作用:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT); $fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
但是你确定你的传入数据是正确的? 因为在错误消息中,似乎只有一个数字后面的引号(而不是引号中的整数)。 这也可能是您的传入数据的错误。 你可以做一个print_r($_GET);
找出?
对于LIMIT :init, :end
你需要以这种方式绑定。 如果你有像$req->execute(Array());
它不会工作,因为它会将PDO::PARAM_STR
到数组中的所有variables,而对于LIMIT
您绝对需要一个整数。 bindValue或BindParam,如你所愿。
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
这就如同总结。
有四个选项来设置LIMIT / OFFSET值:
-
如上所述禁用
PDO::ATTR_EMULATE_PREPARES
。它阻止每个传递的值
->execute([...])
始终显示为string。 -
切换到手动
->bindValue(..., ..., PDO::PARAM_INT)
参数填充。然而,这不如 – >执行列表[]。
-
只需在这里创build一个exception,并在准备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子句中的expression式。 还没。
使用PDO :: PARAM_INT绑定值偏移和限制,它将工作
由于没有人解释为什么会发生这种情况,我添加了一个答案。 这是行为的原因是因为你正在使用trim()
。 如果您查看trim
的PHP手册,返回types是string
。 然后你试图通过这个PDO::PARAM_INT
。 有几种方法可以解决这个问题:
- 使用
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
来确保传递一个整数。 - 正如其他人所说,使用
intval()
- 用
(int)
- 检查它是否是一个整数与
is_int()
还有更多的方法,但这基本上是根本原因。
// BEFORE(存在错误)$ query =“…. LIMIT:p1,30;”; … $ stmt-> bindParam(':p1',$ limiteInferior);
// AFTER(Error corrected)$ query =“…. LIMIT:p1,30;”; … $ limiteInferior =(int)$ limiteInferior; $ stmt-> bindParam(':p1',$ limiteInferior,PDO :: PARAM_INT);
我做了以下我的$ 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 ); }
这部分基于Sebas的代码。