foreachで&を使った後、unsetを忘れると値が壊れる
foreach ($arr as &$v) で配列を参照渡しで回すと、ループ終了後も$vは配列の最後の要素への参照を保持し続けます。unset($v)を忘れると、次に$vという名前でループを回したときに最後の要素が上書きされます。
なぜエラーが出ないのか
出力: 244
(エラーなし)- PHPは何も報告しません。文法として正しいためです
出力: 244- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 <?php 2 $nums = [1, 2, 3]; 3 foreach ($nums as &$v) { 4 $v *= 2; 5 } 6 ^ 7 foreach ($nums as $v) { 8 echo $v; 9 } 10 echo "\n" . implode(",", $nums);
出力: 244
2,4,4期待: 2,4,6 (エラーにはなりません)
1回目のforeachが終わっても$vは配列の最後の要素への参照を保持したままです。unsetし忘れると、2回目のforeachが同じ名前の$vで回った瞬間、最後の要素が上書きされます。
直し方: (空) を unset($v); にします。
広告
広告スロット(未設定)
パターン2
1 <?php 2 $scores = [5, 10, 15]; 3 foreach ($scores as &$s) { 4 $s += 1; 5 } 6 ^ 7 foreach ($scores as $s) { 8 echo $s; 9 } 10 echo "\n" . implode(",", $scores);
出力: 61111
6,11,11期待: 6,11,16 (エラーにはなりません)
$sも参照のまま残るため、2回目のforeachで自分自身に代入を続け、最後の要素が壊れます。
直し方: (空) を unset($s); にします。
パターン3
1 <?php 2 $prices = [100, 200, 300]; 3 foreach ($prices as &$p) { 4 $p -= 10; 5 } 6 ^ 7 foreach ($prices as $p) { 8 echo $p . " "; 9 } 10 echo "\n" . implode(",", $prices);
出力: 90 190 190
90,190,190期待: 90,190,290 (エラーにはなりません)
unset($p)を忘れると、参照が残ったままの$pに2回目のforeachの値が書き込まれ続け、最後の要素が壊れます。
直し方: (空) を unset($p); にします。
よくある誤解
「foreachのループ変数はループが終われば無関係になるはず」という思い込みは、&を使った参照foreachには当てはまりません。unsetし忘れると、後続のコードが気づかないうちに配列を壊します。
まとめ
foreachで&を使った後、unsetを忘れると値が壊れるは中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。