foreachの要素の変数はループを抜けても保持されるのでunset()した
はまったのでメモ。
foreachで配列の中身を直接いじっているコードがあって、その後、同じ変数名を使っていたら中身が壊れてえらい目にあった。
先に結論を書くと、foreachのリファレンス変数はforeachを抜けても残っていて、変な動きをしていた。
そしてそれは、unset()で解除できる。
<?php $users = [ ['id' => 1, 'name' => 'Ichirou', 'age' => 39], ['id' => 2, 'name' => 'Jirou' , 'age' => 36], ['id' => 3, 'name' => 'Saburou', 'age' => 33], ]; foreach ($users as &$user) { $user['age'] += 1; } // nanika foreach ($users as $user) { echo sprintf("id: %d, name: %s, age: %d\n", $user['id'], $user['name'], $user['age'] ); }
結果
id: 1, name: Ichirou, age: 40 id: 2, name: Jirou, age: 37 id: 2, name: Jirou, age: 37
id: 3は一体どこへ…。
ちなみに、// nanika の時点ではまだ、3つの要素は無事存在している。
foreachのドキュメントにちゃんと書いてあった。
PHP: foreach - Manual
警告
foreach ループを終えた後でも、 $value は配列の最後の要素を参照したままとなります。unset()でその参照を解除しておくようにしましょう。
なんでそんな面倒な仕様なんだ なるほど。
というわけで、unset()を挟んだらちゃんと意図したとおりに動いた。
<?php foreach ($users as &$user) { $user['age'] += 1; } unset($user); // ★ここ // nanika foreach ($users as $user) { echo sprintf("id: %d, name: %s, age: %d\n", $user['id'], $user['name'], $user['age'] ); }
id: 1, name: Ichirou, age: 40 id: 2, name: Jirou, age: 37 id: 3, name: Saburou, age: 34
これを突き止めるのに、時間がかかってしまった。
Perlのブロックレベルでのスコープが恋しい。