Donnerstag, 4. Dezember 2008

entwickler.com Magazine Konferenzen Entwickler Akademie Entwickler-Forum Jobbörse Bücher
Software & Support Verlag





April 2006
aus PHP Magazin Ausgabe: 05.2004
Ganz einfach XML ...
Die neue SimpleXML-API von PHP 5 im Einsatz
von Arne Blankerts

Mit Freigabe der Version 5 von PHP verschwindet auch das letzte Argument gegen den Einsatz von XML: Die Ausrede zu kompliziert gilt nicht mehr, denn die ab jetzt integrierte und standardmäßig aktivierte Schnittstelle SimpleXML ermöglicht, passend zum Namen, die einfache Verarbeitung von XML-Strukturen.


Lange hat es ja gedauert: Mit der Version 4 von PHP wurde zuerst das ereignisgesteuerte Verarbeiten von XML über expat [1][2] standardmäßig aktiviert - verfügbar war die mit --with-xml einzubindende Erweiterung bereits seit Version 3.0.6. Die Anbindung der immer populärer gewordenen libXML2 des Gnome Projekts [3] zur DOM-basierten Behandlung folgte, ist dem experimentell-Status aber leider nie wirklich entwachsen [4], selbst wenn sie in der aktuellen Version 4.3.5 durchaus als stabil einzustufen ist. Auch die API zur Verarbeitung von XSL hat stürmische Zeiten hinter sich: so wurde die Ansteuerung des XSL-Prozessors Sablotron [5] mehrfach umgeschrieben und auch die Unterstützung der über die libXML2 angebundenen libxslt [6] war nur rudimentär.

Mit PHP 5 wurde dies nun endlich aufgeräumt und nicht nur das. Die inzwischen vollständig und ausschließlich auf der libXML2 (sowie libxslt zur XSL-Verarbeitung) basierende Implementierung ist geradezu perfekt mit PHP verschmolzen und ermöglicht Dinge, von denen man früher nur träumen konnte. Neben der optimalen Verzahnung von XML, XSL und PHP ist die große Neuerung vor allem die neue Erweiterung simpleXML[7], der sich dieser Artikel widmet.

Startschuss
Wie eingangs erwähnt, gibt es im Grunde zwei Ansätze, um XML zu verarbeiten: Ereignis-gesteuert, d.h. über Handler, welche auf das Öffnen und Schließen von Tags beim Parsen reagieren oder die Abbildung in der Baumstruktur des DOM. Etwaige Lösungen, die XML wie einen einfachen Text behandeln und versuchen, per Regular Expressions oder anderen Methoden auf Daten und Inhalte zuzugreifen, dürften kaum in der Praxis anzutreffen sein, geschweige denn performantes Arbeiten ermöglichen.

Zum gezielten Auffinden oder Verarbeiten von Daten ist ein ereignisorientierter Ansatz wohl ebenfalls schon auf den ersten Blick wenig sinnvoll. Daher hat sich für die meisten Anwendungsgebiete der Einsatz von DOM und XPath als Standard etabliert (vgl. PHP Magazin 5.03, Kreuzwege). Wem das alles noch zu kompliziert ist oder war, der findet vielleicht in SimpleXML den passenden Schlüssel zur Welt der eXtensible Markup Language.

SimpleXML definiert für jeden Node im Dokument ein eigenes Objekt, welches mit seinen Eltern- und Kind-Elementen verknüpft wird. All dies geschieht beim Laden automatisch und für den (PHP-) Entwickler vollkommen transparent im Hintergrund. An der einmal erstellten Objektkette braucht man sich dann nur noch, wie bei der objektorientierten Programmierung üblich, mit ->, entlang zu hangeln:

$phpmag=simplexml_load_string($xml);
echo $phpmag->ausgabe->inhalt[0];

Der obige Beispielcode würde unter Verwendung der in Listing 1 definierten XML-Struktur die Kurzbeschreibung dieses Artikels ausgeben. Ist doch ganz einfach, oder? Und Sie dachten, XML-Verarbeitung ist schwer ...

Doch Vorsicht, die flexible Handhabung von Variablentypen in PHP hat uns hier soeben den Hals gerettet. Denn eigentlich bezeichnet das Konstrukt $phpmag->ausgabe->inhalt[0] keinen String, sondern ein Objekt vom Typ simplexml_element, dessen Wert hier eher zufällig als String verwendet werden kann. Die Notwendigkeit eines so genannten Typecasts, das Erzwingen einer bestimmten Typisierung, wird besonders bei einem Vergleich sofort auffällig:

