プレースホルダに bindParam() を使って値をセットする時、
同じ変数名を使うと参照渡しが行われてしまい、最後にセットした値が
全てのプレースホルダにセットされてしまいます。
<?php $host = 'localhost'; $name = 'test'; $user = 'user'; $pass = 'password'; try { $dsn = "mysql:host={$host};dbname={$name};charset=utf-8"; $db = new PDO($dsn, $user, $pass); } catch (PDOExceprion $e){ echo $e->getMessage(); } $sql = "SELECT * FROM sample WHERE (name = ? OR name = ?)"; $stmt = $db->prepare($sql); $value = "foo"; $stmt->bindParam(1, $value); $value = "bar"; $stmt->bindParam(2, $value); $stmt->execute();
上の例では、
「SELECT * FROM sample WHERE (name = ? OR name = ?)」
という SQL 文の疑問符部分に対してそれぞれ「foo」と「bar」をバインドしています。
「name = ‘foo’ OR name = ‘bar’」となるつもりが、実際の結果は
「name = ‘bar’ OR name = ‘bar’」となってしまいました。
どちらも value と言う変数名を使っているため、後から代入した値によって
他の値が上書きされています。
$value1, $value2 のように名前を分ければこれは発生しません。
あるいは一度使った $value を unset() しておけば同じ名前であっても大丈夫です。
$value = "foo"; $stmt->bindParam(1, $value); unset($value); $value = "bar"; $stmt->bindParam(2, $value);
execute() を行うまでは $value に代入した時点で上書きが行われるので、
bindParam() を使ったかどうかは実際には関係ありません。
次のようにすると全てのパラメータは「hello」に上書きされます。
$value = "foo"; $stmt->bindParam(1, $value); $value = "bar"; $stmt->bindParam(2, $value); $value = "hello";
【結果】
SELECT * FROM sample WHERE (name = 'hello' OR name = 'hello')
ループ内で使う場合は、参照渡し「&$value」を使って bindParam() を行います。
後から問題を起こさないように使った変数は unset() で片付けておきます。
$i=1;foreach($arr as &$value){ $stmt->bindParam($i, $value); $i++; } unset($value);
参照渡しをしない bindValue() を使えばこの問題が発生しないので、bindParam にこだわりがなければ
for や foreach などのループ内では bindValue() を使ったほうが無駄な混乱が起こりにくくなると思います。