PHP Foreach Pass by Reference: Letztes Element duplizieren? (Fehler?)

  • Ich hatte gerade ein seltsames Verhalten mit einem einfachen PHP-Skript, das ich schrieb. Ich habe es auf das notwendige Minimum reduziert, 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 Folgendes aus:

     Array
    (
        [0] => foo
        [1] => bar
        [2] => baz
    )
    Array
    (
        [0] => foo
        [1] => bar
        [2] => bar
    )
     

    Ist dies ein Fehler oder ein wirklich merkwürdiges Verhalten, das passieren soll?

    20 May 2013
    John Smithregality
6 answers
  • Nach der ersten foreach-Schleife ist $item immer noch ein Verweis auf einen Wert, der auch von $arr[2] verwendet wird. Jeder foreach-Aufruf in der zweiten Schleife, die nicht durch Verweis aufgerufen wird, ersetzt also diesen Wert und somit $arr[2] durch den neuen Wert.

    Also Wert 1 und $arr[2] werden zu $arr[0], was "foo" ist.
    Loop 2, der Wert und $arr[2] werden zu $arr[1], was 'bar' ist.
    Loop 3, der Wert und $arr[2] werden zu $arr[2], was 'bar' ist (wegen Schleife 2).

    Der Wert 'baz' geht beim ersten Aufruf tatsächlich verloren der zweiten foreach-Schleife.

    Debuggen der Ausgabe

    Bei jeder Iteration der Schleife geben wir den Wert von $item sowie rekursiv das Array $arr drucken.

    Wenn die erste Schleife durchlaufen wird, sehen wir diese Ausgabe:

     foo
    Array ( [0] => foo [1] => bar [2] => baz )
    
    bar
    Array ( [0] => foo [1] => bar [2] => baz )
    
    baz
    Array ( [0] => foo [1] => bar [2] => baz )
     

    Am Ende der Schleife zeigt $item immer noch auf dieselbe Stelle wie $arr[2].

    Wenn die zweite Schleife durchlaufen wird, sehen wir diese Ausgabe:

     foo
    Array ( [0] => foo [1] => bar [2] => foo )
    
    bar
    Array ( [0] => foo [1] => bar [2] => bar )
    
    bar
    Array ( [0] => foo [1] => bar [2] => bar )
     

    Sie werden es bemerken Jedes Mal, wenn Array einen neuen Wert in $item einfügt, wird auch $arr[3] mit aktualisiert denselben Wert, da beide immer noch auf dieselbe Position zeigen. Wenn die Schleife den dritten Wert des Arrays erreicht, enthält sie den Wert bar, da sie gerade durch die vorherige Iteration dieser Schleife festgelegt wurde.

    Ist es ein Fehler

    Nein. Dies ist das Verhalten eines referenzierten Elements und nicht eines Fehlers. Es wäre ähnlich, etwas zu laufen:

     for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }
     

    Eine foreach-Schleife ist in der Natur nicht besonders kann referenzierte Elemente ignorieren. Diese Variable wird einfach jedes Mal auf den neuen Wert gesetzt, als würden Sie sich außerhalb einer Schleife befinden.

    23 February 2012
    animusonSSEMember
  • $item ist eine Referenz auf $arr[2] und wird von der zweiten Foreach-Schleife überschrieben, wie auf Animuson hingewiesen wird.

     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?
     
    22 November 2011
    Michael Leaney
  • Auch wenn dies nicht offiziell ein Fehler ist, so ist es meiner Meinung nach. Ich denke, das Problem hier ist, dass wir erwarten, dass $item den Gültigkeitsbereich verlässt, wenn die Schleife beendet wird, wie es in vielen anderen Programmiersprachen der Fall wäre. Dies scheint jedoch nicht der Fall zu sein ...

    Dieser Code ...

     $arr = array('one', 'two', 'three');
    foreach($arr as $item){
        echo "$item\n";
    }    
    echo $item;
     

    Gibt die Ausgabe aus ...

     one
    two
    three
    three
     

    Wie andere bereits gesagt haben , Sie überschreiben die referenzierte Variable in $arr[2] mit Ihrer zweiten Schleife. Dies geschieht jedoch nur, weil $item nie den Rahmen überschritten hat. Was meint ihr ... Fehler?

    25 November 2011
    jocull
  • liegt daran, dass Sie die ref-Direktive (& amp;) verwenden. Der letzte Wert wird durch die zweite Schleife ersetzt, wodurch das Array beschädigt wird. Die einfachste Lösung besteht darin, einen anderen Namen für die zweite Schleife zu verwenden:

     foreach ($arr as &$item) { ... }
    
    foreach ($arr as $anotherItem) { ... }
     
    15 April 2018
    Amir Surnay
  • Das korrekte Verhalten von PHP ist in meiner Meinung nach ein NOTICE-Fehler. Wenn eine referenzierte Variable, die in einer foreach-Schleife erstellt wird, außerhalb der Schleife verwendet wird, sollte dies zu einer Meldung führen. für dieses Verhalten sehr schwer zu erkennen, wenn es passiert ist. Und kein Entwickler wird die Dokumentationsseite von foreach lesen. Es ist keine Hilfe.

    Sie sollten unset() Die Referenz nach Ihrer Schleife, um diese Art von Problem zu vermeiden. Unset () auf einer Referenz entfernt die Referenz einfach, ohne die Originaldaten zu beschädigen.

    28 September 2017
    John
  • Eine einfachere Erklärung scheint Rasmus Lerdorf, ursprünglicher Schöpfer von PHP: https://bugs.php.net/bug.php?id=71454

    09 June 2017
    qdinar