EisFuX: verschachtelte Arrays, Referenzen, endlose(?) Rekursion

Hallo, Guten Morgen und Mahlzeit alle zusammen,

Wenn man sich (unter Zuhilfenahme von Referenzen) folgendes Array bastelt

  
$test = array( 1, 2, 3);  
$test[] = &$test;  

ergibt sich das Problem, dass eine darauf angesetzte Routine zum rekursiven Durchlaufen dieses Array in einem Endloslauf landen würde.

Das klassische Beispiel für so ein Array ist "$GLOBALS", dass in sich eine Referenz auf sich selbst enthält: "$GLOBALS['GLOBALS']".

Setzt man jetzt PHP-Funktionen darauf an, gehen diese ganz unterschiedlich vor:

print_r() meldet ordnungsgemäß diese "unendliche Verschachtelung" mit *RECURSION* -- gleich beim ersten mal.

var_dump() braucht eine Hierarchie-Ebene länger, bevor es mitbekommt, dass da das gleiche Array immer und immer wieder durchlaufen wird.

var_export() dagegen, scheint munter weiterlaufen zu wollen, wird aber irgendwann mit der Fehlermeldung "Fatal error: Nesting level too deep - recursive dependency?" abgebrochen.

Jetzt meine Frage: Wenn ich mir eine solche Funktion wie beispielsweise print_r() selbst schreiben möchte, wie kann ich erkennen, ob ein zu durchlaufendes Array schon mal durchlaufen wurde? Das PHP-Manual schweigt sich dazu aus. Es scheint auch nicht möglich sein, zu erkennen, ob eine Variable eine Referenz oder die "Original-Variable" darstellt.

Ach, ja: array_walk_recursive() hab ich mir schon angeschaut. Allerdings gibts das erst unter PHP5 als eingebaute Funktion, und das array_walk_recursive() aus PEAR_Compat hat keinerlei Abfragen nach Referenzen oder Endlos-Verschachtelungen eingebaut.

MffG
EisFuX