$test='Hallo Welt';
if ($phpmag->ausgabe->inhalt[0] == $test ) { ... // funktioniert nicht wie erwartet

Soll hier jetzt ein Objekt mit einem String verglichen werden oder doch eher die beiden String-Werte? Für PHP ist die vermeintlich logische Antwort auf diese Frage nicht zu ermitteln und muss daher vorgegeben werden:

if ((string)$phpmag->ausgabe->inhalt[0] == $test )  { ... // so geht's

Doch kommen wir noch einmal zurück zum Laden eines Dokuments. Anstatt das XML aus einem String zu lesen, kann selbstverständlich auch auf eine Datei als Datenquelle zurückgegriffen werden:

$phpmag=simplexml_load_file('datei.xml');

Befindet sich das Dokument bereits als DOM-Objekt im Speicher, kann auch dieses verwendet werden, indem man es schlichtweg importiert:

$dom = new domDocument;	$dom->loadXML($xml);
$phpmag=simplexml_import_dom($obj);

Importiert werden kann nicht nur das root-Objekt, sondern auch ein vorher ausgewählter Teilbereich - z.B. ein Node aus einer über DomXML ausgeführten Xpath-Anfrage. Der Begriff importiert ist hier eigentlich falsch, da das Dokument selbst nicht verändert wird, sondern lediglich eine neue Objekt-Ansicht auf die Daten erzeugt wird.

Suchet, so werdet ihr finden ...
Die einfache Struktur ist zwar schön, aber was tun, wenn die Position eines Elements nicht so klar und einfach wiedergegeben werden kann, vielleicht gar unbekannt ist? Spätestens ab hier führt mal wieder kein Weg an XPath vorbei, welches wiederum direkt als Methode in die SimpleXML-Architektur integriert wurde und eine Liste der passenden SimpleXML-Objekte zurück liefert (oder false, wenn keine gefunden wurden):

$phpmag= simplexml_load_string($xml);
$res = $phpmag->ausgabe[0]->xpath("inhalt/@artikel");
if (is_array($res)) {
foreach ($res as $obj) {
echo (string)$obj;
}
}

Selbst in diesem Beispiel wird deutlich: Dadurch, dass alle Elemente vom Typ simpleXML sind, steht jede Funktion auf jeder Ebene zur Verfügung. Wenn man die anfallende Suchzeit bei komplexen Dokumenten einmal außen vor lässt, ist es für PHP egal, ob der xpath-Aufruf in der obersten Ebene () oder an einer beliebigen Stelle einer verschachtelten Struktur ansetzt.

Das direkte Verschachteln sowie das Integrieren von xpath()-Aufrufen in eine Kette ist leider nicht möglich, da der Rückgabewert grundsätzlich vom Typ Array ist - unabhängig von der Anzahl der ermittelten, passenden Einträge.
Neben der gerade demonstrierten Möglichkeit, Attribute via Xpath()-Anfrage aus der Struktur zu fischen, gibt es auch zwei native Methoden. Zum Einen - wenig erstaunlich - die Methode Attribute(). Diese liefert ein mit Hilfe von while, list oder foreach auswertbares Konstrukt zurück:

$phpmag= simplexml_load_string($xml);
foreach ($phpmag->ausgabe->attributes() as $name => $wert) {
echo "Attribute '$name' mit Wert '$wert'<br/>\n";
}

Im Übrigen handelt es sich bei dem Rückgabewert nicht, wie man vielleicht meinen möchte, um ein gewöhnliches Array, sondern um einen in PHP 5 ebenfalls neu eingeführten Iterator: Jeder Zugriff auf ein solches Element verschiebt automatisch den internen Array-Zeiger um eins nach vorne. Besonders fatal wirkt sich dies auf den Aufruf von var_dump() aus, denn anstatt eine vollständige Liste zu bekommen, wird nur der gerade aktuelle Wert ausgegeben und dabei auch noch der Zeiger bewegt, sodass eine nachfolgende Funktion bereits das nächste Element verwendet.

Ist der Name des Attributs bekannt, so kann auf dieses über den Namen als Array-Key zugegriffen werden:

$phpmag= simplexml_load_string($xml);
echo $phpmag->ausgabe['jahr']; // liefert 2004

Diese Syntax sollte nicht mit einem $phpmag->ausgabe->jahr-Aufruf verwechselt werden, welcher nicht das Attribut jahr vom Element ausgabe bezeichnet, sondern ein Kindelement gleichen Namens.

Nicht nur auf Attribute eines Elements kann zugegriffen werden, denn die Schwester-Methode children() liefert alle Kindelemente des betreffenden Node auf die selbe Art und Weise zurück, wie attribute() es für die Attribute tut. Das nachfolgende, kombinierte Beispiel verdeutlicht dies:

$phpmag= simplexml_load_string($xml);
foreach ($phpmag->children() as $nodename => $obj) {
echo "Element '$nodename':<br/>\n";
foreach ($obj->attributes() as $name => $wert) {
echo "-- Attribute '$name' mit Wert '$wert'<br/>\n";
}
}

Read only?
Auch wenn der weitaus größte Teil der Funktionalität von SimpleXML auf das möglichst unkomplizierte Finden und Lesen von Information ausgelegt ist, lässt sich mit der SimpleXML-API auch schreibend auf das Dokument zugreifen. Leider ist dies bisher auf das Setzen von Werten und Attributen beschränkt; das Handling von Mixed-Content, CDATA- oder PI-Blöcken ist nicht vorgesehen. Die grundsätzliche Syntax zum Schreiben von Werten ist dafür umso einfacher - man folgt der gewohnten PHP-Nomenklatur:

$phpmag=simplexml_load_string($xml);
$phpmag->ausgabe[0]->inhalt='Dies ist ein neuer Inhalt';
$phpmag->ausgabe[0]->inhalt['autor']='Reiner Zufall';

Spätestens nachdem man auf diese Weise Veränderungen vorgenommen hat, ist es natürlich sinnvoll, das in der Objekt-Verkettung gespeicherte Dokument wieder auf Festplatte zu sichern oder zumindest in einen XML-String auszugeben. Dies wird wenig spektakulär durch die Methode asXML() veranlasst:

// als String ...
$phpmag->asXML();
// ... oder direkt in eine Datei
$phpmag->asXML('datei.xml');

Auch hier gilt die freie Auswahl der Node - es besteht keine zwingende Veranlassung, nur von der Root-Ebene auszugehen. Auch ein beliebiger Teilbereich kann in einen String verwandelt werden:

$phpmag= simplexml_load_string($xml);
$res = $phpmag->ausgabe[0]->xpath("inhalt");
if (is_array($res)) {
foreach ($res as $obj) {
echo $obj->asXML();
}
}

Zuviel des Guten?
Der Interessenkonflikt, der entsteht, wenn versucht wird, eine tendenziell eher komplexe Funktionalität hinter einer einfachen API zu verbergen, ist so alt wie die Softwareentwicklung selbst. Die daraus entstehenden Probleme sind es leider genauso und auch SimpleXML leidet in seiner derzeitigen Form ein wenig an der auferzwungenen Simplifizierung. So ist es zur Zeit offensichtlich (noch?) nicht möglich, mehrere Dokumente zu verknüpfen oder eine bestehende Struktur umzubauen, ohne doch wieder auf DOM-Funktionen zurückzugreifen.

Da man über die noch undokumentierte Funktion dom_import_simplexml($obj); jedoch mit vergleichsweise geringem Aufwand aus einem SimpleXML-Objekt ein DOM-Objekt erzeugen kann, lässt sich diese Einschränkung vielleicht verschmerzen. Neu geparst werden muss das Dokument bei dieser Umwandlung zum Glück nicht, denn SimpleXML und DOM können den gleichen Datenpool verwenden. Über die beiden import-Funktionen wird hier, wie bereits im obigen Teil angedeutet, lediglich eine weitere Objektinstanz mit den inhaltlich gleichen Daten erzeugt (Stichwort: Referenz). Eine sehr sinnvolle und speicherschonende Vorgehensweise, von der daraus resultierenden Einsparung an Rechenzeit ganz zu schweigen. Wie das in der Praxis funktionieren könnte, zeigt das folgende Beispiel. Das SimpleXML-Objekt des Nodes Ausgabe wird an das DOM-Subsystem übergeben, dieses löscht daraufhin den ersten Node inhalt. Der Aufruf der Methode asXML() vom ursprünglichen SimpleXML-Objekt beweist, auch hier ist der Node inhalt bereits verschwunden:

$phpmag=simplexml_load_string($xml);

$dom=dom_import_simplexml($phpmag->ausgabe[0]);

$child=$dom->getElementsByTagname('inhalt');
$dom->removeChild($child->item(0));

echo $phpmag->asXML();

Finale
Mit der Einführung von SimpleXML hat das PHP-Entwicklerteam eine großartige API entworfen, die vielen Programmierern das Leben und die Einführung von XML erleichtern wird. Wenn auch noch die eine oder andere Funktionalität schmerzlich vermisst wird, so lassen sich doch viele alltägliche Arbeiten mit einer deutlich vereinfachten Syntax erledigen, was sicherlich auch vielen alten Hasen gefallen wird.

Im Zusammenspiel mit der SOAP-Implementierung könnte hier speziell im WebService-Bereich ein neues Dreamteam an Erweiterungen entstehen, das die Entwicklung eigener Services in Rekordzeit ermöglicht.
Man darf also gespannt sein, wann PHP 5 auf breiter Basis installiert sein wird.

Arne Blankerts (theseer@php.net) ist Leiter der Entwicklung bei der Sales Emotion GmbH in Hamburg.

Die in allen Beispielen verwendete XML-Struktur:

<?php

$xml =<<<EOF
<?xml version="1.0" encoding="iso-8859-1" ?>
<phpmag>
<verlag name="Software und Support Verlag" />
<ausgabe jahr="2004" nr="4" cd="Y">
<inhalt artikel="ganz einfach XML">
Eine Einführung zur SimpleXML API von PHP 5
</inhalt>
</ausgabe>
</phpmag>
EOF;

?>


Links


    Hat Ihnen dieser Artikel gefallen? Dann abonnieren Sie das PHP Magazin direkt über unser

zur vorherigen Seite
zurück
an den Anfang der Seite
nach oben
Diesen Artikel drucken
drucken
Diesen Artikel weiterempfehlen
empfehlen

Software & Support Verlag GmbH