以下是我的代码的快照:
$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注入安全的。
答案 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值有四个选项:
如上所述above禁用PDO::ATTR_EMULATE_PREPARES
。
这可以防止每个->execute([...])
传递的值始终显示为字符串。
切换到手动->bindValue(..., ..., PDO::PARAM_INT)
参数填充。
然而,这不如 - >执行列表[]。
只需在此处进行异常,并在准备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子句中的表达式。还没有。
答案 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
传递。解决这个问题的几种方法是:
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
确保传递整数。intval()
(int)
is_int()
还有很多方法,但这基本上是根本原因。
答案 6 :(得分:1)
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 );
}
这部分基于塞巴斯的代码。