プレースホルダに bindParam() を使って値をセットする時、
同じ変数名を使うと参照渡しが行われてしまい、最後にセットした値が
全てのプレースホルダにセットされてしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?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() しておけば同じ名前であっても大丈夫です。
1 2 3 4 5 6 7 | $value = "foo" ;
$stmt ->bindParam(1, $value );
unset( $value );
$value = "bar" ;
$stmt ->bindParam(2, $value );
|
execute() を行うまでは $value に代入した時点で上書きが行われるので、
bindParam() を使ったかどうかは実際には関係ありません。
次のようにすると全てのパラメータは「hello」に上書きされます。
1 2 3 4 5 6 7 | $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() で片付けておきます。
1 2 3 4 5 | $i =1; foreach ( $arr as & $value ){
$stmt ->bindParam( $i , $value );
$i ++;
}
unset( $value );
|
参照渡しをしない bindValue() を使えばこの問題が発生しないので、bindParam にこだわりがなければ
for や foreach などのループ内では bindValue() を使ったほうが無駄な混乱が起こりにくくなると思います。