1

PDOStatement::bindParam的一个陷阱

 2 years ago
source link: https://www.laruence.com/2012/10/16/2831.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

PDOStatement::bindParam的一个陷阱

废话不多说, 直接看代码:

  1. <?php
  2. $dbh = new PDO('mysql:host=localhost;dbname=test', "test");
  3. $query = <<<query
  4.   INSERT INTO `user` (`username`, `password`) VALUES (:username, :password);
  5. QUERY;
  6. $statement = $dbh->prepare($query);
  7. $bind_params = array(':username' => "laruence", ':password' => "weibo");
  8. foreach( $bind_params as $key => $value ){
  9.     $statement->bindParam($key, $value);
  10. $statement->execute();

请问, 最终执行的SQL语句是什么, 上面的代码是否有什么问题?
Okey, 我想大部分同学会认为, 最终执行的SQL是:

  1. INSERT INTO `user` (`username`, `password`) VALUES ("laruence", "weibo");

但是, 可惜的是, 你错了, 最终执行的SQL是:

  1. INSERT INTO `user` (`username`, `password`) VALUES ("weibo", "weibo");

是不是很大的一个坑呢?
------ 如果你想自己找到原因, 那么就不要继续往下读了---------
这个问题, 来自今天的一个Bug报告: #63281
究其原因, 也就是bindParam和bindValue的不同之处, bindParam要求第二个参数是一个引用变量(reference).
让我们把上面的代码的foreach拆开, 也就是这个foreach:

  1. <?php
  2. foreach( $bind_params as $key => $value ){
  3.     $statement->bindParam($key, $value);
  1. <?php
  2. //第一次循环
  3. $value = $bind_params[":username"];
  4. $statement->bindParam(":username", &$value); //此时, :username是对$value变量的引用
  5. //第二次循环
  6. $value = $bind_params[":password"]; //oops! $value被覆盖成了:password的值
  7. $statement->bindParam(":password", &$value);

所以, 在使用bindParam的时候, 尤其要注意和foreach联合使用的这个陷阱. 那么正确的作法呢?
1. 不要使用foreach, 而是手动赋值

  1. <?php
  2. $statement->bindParam(":username", $bind_params[":username"]); //$value是引用变量了
  3. $statement->bindParam(":password", $bind_params[":password"]);

2. 使用bindValue代替bindParam, 或者直接在execute中传递整个参数数组.
3. 使用foreach和reference(不推荐, 原因参看:微博)

  1. <?php
  2. foreach( $bind_params as $key => &$value ) { //注意这里
  3.     $statement->bindParam($key, $value);

最后, 展开了说, 对于要求参数是引用, 并且有滞后处理的函数, 都要在使用foreach的时候, 谨慎!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK