foreachの落とし穴($アイテムとして&$アイテム)

多くの人々は、そのような構造を何らかの形で書くのが好きです、誰もが遭遇しました:

foreach ($items as &$item) { $item += 2; }
      
      





しかし、多くの人がここに潜んでいる危険を疑っていません。

例を考えてみましょう。



Vasya Pupkinは配列を取得し、それに沿って歩いて、すべての要素を2つ増やしました。

 $items = array( 'a' => 10, 'b' => 20, 'c' => 30, ); foreach ($items as &$item) { $item += 2; } print_r($items);
      
      





私はダンプを見て、問題が解決したことを見て、満足したままにしておきました:

 Array ( [a] => 12 [b] => 22 [c] => 32 )
      
      





しばらくして、Petrovichはコードのこのセクションを別の検索で補完することを決定し、以下を追加しました。

 $newitems = array( 'a' => 10, 'b' => 20, 'c' => 30, ); foreach ($newitems as $key=>$item) { $newitems[$key] += 5; } print_r($newitems);
      
      





彼は自分のタスクも解決されたように見え、達成感を持ってファイルを閉じました。

 Array ( [a] => 15 [b] => 25 [c] => 35 )
      
      





しばらくして、説明できないバグが出始めました。 なんで?

コードの最後でvar_dump($アイテム)を実行しましょう:

 array(3) { ["a"]=> int(12) ["b"]=> int(22) ["c"]=> &int(30) }
      
      





30! Vasya Pupkinはチェックしたことを誓います。 なぜ32で、ペトロビッチコード30の後ですか?



その理由はアンパサンドにあります。 彼は、他の誰かがマークされたデータを参照していると報告します。 退出する際、Vasyaは反復に使用した一時変数($アイテム)を消去しませんでした。 この変数は、「参照による割り当て」とも呼ばれるソース(「&」)を変更する権限で使用されました。 彼は、変数がループ内でのみ使用されると確信していました。 Petrovichは、列挙中に同じ名前の変数を使用して値を変更し、この変数が格納されている場所が変わるたびに変更しました。 そして、それはパプキン山塊の最後の要素と同じ場所に保存されました。



もちろん、記事が誇張されている場合。 実際、そのような関係は、特にプロジェクトが安価で、経験豊富で異なるWeb開発者が十分に参加していない場合、非常に複雑になる可能性があります。



どのようにこれを処理できますか?



結論として、リンクに関連するバグはforeachだけではない可能性があると言います。 そして、それらのすべてがかつて議論されました。 しかし、私の経験から判断すると、このケースは実際に広まっているため、特別な注意に値します。



All Articles