| Mehrdimensionales Array hierarchisch ausgeben |
Mehrdimensionales Array hierarchisch ausgebenZum Handwerkszeug eines PHP Programmierers gehört der perfekte Umgang mit Arrays. Bereits den meisten Anfängern bereitet die Ausgabe eines eindimensionalen Arrays keine wirklichen Probleme dank der foreach-Schleife. Komplizierter wird es dann schon, wenn man ein mehrdimensionales Array ausgeben muss. Regelrecht zum Haare raufen wird es, wenn das über 3, 4 oder mehr Ebenen in einer hierarchischen Struktur geschehen soll. Dann benötigt man etwas, dass häufig selbst Fortgeschrittenen kalte Schauer über den Rücken laufen lässt: Rekursion! Den Begriff Rekursion kennen die meisten sicherlich bereits. Darunter versteht man in der Programmiererei, dass sich eine Funktion selbstständig erneut aufruft, bis die Abbruch-Bedingung eintritt. Hier liegt auch die Gefahr. Arbeitet die Rekursion nicht hunderprozentig, landet das Script in einer Endlosschleife, was in letzter Konsequenz den Absturz des gesamten Webserver zur Folge haben kann. Mit PHP 5 wurde die SPL eingeführt, die einem die Arbeit mit Arrays enorm erleichtert. Anhand von zwei kleinen Beispielen möchte ich kurz zeigen, wie man sich viel Zeit und Stress sparen kann, wenn man ein mehrdimensionales Array hierarchisch ausgeben möchte. Hier zunächst das Array mit dem wir arbeiten wollen: $daten = array ( "produkte" => array ( "elektronik" => array ( "tv" => array ( "LG Electronics", "Samsung", "Toshiba", "Sony", ), "video" => array ( "DVD" => array ( "Sony", "Panasonic", "Philips", ), "Blu-ray" => array ( "Samsung", "Panasonic", "Philips", "Toshiba", ), ), ), "baumarkt" => array ( "renovieren" => array ( "Tapeten", "Farben", "Bodenbeläge", ), "baustoffe" => array ( "Beton", "Dämmstoffe", ), "arbeitskleidung" => array ( "Gehörschutz", "Sicherheitsschuhe", "Schutzbrille", ), ), ), "service" => array ( "Beratung", "Bestellung und Versand", "Telefon Hotline", "FAQ", ), ); Wie unschwer zu erkennen ist, könnten das die extern gepflegten Menüpunkte einer Navigation für einen Online Shop sein. Manche Zweige sind flach, andere verzweigen sich bis in die 4. Ebene. Die einfache Ausgabe der einzelnen Einträge in einer Schleife, auch Iteration, bzw. iterieren genannt, ist mit klassischen Mitteln (rekursiv arbeitende Funktion) noch relativ einfach zu lösen. Erheblich komplizierter wird es allerdings, wenn wir wirklich eine HTML Liste für z.B. ein Menü daraus erstellen wollen. Einfache Ausgabe mithilfe der SPL Wir erzeugen aus unserem Array also ein RecursiveArrayIterator Objekt, das wir an die RecursiveIteratorIterator Klasse übergeben. Diese Klasse stellt uns erneut Methoden zur Verfügung, um auf jedes einzelne Element aus egal welcher Ebene zugreifen zu können. Erzeugen wir also erst mal unser Objekt: // Iterator Objekt mithilfe der SPL Iterator-Klassen erzeugen $datenarray = new RecursiveArrayIterator( $daten ); $iterator = new RecursiveIteratorIterator( $datenarray, TRUE ); Soweit, sogut. Das zweite Argument TRUE, dass an den RecursiveIteratorIterator übergeben wird hat den Zweck, dass auch der Name das erste Element einer Ebene ausgegeben wird, sofern es sich dabei um ein Array handelt. // Zeiger auf das erste Element setzen $iterator->rewind(); // Einfache Ausgabe des Array echo "<pre>"; foreach ($iterator as $schluessel => $wert) { $ausgabe = (is_array( $wert )) ? $schluessel : $wert; $einruecken = str_repeat( " ", $iterator->getDepth() ); echo $einruecken . $ausgabe . "\n"; } echo "</pre>"; Tja, das war's schon. In der ersten Zeile setzen wir den internen Zeiger auf das erste Array Element. Anschließend durchlaufen wir das Array in einer einfachen foreach-Schleife. In $ausgabe halten wir den Name des Elements fest, auf dem sich der Zeiger gerade befindet. Würden wir immer nur $wert ausgeben, so hätten wir bei jedem Ebenenwechsel einfach nur Array da stehen, statt dem Name der Ebene. produkte
elektronik
tv
LG Electronics
Samsung
Toshiba
Sony
video
DVD
Sony
Panasonic
Philips
Blu-ray
Samsung
Panasonic
Philips
Toshiba
baumarkt
renovieren
Tapeten
Farben
Bodenbeläge
baustoffe
Beton
Dämmstoffe
arbeitskleidung
Gehörschutz
Sicherheitsschuhe
Schutzbrille
service
Beratung
Bestellung und Versand
Telefon Hotline
FAQWenn wir aber eine HTML Liste (ul) aus den Array Elementen generieren wollen, müssen wir den Code noch etwas erweitern. Dazu reicht es allerdings nicht ganz aus in der Ausgabe ein paar HTML-Tags zu ergänzen, sondern wir müssen die Basis-Klasse erweitern. Erweiterte Ausgabe als HTML Liste // Basis-Klasse erweitern class ArrayAusgabe extends RecursiveIteratorIterator { public $endChildren = ''; // Konstruktor der Basis-Klasse aufrufen public function __construct( Traversable $iterator, $modus ) { parent::__construct( $iterator, $modus ); } // Methode wird automatisch von der Basis-Klasse aufgerufen // wenn die Iteration über eine Ebene endet public function endChildren() { $this->endChildren .= "</ul>\n"; } } Der einzige Grund, weshalb wir die Klasse erweitern ist der, dass wir unsere verschachtelten Listen schließen müssen, um semantisches HTML zu erhalten. Die Methode endChildren() wird immer dann automatisch von der RecursiveIteratorIterator Klasse aufgerufen, wenn keine weiteren Kind-Elemente mehr vorhanden sind. Oder anders ausgedrückt, wenn in einem Array keine Elemente mehr vorhanden sind und eine Ebene höher gewechselt wird. Das ist auch der Moment in dem wir unsere ul schließen müssen. // Iterator Objekt erzeugen $datenarray = new RecursiveArrayIterator( $daten ); $iterator = new ArrayAusgabe( $datenarray, TRUE ); // Zeiger auf das erste Element setzen $iterator->rewind(); // Unordered List (ul) öffnen echo "<ul>\n"; // Iterator-Objekt rekursive in einer Schleife ausgeben while ($iterator->valid()) { // Prüfen ob ein Kind (Sub-Array) existiert if ($iterator->callHasChildren()) { // Schlüssel ausgeben und ul öffnen echo "<li>" . $iterator->key() . "<ul>\n"; } // Einträge des Array ausgeben else { echo "<li>" . $iterator->current() . "</li>\n"; } // Zum nächsten Array-Eintrag gehen $iterator->next(); // Prüfen ob RecursiveIteratorIterator->endChildren() aufgerufen wurde if (isset( $iterator->endChildren )) { // ul schließen echo $iterator->endChildren; // Eigenschaft resetten unset( $iterator->endChildren ); } } // ul schließen echo "</ul>\n"; Die HTML Ausgabe, mit korrekter Verschachtelung der Listen, kann man auf der Demoseite bewundern. Bis zum nächsten Mal, Beispiel herunterladen (1 kB) |