我开始知道在使用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的一个很好的理由。