Referencja w pętli foreach – bug który nie jest bugiem
Weźmy pod lupę prostą pętle foreach w PHP, która zamienia we wszystkich elementach tablicy litery na duże:
$words = array('system', 'baza', 'telefon');
foreach ($words as &$item)
{
$item = strtoupper($item);
}
Jak widać, kolejne elementy tablicy pobierane są po przez referencję, tj. tworzony jest wskaźnik na konkretny element a nie jego kopia w pamięci. Dlatego też, zmieniając w pętli zawartość zmiennej $item modyfikujemy pierwotną tablicę. W czym zatem tkwi problem?
Po wykonaniu powyższego kodu tablica $words wygląda następująco (var_dump($words)):
array(3) {
[0]=>
string(4) "SYSTEM"
[1]=>
string(4) "BAZA"
[2]=>
&string(6) "TELEFON"
}
Wszystko wygląda na pierwszy rzut oka tak jak oczekiwaliśmy. Spróbujmy zatem wyświetlić teraz wszystkie elementy:
foreach ($words as $item)
{
echo $item.' ';
}
…w rezultacie otrzymujemy:
SYSTEM BAZA BAZA
Zdziwieni? Dzieje się tak dlatego, iż po pierwszej pętli zmienna $item, która jest wskaźnikiem, wciąż wskazuje na ost. element tablicy. W kolejnym przejściu, mimo iż nie użyliśmy już referencji, to zmienna $item wciąż jest typu wskaźnikowego i następuje wówczas wyciągnięcie aktualnego elementu w pętli foreach i przechowywanie go w ost. komórce tablicy którą analizujemy (zamiast w osobnej zmiennej). Z tej przyczyny, w końcowym kroku, ost. komórka zawiera wartość z kroku poprzedniego – stąd powtórzone słowo „BAZA”. Oryginalna tablica $words również posiada zmodyfikowaną wartość, co nie powinno mieć miejsca.
Aby uniknąć takich sytuacji możemy zastosować kilka rozwiązań. Pierwsze i chyba najlepsze, zwalniać zmienną iteracyjną po skończonej pętli jeśli używaliśmy referencji:
$words = array('system', 'baza', 'telefon');
foreach ($words as &$item)
{
$item = strtoupper($item);
}
unset($item);
Drugie wyjście, użyć innej zmiennej (o innej nazwie) w ew. kolejnej pętli
. I na koniec, rozwiązanie na siłę:
$words = array('system', 'baza', 'telefon');
foreach ($words as $key => $item)
{
$words[$key] = strtoupper($item);
}
…aczkolwiek jeśli używamy referencji to z reguły z powodu z którego nie możemy użyć w/w konstrukcji.
To zachowanie, mimo iż poprawne z punktu widzenia języka PHP, jest częstą przyczyną zgryzów u osób które nie zetknęły się nigdy z tym problemem. Często też jest to uważane za błąd samego parsera, który de facto nim nie jest.
a to co:
1 array(3) {
2 [0]=>
3 string(4) „MYSZ”
4 [1]=>
5 string(4) „OKNO”
6 [2]=>
7 &string(6) „SYSTEM”
8 }
bazowa tablica nie zawierała jakichś innych tekstów przypadkiem?
To po prostu funkcja strtoupper nieco pozmieniała
Oczywiście żartuję, dzięki… już poprawiłem.