Saya baru saja memiliki perilaku yang sangat aneh dengan skrip php sederhana yang saya tulis. Saya mengurangi jumlah minimum yang diperlukan untuk membuat ulang bug:
<?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?
?>
Output ini:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
Apakah ini bug atau perilaku aneh yang seharusnya terjadi?
foreach($x AS &$y){ ... unset($y); }
- sebenarnya ada di php.net (tidak tahu di mana) karena itu adalah kesalahan yang banyak dibuat.Jawaban:
Setelah loop foreach pertama,
$item
masih referensi ke beberapa nilai yang juga digunakan oleh$arr[2]
. Jadi setiap panggilan foreach di loop kedua, yang tidak memanggil dengan referensi, menggantikan nilai itu, dan dengan demikian$arr[2]
, dengan nilai baru.Jadi loop 1, nilainya dan
$arr[2]
menjadi$arr[0]
, yaitu 'foo'.Loop 2, nilai dan
$arr[2]
menjadi$arr[1]
, yang merupakan 'bar'.Loop 3, nilai dan
$arr[2]
menjadi$arr[2]
, yang merupakan 'bar' (karena loop 2).Nilai 'baz' sebenarnya hilang pada panggilan pertama dari loop foreach kedua.
Debugging Output
Untuk setiap iterasi dari loop, kami akan menggema nilai
$item
serta mencetak array secara rekursif$arr
.Ketika loop pertama dijalankan, kita melihat output ini:
Di akhir perulangan,
$item
masih menunjuk ke tempat yang sama dengan$arr[2]
.Ketika loop kedua dijalankan, kita melihat output ini:
Anda akan melihat bagaimana setiap array waktu memasukkan nilai baru
$item
, juga diperbarui$arr[3]
dengan nilai yang sama, karena keduanya masih menunjuk ke lokasi yang sama. Ketika loop sampai ke nilai ketiga dari array, itu akan berisi nilaibar
karena hanya diatur oleh iterasi sebelumnya dari loop itu.Apakah ini bug?
Tidak. Ini adalah perilaku dari item yang dirujuk, dan bukan bug. Ini akan mirip dengan menjalankan sesuatu seperti:
Loop foreach tidak bersifat khusus di mana ia dapat mengabaikan item yang direferensikan. Ini hanya mengatur variabel itu ke nilai baru setiap kali seperti yang Anda lakukan di luar loop.
sumber
$item
bukan referensi ke$arr[2]
, nilai yang terkandung oleh$arr[2]
adalah referensi ke nilai yang dirujuk oleh$item
. Untuk mengilustrasikan perbedaannya, Anda juga dapat menghapus$arr[2]
, dan$item
tidak akan terpengaruh, dan menulis untuk$item
tidak akan mempengaruhinya.$item
keluar dari ruang lingkup ketika loop foreach keluar? Ini sepertinya masalah penutupan?$item
adalah referensi ke$arr[2]
dan sedang ditimpa oleh loop foreach kedua seperti yang ditunjukkan animuson.sumber
Meskipun ini mungkin bukan bug resmi, menurut saya itu bug. Saya pikir masalahnya di sini adalah kita memiliki harapan untuk
$item
keluar dari ruang lingkup ketika loop keluar seperti dalam banyak bahasa pemrograman lainnya. Namun itu tampaknya tidak menjadi masalah ...Kode ini ...
Memberikan output ...
Seperti yang sudah dikatakan orang lain, Anda menimpa variabel referensi
$arr[2]
dengan loop kedua Anda, tetapi itu hanya terjadi karena$item
tidak pernah keluar dari ruang lingkup. Apa yang kalian pikirkan ... bug?sumber
{}
blok secara umum. Beginilah cara kerjanya bahasaPenjelasan yang lebih mudah, tampaknya dari Rasmus Lerdorf, pencipta asli PHP: https://bugs.php.net/bug.php?id=71454
sumber
Perilaku PHP yang benar bisa menjadi kesalahan PEMBERITAHUAN menurut pendapat saya. Jika variabel referensi dibuat dalam loop foreach digunakan di luar loop itu harus menyebabkan pemberitahuan. Sangat mudah jatuh dalam perilaku ini, sangat sulit dikenali ketika itu terjadi. Dan tidak ada pengembang yang akan membaca halaman dokumentasi foreach, itu tidak membantu.
Anda harus
unset()
referensi setelah loop Anda untuk menghindari masalah semacam ini. unset () pada referensi hanya akan menghapus referensi tanpa merusak data asli.sumber
itu karena Anda menggunakan direktif ref (&). nilai terakhir akan diganti oleh loop kedua dan merusak array Anda. solusi paling sederhana adalah dengan menggunakan nama yang berbeda untuk loop kedua:
sumber