プレースホルダに 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() を使ったほうが無駄な混乱が起こりにくくなると思います。