Po prostu miałem bardzo dziwne zachowanie z prostym skryptem php, który pisałem. Zredukowałem to do minimum niezbędnego do odtworzenia błędu:
<?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?
?>
To daje:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
Czy to błąd, czy jakieś naprawdę dziwne zachowanie, które powinno się wydarzyć?
foreach($x AS &$y){ ... unset($y); }
- tak naprawdę jest na php.net (nie wiem gdzie), ponieważ jest to dużo popełniony błąd.Odpowiedzi:
Po pierwszej pętli foreach
$item
nadal znajduje się odniesienie do jakiejś wartości, z której również korzysta$arr[2]
. Zatem każde wywołanie foreach w drugiej pętli, które nie wywołuje przez odwołanie, zastępuje tę wartość, a tym samym$arr[2]
nową wartością.Więc pętla 1, wartość i
$arr[2]
get$arr[0]
, czyli „foo”.Pętla 2, wartość i
$arr[2]
staje się$arr[1]
, czyli „bar”.Loop 3, wartość i
$arr[2]
get$arr[2]
, czyli „bar” (z powodu pętli 2).Wartość „baz” jest faktycznie tracona przy pierwszym wywołaniu drugiej pętli foreach.
Debugowanie danych wyjściowych
Dla każdej iteracji pętli będziemy powtarzać wartość,
$item
a także rekurencyjnie wypisywać tablicę$arr
.Po wykonaniu pierwszej pętli widzimy następujące dane wyjściowe:
Na końcu pętli
$item
nadal wskazuje to samo miejsce co$arr[2]
.Po wykonaniu drugiej pętli widzimy następujące dane wyjściowe:
Zauważysz, że za każdym razem, gdy tablica umieszcza nową wartość
$item
, jest również aktualizowana$arr[3]
o tę samą wartość, ponieważ obie nadal wskazują to samo miejsce. Gdy pętla dotrze do trzeciej wartości tablicy, będzie zawierała wartość,bar
ponieważ została ustawiona przez poprzednią iterację tej pętli.Czy to błąd?
Nie. To jest zachowanie przywoływanego elementu, a nie błąd. Byłoby to podobne do uruchomienia czegoś takiego:
Pętla foreach nie ma specjalnego charakteru, w którym może ignorować elementy, do których się odwołuje. Po prostu ustawia tę zmienną na nową wartość za każdym razem, jakbyś był poza pętlą.
źródło
$item
nie jest odniesieniem do$arr[2]
, wartość zawarta w$arr[2]
jest odniesieniem do wartości, do której odwołuje się$item
. Aby zilustrować różnicę, możesz również wyłączyć ustawienie$arr[2]
i$item
nie będzie to miało wpływu, a pisanie w celu$item
nie wpłynie na to.$item
nie wychodzi poza zakres, gdy kończy się pętla foreach? Wygląda na to, że problem z zamknięciem?$item
jest odniesieniem do$arr[2]
drugiej pętli foreach i jest przez nią nadpisywany, jak wskazał Animuson.źródło
Chociaż oficjalnie może to nie być błąd, moim zdaniem tak jest. Myślę, że problem polega na tym, że oczekujemy,
$item
że wyjdziemy poza zakres, gdy pętla zostanie zamknięta, tak jak ma to miejsce w wielu innych językach programowania. Jednak wydaje się, że tak nie jest ...Ten kod ...
Daje wyjście ...
Jak już powiedzieli inni ludzie, nadpisujesz zmienną, do której się odwołujesz,
$arr[2]
drugą pętlą, ale dzieje się to tylko dlatego, że$item
nigdy nie wyszło poza zakres. Co wy myślicie ... błąd?źródło
{}
ogólnie nie tworzy nowego zakresu dla pętli lub bloków. Tak działa językŁatwiejsze wyjaśnienie pochodzi od Rasmusa Lerdorfa, oryginalnego twórcy PHP: https://bugs.php.net/bug.php?id=71454
źródło
Moim zdaniem poprawne zachowanie PHP powinno być POWIADOMIENIEM. Jeśli zmienna, do której istnieje odwołanie, utworzona w pętli foreach jest używana poza pętlą, powinno to spowodować powiadomienie. Bardzo łatwo dać się nabrać na takie zachowanie, bardzo trudno je zauważyć, kiedy to się stało. Żaden programista nie przeczyta każdej strony dokumentacji, to nie jest pomoc.
Powinieneś
unset()
odwołać się po pętli, aby uniknąć tego rodzaju problemów. unset () w odwołaniu spowoduje po prostu usunięcie odniesienia bez szkody dla oryginalnych danych.źródło
to dlatego, że używasz przez dyrektywę ref (&). ostatnia wartość zostanie zastąpiona przez drugą pętlę, co spowoduje uszkodzenie tablicy. najprostszym rozwiązaniem jest użycie innej nazwy dla drugiej pętli:
źródło