当Mysqli发送准备好的查询时,PDO向MySQL发送原始查询,两者都产生相同的结果

时间:2013-08-02 21:02:45

标签: php mysql pdo mysqli

我开始知道在使用MySQLi和PDO时预备语句是如何工作的,第一步,我启用了MySQL查询监控,如下所述:How can I view live MySQL queries?。然后我创建了以下测试:

使用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 integer更改为string:

之后

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()或类似的东西,Bill也回答这是正确的。

1 个答案:

答案 0 :(得分: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:这可能是一个很好的理由,为什么习惯性地将id值设置为1而不是0。