Ich hatte gerade ein sehr seltsames Verhalten mit einem einfachen PHP-Skript, das ich schrieb. Ich habe es auf das Minimum reduziert, das notwendig ist, um den Fehler neu zu erstellen:
<?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?
?>
Dies gibt aus:
Array
(
[0] => foo
[1] => bar
[2] => baz
)
Array
(
[0] => foo
[1] => bar
[2] => bar
)
Ist das ein Fehler oder ein wirklich seltsames Verhalten, das passieren soll?
foreach($x AS &$y){ ... unset($y); }
- Es befindet sich tatsächlich auf php.net (weiß nicht wo), da es sich um einen häufig gemachten Fehler handelt.Antworten:
Nach der ersten foreach-Schleife
$item
wird immer noch auf einen Wert verwiesen, der auch von verwendet wird$arr[2]
. Jeder foreach-Aufruf in der zweiten Schleife, der nicht als Referenz aufgerufen wird, ersetzt diesen Wert und damit$arr[2]
den neuen Wert.Also Schleife 1, den Wert und
$arr[2]
werden$arr[0]
, was 'foo' ist.Schleife 2, der Wert und
$arr[2]
werden$arr[1]
, was 'bar' ist.Schleife 3, der Wert und
$arr[2]
werden$arr[2]
, was 'bar' ist (wegen Schleife 2).Der Wert 'baz' geht beim ersten Aufruf der zweiten foreach-Schleife tatsächlich verloren.
Debuggen der Ausgabe
Für jede Iteration der Schleife geben wir den Wert des
$item
Arrays wieder und drucken es rekursiv aus$arr
.Wenn die erste Schleife durchlaufen wird, sehen wir diese Ausgabe:
Zeigt am Ende der Schleife
$item
immer noch auf die gleiche Stelle wie$arr[2]
.Wenn die zweite Schleife durchlaufen wird, sehen wir diese Ausgabe:
Sie werden feststellen, dass jedes Mal, wenn ein Array einen neuen Wert einfügt
$item
, dieser ebenfalls$arr[3]
mit demselben Wert aktualisiert wird, da beide immer noch auf denselben Speicherort verweisen. Wenn die Schleife den dritten Wert des Arrays erreicht, enthält sie den Wert,bar
da er gerade durch die vorherige Iteration dieser Schleife festgelegt wurde.Ist es ein Fehler?
Nein. Dies ist das Verhalten eines referenzierten Elements und kein Fehler. Es wäre ähnlich wie beim Ausführen von:
Eine foreach-Schleife ist nichts Besonderes und kann referenzierte Elemente ignorieren. Sie setzen diese Variable einfach jedes Mal auf den neuen Wert, wie Sie es außerhalb einer Schleife tun würden.
quelle
$item
ist kein Verweis auf$arr[2]
, der in enthaltene Wert$arr[2]
ist ein Verweis auf den von$item
. Um den Unterschied zu veranschaulichen, könnten Sie auch deaktivieren$arr[2]
und$item
wären davon nicht betroffen, und das Schreiben an$item
würde dies nicht beeinflussen.$item
wird der Gültigkeitsbereich nicht verlassen, wenn die foreach-Schleife beendet wird? Dies scheint ein Schließungsproblem zu sein?$item
ist ein Verweis auf$arr[2]
und wird von der zweiten foreach-Schleife überschrieben, wie animuson hervorhob.quelle
Obwohl dies offiziell kein Fehler sein mag, ist es meiner Meinung nach so. Ich denke, das Problem hier ist, dass wir die Erwartung haben, dass wir den
$item
Bereich verlassen, wenn die Schleife verlassen wird, wie es in vielen anderen Programmiersprachen der Fall wäre. Dies scheint jedoch nicht der Fall zu sein ...Dieser Code ...
Gibt die Ausgabe ...
Wie andere bereits gesagt haben, überschreiben Sie die referenzierte Variable
$arr[2]
mit Ihrer zweiten Schleife, aber dies geschieht nur, weil$item
der Gültigkeitsbereich nie überschritten wurde . Was meint ihr ... Fehler?quelle
{}
im Allgemeinen keinen neuen Bereich für Schleifen oder Blöcke. So funktioniert die SpracheEine einfachere Erklärung scheint von Rasmus Lerdorf, dem ursprünglichen Schöpfer von PHP: https://bugs.php.net/bug.php?id=71454
quelle
Das korrekte Verhalten von PHP könnte meiner Meinung nach ein NOTICE-Fehler sein. Wenn eine in einer foreach-Schleife erstellte referenzierte Variable außerhalb der Schleife verwendet wird, sollte dies einen Hinweis verursachen. Es ist sehr leicht, auf dieses Verhalten hereinzufallen, es ist sehr schwer zu erkennen, wenn es passiert ist. Und kein Entwickler wird die foreach-Dokumentationsseite lesen, es ist keine Hilfe.
Sie sollten
unset()
die Referenz nach Ihrer Schleife verwenden, um diese Art von Problem zu vermeiden. unset () für eine Referenz entfernt nur die Referenz, ohne die Originaldaten zu beschädigen.quelle
Das liegt daran, dass Sie die Ref-Direktive (&) verwenden. Der letzte Wert wird durch die zweite Schleife ersetzt und Ihr Array wird beschädigt. Die einfachste Lösung besteht darin, einen anderen Namen für die zweite Schleife zu verwenden:
quelle