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


PDO sends raw query to MySQL while Mysqli sends prepared query, both produce the same result

我开始知道在使用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如何处理这个查询,并用0替换'''''1'''''。在这种情况下,如果使用PDO,监视查询将不准确,同时,使用PDO可以更好地了解发送到MySQL而不是MySQLi的确切查询。

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

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也回答了这一点,这是正确的。

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

准备好的查询将字符串''1''绑定为整数值参数值。PHP使用类似intval()的东西将它强制为一个整数。PHP将任何包含非数字前导字符的字符串解释为0,因此prepare之后发送的参数值为0。

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

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

你也可以让PDO使用真正的准备好的查询,所以它应该像mysqli一样工作:

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

附言:这可能说明了为什么习惯将id值从0开始设置为1的一个很好的理由。