【PHP】foreachの参照渡しで起こり得るバグの対策方法

PHPのforeachにおける参照渡しの注意点と、バグ回避方法についてまとめました。

参考 PHPのforeachを徹底解説【サンプルコード有】

foreachの参照渡しとは

PHPのforeachは、値となる変数(ここでは$valueとします)の前に「&」を付けることで、参照渡しを行えます。

参照渡しにより、ループ内で値を書き換えると元の配列の値も書き換えることが出来ます。

通常のループ

サンプルコード
$aryHoge = [1, 2, 3, 4];
foreach($aryHoge as $value){
    $value *= 3; //値を3倍にして代入
}

print_r($aryHoge); //3倍になっていない
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
    [3] => 4
)

参照渡しのループ

サンプルコード
$aryHoge = [1, 2, 3, 4];
foreach($aryHoge as &$value){
    $value *= 3; //値を3倍にして代入
}

print_r($aryHoge); //3倍になっている
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
    [3] => 12
)

参照渡しに使用された値の変数を代入に注意

foreachの参照渡しループ内で使用した値の変数($value)の取り扱いに注意が必要です。

参照渡しでループを回した場合、ループを抜けた後も、使用した変数が元の配列の最後の値として参照されてしまいます。

サンプルコード
$aryHoge = [1, 2, 3, 4];
foreach($aryHoge as &$value){
    $value *= 3; //値を3倍にして代入
}

$value = 1000;

print_r($aryHoge); //最後の値に注目
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
    [3] => 1000
)

foreach内のループの変数は、ループ内だけで有効な変数ではありません。

ループを抜けた後も最後に代入された値($value)は生きているため、代入を行うと元の配列の最後の値が変更されてしまいます。

このようにforeachの参照渡しは、記述によってはバグを引き起こす原因となるため注意しましょう。

foreachの参照渡しの対策

foreachの参照渡しに起因するバグを回避する方法を2つ紹介します。

1.ループ終了後にunsetして変数を破棄する

参照渡しのループが終了された後、値の変数をunset()で破棄することで参照が解除されます。

サンプルコード
$aryHoge = [1, 2, 3, 4];
foreach($aryHoge as &$value){
    $value *= 3; //値を3倍にして代入
}

unset($value); //参照の解除

$value = 1000;

print_r($aryHoge); //値が代入されていない
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
    [3] => 12
)

2.参照渡しではなく、キーも取得して元の配列に直接アクセスする

そもそも参照渡しではなく、foreachでキーも取得して値を直接書き換える方法もあります。

サンプルコード
$aryHoge = [1, 2, 3, 4];
foreach($aryHoge as $key => $value){ //キーも取得する文法に変更
    $aryHoge[$key] *= 3; //値を3倍にして代入
}

$value = 1000;

print_r($aryHoge); //値が代入されていない
Array
(
    [0] => 3
    [1] => 6
    [2] => 9
    [3] => 12
)

foreachで参照渡しを行う際は、バグの原因とならないように仕様を把握しておきましょう。

以上、foreachの参照渡しで起こり得るバグの対策方法、でした。

PHP