PHP中PDO数据访问对象处理LIMIT子句的一处BUG

近期的项目中,,数据访问层类使用了PDO作为底层,PDO相比mysql_*系列函数具有更高的性能和安全性。在使用时却发现了PDO对象处理带有LIMIT子句的SQL语句时,不能正确处理LIMIT子句中的占位符。例如:

$sql = "SELECT * FROM user LIMIT ?, ?";$stmt = $pdo->prepare($sql);$stmt->execute(array(0, 20));….

在这段代码中,按照PDO文档的说法,两个问号的位置将会被0, 20这两个参数填充,并且类型将会使用和参数类型相匹配的PDO数据类型。但是执行这条语句却返回一个空集合。

var_dump($stmt->fetchAll());// 输出 empty

然而将问号的位置直接填写数字,是可以正常取得20条记录的。

在谷歌上搜索发现,有其他人也遇到了同样的BUG:

关于这一Bug,可以在PHP官方的Bug Report平台找到:

https://bugs.php.net/bug.php?id=44639

解决的方法是,使用bindValue方法绑定值到SQL语句,并且指定第三个参数为INT类型:

$sql = "SELECT * FROM user LIMIT ?, ?";$stmt = $pdo->prepare($sql);$stmt->bindValue(1, 0, PDO::PARAM_INT);$stmt->bindValue(2, 20, PDO::PARAM_INT);$stmt->execute();

这样再次执行,发现20条记录被正确地查询出来。

不过这样不算完美解决,我们通常会对PDO对象进行一些封装才使用,比如构建一次查出来SQL语句并返回结果集数组的方法,传参是不能用bindValue的。于是我写了一个用于自动选择类型的函数:

function select_datatype($val) {if (is_bool($val)) {return PDO::PARAM_BOOL;} elseif (is_int($val)) {return PDO::PARAM_INT;} elseif {is_null($val)) {return PDO::PARAM_NULL;} else {return PDO::PARAM_STR;}}

这样将$stmt->execute(array(0, 20));类似的语句,转换为循环遍历使用bindValue()绑定值,并使用select_datatype()选择类型即可解决这一问题。

其实早在2008年,就有人向PHP官方报告了这个Bug,但是至今仍未被Fix。如果之后有时间,研究研究能不能做个针对这个问题的补丁。

创造条件,去改变生活,做生活的强者.愿你早日成为生活的强者

PHP中PDO数据访问对象处理LIMIT子句的一处BUG

相关文章:

你感兴趣的文章:

标签云: