Code Fix

中級

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パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)