3

我开始知道使用 MySQLi 和 PDO 时准备好的语句是如何工作的,第一步,我启用了 MySQL 查询监控,如下所述:如何查看实时 MySQL 查询?. 然后我创建了以下测试:

使用 mysqli:

$stmt = $mysqli->prepare("SELECT * FROM users WHERE username =?")) {
$stmt->bind_param("i", $user);
$user = "''1''";

服务器日志:

  130802 23:39:39   175 Connect   ****@localhost on testdb
    175 Prepare   SELECT * FROM users WHERE username =?
    175 Execute   SELECT * FROM users WHERE username =0
    175 Quit

使用 PDO:

  $user = "''1''";
  $sql = 'SELECT * FROM user WHERE uid =?';
  $sth = $dbh->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
  $sth->bindParam(1, $user, PDO::PARAM_INT);

服务器日志:

  130802 23:41:42   176 Connect   ****@localhost on testdb
    176 Query SELECT * FROM user WHERE uid ='\'\'1\'\''
    176 Quit

但是,两者都提供相同的结果:

uid: 0
username: admin
role: admin

注意:uid = 0是正确的,因为intval("''1''") = 0

这里重要的是:

PDO 查询如何在向 MySQL 发送不同的查询时获得相同的结果?

SELECT * FROM user WHERE uid ='\'\'1\'\''

我从 PHP 手册中只找到一个指示:http ://www.php.net/manual/en/pdo.prepare.php

笔记:

模拟的预处理语句不与数据库服务器通信,因此 PDO::prepare() 不检查语句。

但我不确定 MySQL 如何处理此查询并替换'\'\'1\'\''0. 在这种情况下,如果使用 PDO,监视查询将不准确,同时,使用 PDO 更好地了解发送到 MySQL 而不是 MySQLi 的确切查询。

更新: 将参数类型frm整数更改为字符串后:

MySQLi 日志:

    188 Prepare   SELECT * FROM awa_user WHERE username =?
    188 Execute   SELECT * FROM awa_user WHERE username ='\'\'1\'\''
    188 Quit

PDO 日志:

    189 Query SELECT * FROM awa_user WHERE userame ='\'\'1\'\''
    189 Quit

这意味着 MySQLi 和 PDO 在使用字符串时会在发送到 MySQL 之前转义数据,而对于整数,mysqli 在发送查询之前应用 intval() 或类似的东西,比尔也回答了这是正确的。

4

1 回答 1

6

您的 PDO 配置为模拟准备好的查询,而 mysqli 使用真正的准备好的查询。

准备好的查询将字符串绑定''1''为整数参数值。PHP 使用类似intval(). 任何带有非数字前导字符的字符串都被 PHP 解释为 0,因此在prepare之后发送的参数值是值 0。

假的准备好的查询使用字符串插值(而不是绑定)在MySQL 解析它之前''1''将字符串添加到 SQL 查询中。但结果是相似的,因为 SQL 还将整数上下文中具有非数字前导字符的字符串视为值 0。

唯一的区别是,当参数在准备之前和准备之后绑定时,一般查询日志中的结果是什么。

您还可以使 PDO 使用真正的准备好的查询,因此在这种情况下它应该像 mysqli 一样工作:

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

PS:这可能是一个很好的理由,为什么习惯上从 1 而不是 0 开始 id 值。

于 2013-08-02T21:49:55.660 回答