--
Erinnert mich ein Bisschen an die if-then-Schleifen von Java.
(Alexandra Freund, IT-Compactkurs)
  1. Hallo,

    ein ähnliches Problem hast Du, wenn Du ein PPS oder WWS aufbaust, dass Baugruppen kennt. Eine Baugruppe kann niemals Bestandteil ihrer Selbst sein, auch nicht in einem untergeordneten Zweig.

    Du musst also versuchen, die Bäume nachzuzeichnen. Wenn im nachgezeichneten Baum ausgehend vom Einfügeort Richtung Wurzel die ID des Elementes schon einmal vorkommt, liegt Rekursion vor.

    Leider kommst Du an die "ID" der Variable in diesem Fall nicht heran, denn die würde durch die reale Speicheradresse symbolisiert. Und durch die Einführung von Aliasen (indirekte Verweise) wird es dann nochmals komplizierter.

    LG
    Chris

    1. Hallo Chris,

      wenigstens einer, der geantwortet hat ...

      Leider kommst Du an die "ID" der Variable in diesem Fall nicht heran, denn die würde durch die reale Speicheradresse symbolisiert. Und durch die Einführung von Aliasen (indirekte Verweise) wird es dann nochmals komplizierter.

      So wie es aussieht, stellt in PHP(4+5) jede Variable eine Referenz (und zwar auf die interne "ID" oder Speicherplatzadresse) dar. Deshalb kann man nicht herausfinden, ob es sich bei einer bestimmten Variablen um eine "echte" oder nur eine "referenzierte" handelt. Da bleibt mir anscheinend doch nichts anderes übrig, als den Output von print_r() zu parsen und nach "*RECURSION*" zu suchen. Irgendwie finde ich, dass das keine besonders tolle Idee ist ...

      MffG
      EisFuX

      --
      Erinnert mich ein Bisschen an die if-then-Schleifen von Java.
      (Alexandra Freund, IT-Compactkurs)
      1. Hallo EisFuX,

        So wie es aussieht, stellt in PHP(4+5) jede Variable eine Referenz (und zwar auf die interne "ID" oder Speicherplatzadresse) dar. Deshalb kann man nicht herausfinden, ob es sich bei einer bestimmten Variablen um eine "echte" oder nur eine "referenzierte" handelt. Da bleibt mir anscheinend doch nichts anderes übrig, als den Output von print_r() zu parsen und nach "*RECURSION*" zu suchen. Irgendwie finde ich, dass das keine besonders tolle Idee ist ...

        Du kannst den Baum trotzdem nachbauen, und debei erstmal nach gleichen Namen oder Namensteilen suchen (von hinten auflösen).

        Das funktioniert dann so ähnlich, wie im Domain Name Service. Dort kannst Du schließlich auch Rekursionen bauen.

        LG
        Chris

        1. Hallo Chris,

          Du kannst den Baum trotzdem nachbauen, und debei erstmal nach gleichen Namen oder Namensteilen suchen (von hinten auflösen).
          Das funktioniert dann so ähnlich, wie im Domain Name Service. Dort kannst Du schließlich auch Rekursionen bauen.

          Naja, aber in (verschachtelten) PHP-Arrays sind ja gleiche Namen (Keys) durchaus erlaubt, wenn sie sich in verschiedenen Hierachie-Ebenen befinden. Und gleiche Namen deuten ja noch nicht auf identische Array-Einträge hin. Das würde eher in "Raten und Vermuten" ausarten.

          Ich habe das jetzt etwas anders gelöst. Ich benutze die Ausgabe von print_r($array, 1), und vergleiche in jeder Hierarchie-Ebene die dort auftretenden Arrays und Objekte mit dieser Ausgabe. Ein übler Workaround (besser: "Würgaround"), aber er funktioniert für meine Zwecke, weil er die endlose Rekursion verhindert. Es gibt allerdings ganz, ganz seltene Ausnahmefälle, die Probleme machen könnten.

          Ich räume mal den Quellcode auf, dann poste ich ihn hier rein. Könnte ja sein, dass jemand was damit anfangen kann ...

          MffG
          EisFuX

          --
          ... Suchmaschinen-Blog ...
          1. echo $begrüßung;

            Du kannst den Baum trotzdem nachbauen, und debei erstmal nach gleichen Namen oder Namensteilen suchen (von hinten auflösen).

            Naja, aber in (verschachtelten) PHP-Arrays sind ja gleiche Namen (Keys) durchaus erlaubt, wenn sie sich in verschiedenen Hierachie-Ebenen befinden. Und gleiche Namen deuten ja noch nicht auf identische Array-Einträge hin. Das würde eher in "Raten und Vermuten" ausarten.

            Da ein

            $a = array(
                array('key' => 'value'),
                array('key' => 'value'));

            problemlos (aus Sicht von PHP) verwendet werden kann (man beachte das doppelte 'key'), ist dies wirklich keine Lösung.

            Ich bin immer noch dagegen, von Hierarchie-Ebenen oder Dimensionen bei PHP-Arrays zu sprechen. Dadurch wird man nur zu sehr davon abgelenkt, dass ein PHP-Array einfach nur ein Gebilde ist, das mehrere Werte zusammenfassen kann. Dass diese Werte einfachen oder komplexen Typs sein können, sollte bei der Betrachtung eines Array keine Rolle spielen. Diese Eigenschaften spielen erst dann eine Rolle, wenn die einzelnen Werte an sich und für sich selbst betrachtet werden.

            Als ich aufgrund der ursprünglichen Frage nach einer Lösung suchte, bin ich auf der debug_zval_dump-Handbuchseite auf einen Link gestoßen, der sich mit der Variablen- und Referenzenverwaltung von PHP befasst: References Explained (by Derick Rethans). Danach bin ich zu dem Schluss gekommen, dass wohl nur eine Erweiterung von PHP die für das Problem fehlenden Funktionen is_reference() get_all_references() liefern können wird.

            echo "$verabschiedung $name";

            1. echo $begrüßung;

              printf('%s %s', $grussformel, $name); // echo find ich irgendwie doof ;-)

              Da ein

              $a = array(
                  array('key' => 'value'),
                  array('key' => 'value'));

              problemlos (aus Sicht von PHP) verwendet werden kann (man beachte das doppelte 'key'), ist dies wirklich keine Lösung.

              Da müsste ich jetzt mal an genau so einem Beispiel-Array meinen Ansatz zur Erkennung zirkulärer Referenzen testen ...

              Ich bin immer noch dagegen, von Hierarchie-Ebenen oder Dimensionen bei PHP-Arrays zu sprechen. Dadurch wird man nur zu sehr davon abgelenkt, dass ein PHP-Array einfach nur ein Gebilde ist, das mehrere Werte zusammenfassen kann. Dass diese Werte einfachen oder komplexen Typs sein können, sollte bei der Betrachtung eines Array keine Rolle spielen. Diese Eigenschaften spielen erst dann eine Rolle, wenn die einzelnen Werte an sich und für sich selbst betrachtet werden.

              Naja, ich benutze den Begriff Hierachie, weil ich ein solches zusammengefasstes Gebilde ähnlich wie ein hierarchisch strukturiertes   Verzeichnis (mit Unterverzeichnissen eben) "durchforsten" kann: das Verzeichnis mit opendir() und readdir() und das Array-"Gebilde" eben mit foreach(). Es gibt ja auch die Bezeichnungen Array-of-Array oder Hash-of-Hash -- nur die treffen den Kern der Sache auch nicht wirklich ... Ich wäre aber für eine exakte Begriffsbezeichnung durchaus dankbar. ;-) Dann kann man sich wenigstens klar ausdrücken, wenn man Fragen zum Thema stellt.

              Als ich aufgrund der ursprünglichen Frage nach einer Lösung suchte, bin ich auf der debug_zval_dump-Handbuchseite auf einen Link gestoßen, der sich mit der Variablen- und Referenzenverwaltung von PHP befasst: References Explained (by Derick Rethans).

              Muss ich mir mal durchlesen ...

              Falls ich PHP(4/5) richtig verstanden habe, ist jede Variable eine Referenz, oder genauer gesagt: Jede Variable besteht aus ihrem "Inhalt" und mindestens einem Zeiger (Referenz) auf diesen Inhalt.

              Danach bin ich zu dem Schluss gekommen, dass wohl nur eine Erweiterung von PHP die für das Problem fehlenden Funktionen is_reference() get_all_references() liefern können wird.

              Nicht schön. Denn man kann beim Shared-Hosting selten PHP-Extensions einbinden. Außerdem müsste man erstmal eine solche Extension schreiben. Ich interessiere mich für das Ganze auch nur, weil die PHP-Funktionen wie print_r() und var_dump() zwar Informationen liefern, aber jede Funktion hat ihre Schwächen. So zeigt print_r() beispielsweise alle skalaren Variablen als String. Das sieht beispielsweise beim Typ BOOLEAN nicht toll aus. Auf der anderen Seite var_dump(): Hier wird zwar der Typ einer Variablen angegeben und auch der Wert korrekt, dafür ist die Ausgabe in einen String nicht möglich (oder nur über den Umweg mit Output-Buffering). Eine Funktion, die die Vorteile beider vereint, gibt es nicht. Ach ja, und var_export() erkennt zumindest unter PHP4 die "zirkulären Referenzen" nicht.

              echo "$verabschiedung $name";

              printf('%s%s', $verabschiedung, $my_name);  
              exit();
              
              --
              ... Suchmaschinen-Blog ...