私が書いていた単純なphpスクリプトを使用して、非常に奇妙な動作をしました。バグを再現するために必要な最小限に減らしました。
<?php
$arr = array("foo",
"bar",
"baz");
foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);
foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
?>
この出力:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
これはバグですか、それとも実際に発生するはずの奇妙な動作ですか?
最初のforeachループの後、$item
はまだ$arr[2]
によって使用されている値への参照です。そのため、参照による呼び出しではない2番目のループの各foreach呼び出しは、その値、したがって$arr[2]
を新しい値に置き換えます。
ループ1の場合、値と$arr[2]
は$arr[0]
になります。これは「foo」です。
ループ2、値と$arr[2]
は$arr[1]
になります。これは「バー」です。
ループ3、値と$arr[2]
は$arr[2]
になります。これは「ループ」です(ループ2のため)。
値 'baz'は、2番目のforeachループの最初の呼び出しで実際に失われます。
ループの各反復に対して、$item
の値をエコーし、配列$arr
を再帰的に出力します。
最初のループが実行されると、次の出力が表示されます。
foo
Array ( [0] => foo [1] => bar [2] => baz )
bar
Array ( [0] => foo [1] => bar [2] => baz )
baz
Array ( [0] => foo [1] => bar [2] => baz )
ループの最後で、$item
は$arr[2]
と同じ場所を指し続けています。
2番目のループが実行されると、次の出力が表示されます。
foo
Array ( [0] => foo [1] => bar [2] => foo )
bar
Array ( [0] => foo [1] => bar [2] => bar )
bar
Array ( [0] => foo [1] => bar [2] => bar )
配列が$item
に新しい値を配置するたびに、$arr[3]
も同じ値で更新されることに気付くでしょう。これらは両方ともまだ同じ場所を指しているからです。ループが配列の3番目の値に到達すると、そのループの前回の反復で設定されたため、値bar
が含まれます。
いいえ。これは参照されるアイテムの動作であり、バグではありません。次のような実行に似ています。
for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }
Foreachループは、参照されるアイテムを無視できるという性質上、特別なものではありません。ループの外で行うように、毎回その変数を新しい値に設定するだけです。
$item
は$arr[2]
への参照であり、アニメンソンが指摘したように2番目のforeachループによって上書きされています。
foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);
unset($item); // This will fix the issue.
foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?
これは公式にはバグではないかもしれませんが、私の意見ではそうです。ここでの問題は、他の多くのプログラミング言語の場合のように、ループが終了したときに$item
がスコープ外になることを期待していることだと思います。しかし、そうではないようです...
このコード...
$arr = array('one', 'two', 'three');
foreach($arr as $item){
echo "$item\n";
}
echo $item;
出力を与える...
one
two
three
three
他の人がすでに言ったように、あなたは$arr[2]
の参照変数を2番目のループで上書きしていますが、$item
がスコープ外に出なかったために起こっています。皆さんはどう思いますか...バグ?
これは、refディレクティブ(&)で使用するためです。最後の値は2番目のループに置き換えられ、配列が破損します。最も簡単な解決策は、2番目のループに別の名前を使用することです。
foreach ($arr as &$item) { ... }
foreach ($arr as $anotherItem) { ... }
PHPの正しい動作は、私の意見ではNOTICEエラーになるはずです。foreachループで作成された参照変数がループ外で使用される場合、通知が発生するはずです。 、それが起こったときにそれを見つけるのは非常に困難です。
この種の問題を回避するには、ループの後にunset()
参照する必要があります。参照に対するunset()は、元のデータを損なわずに参照を削除するだけです。
より簡単な説明は、PHPの最初の作成者であるRasmus Lerdorfによると思われます。 https://bugs.php.net/bug.php?id=71454