PHP: Parser für XML-Daten programmieren

Von am 29.11.2005

Wer XML-Daten anbietet, kann sich nicht darauf verlassen, dass der Browser bereits XML beherrscht. Besser ist es, die Daten auf dem Server umzuwandeln. Dieser in Internet Professionell veröffentlichte Artikel zeigt, wie Sie PHP als Parser für XML-Dateien einsetzen.

Ob im Internet oder vernetzten Anwendungen. XML ist die Universalsprache für den Datenaustausch. PHP unterstützt XML auf mehreren Wegen. Eingebaut ist ein Parser für XML-Dokumente und DOM, ein objektorientierter Ansatz, XML zu lesen und zu schreiben. Ebenfalls an Bord: WDDX, ein Standard zum Austauschen von Daten via XML zwischen Programmiersprachen wie PHP, Perl, Java oder ASP. Der Schwerpunkt dieses Beitrags liegt auf Expat, der XML-Schnittstelle von PHP.

Das erste Programm

Das erste Programm-Beispiel überprüft die Syntax eines XML-Dokumentes. Bei einem Fehler erscheint die entsprechende Meldung. Wenn alles stimmt, erscheint nichts. Die hier verwendete Routine bildet die Basis für alle weiteren Beispiele in diesem Artikel.

Speichern Sie die folgenden Zeilen unter dem Namen xml1.php:

<?

$xml_filename="musik.xml";

$xml_parser_handle = xml_parser_create();

if (!($parse_handle = fopen($xml_filename, 'r'))) {

die("FEHLER: Datei $xml_filename nicht gefunden.");

}

while ($xml_data = fread($parse_handle, 4096))

{

if (!xml_parse($xml_parser_handle, $xml_data, feof($parse_handle)))

{

die(sprintf('XML error: %s at line %d',

xml_error_string(xml_get_error_code($xml_parser_handle)),

xml_get_current_line_number($xml_parser_handle)));

}

}

xml_parser_free($xml_parser_handle);

?>

Die erste Zeile definiert den Namen der zu überprüfenden Datei. Danach stellt das Programm einen Handle für den XML-Parser her. Die Funktion xml_parser_create() versteht als Parameter den Zeichensatz. Als Default dient ISO-8859-1. Alternativ stehen US-ASCII und UTF-8 zur Verfügung.

Ein zweiter Handle namens $parse_handle ebnet den Weg zur XML-Datei. Die wird eingelesen und in der while-Schleife durchgearbeitet. Die Funktion xml_parse benötigt als Parameter den Parser-Handle sowie die ausgelesenen Daten. Sobald die Funktion auf das Dateiende stößt, beendet sie ihre Arbeit.

Wenn alles klar geht, gibt xml_parse das Ergebnis TRUE zurück. Falls ein Fehler auftritt, erzeugen xml_error_string und xml_get_current_line_number eine Fehlermeldung. xml_error_string benötigt seinerseits den Fehlercode, den xml_get_error_code aus dem Handle ermittelt.

Um das Programm auszuprobieren, benötigen Sie die Datei musik.xml. Die sieht so aus:

<?xml version="1.0"?>

<musik>

<cd>

<titel>State of Confusion</titel>

<band>Kinks</band>

<laenge>41:29</laenge>

<preis>12,80 DM</preis>

</cd>

<cd>

<titel>Strangers in the Night</titel>

<band>UFO</band>

<laenge>109:26</laenge>

<preis>29,90 DM</preis>

</cd>

</musik>

XML in HTML ausgeben

Eine der vorrangigen Aufgaben der XML-Funktionen in PHP ist das Aufbereiten von XML-Daten in HTML. Der Parser ist ereignisorientiert. Sobald er ein XML-Tag findet, stößt er eine Aktion an, ebenso wenn er auf das Ende eines Tags trifft. Diese Aktion zu gestalten, bleibt dem Programmierer überlassen. In den meisten Fällen wird man das XML-Tag mit einer Formatierungsanweisung in HTML ersetzen. Und für die Daten zwischen den Tags, CDATA genannt, gibt es ebenfalls ein Ereignis und eine Aktion.

Diese Aktionen bestehen aus Funktionen. Das folgende Beispiel xml2.php zeigt, wie es geht:

<?

$xml_filename="musik.xml";

$xml_parser_handle = xml_parser_create();

function startTag($parser, $name, $attrs)

{

echo "Anfangs-Tag <$name><br>";

}

function endTag($parser, $name)

{

echo "Abschluss-Tag </$name><br>";

}

xml_set_element_handler($xml_parser_handle, "startTag", "endTag");

if (!($parse_handle = fopen($xml_filename, 'r'))) {

die("FEHLER: Datei $xml_filename nicht gefunden.");

}

while ($xml_data = fread($parse_handle, 4096))

{

if (!xml_parse($xml_parser_handle, $xml_data, feof($parse_handle)))

{

die(sprintf('XML error: %s at line %d',

xml_error_string(xml_get_error_code($xml_parser_handle)),

xml_get_current_line_number($xml_parser_handle)));

}

}

xml_parser_free($xml_parser_handle);

?>

Die erste Funktion startTag tritt in Aktion, sobald ein Anfangs-Tag in der XML-Datei gefunden wird. Die Funktion erhält als Parameter den Namen des Parser-Handle, den Namen des Tags und in einem Array eventuelle Attribute. Die Funktion gibt lediglich den Namen des Tags aus. Eine zweite Funktion, endTag erwartet nur den Parser-Handle und den Namen des Tags. In diesem Beispiel beschränkt sich die Aktion auf die Ausgabe des Abschlusstags mit voran gestelltem Schrägstrich. Damit die Funktion in Aktion treten können, verwenden Sie xml_set_element_handler. Diese Funktion verwendet als ersten Parameter den Parser-Handle und definiert die Namen der Funktionen als Strings. Der erste Eintrag nach dem Handle nennt die Funktion für das Eingangs-Tag, der zweite die Funktion für das End-Tag. Mit diesem Handler ausgestattet, macht sich der Parser wieder an die Arbeit.

HTML-Code einsetzen

Um die XML-Anweisungen in HTML zu übersetzen, packt man die XML- und die HTML-Tags in Arrays. Davon gibt es je eines für die Anfangs und eines für die End-Tags. Bauen Sie in das Listing aus dem letzten Abschnitt diese Zeilen ein:

$arr_start=array(

"MUSIK" => "<table border=1>\n",

"CD" => "<tr>\n",

"TITEL" => "<td>",

"BAND" => "<td>",

"LAENGE" => "<td>",

"PREIS" => "<td>"

);

$arr_end=array(

"MUSIK" => "</table>\n",

"CD" => "</tr>\n",

"TITEL" => "</td>",

"BAND" => "</td>",

"LAENGE" => " min.</td>",

"PREIS" => "</td>"

);

Die Entsprechungen sind so aufgebaut, dass sich aus dem XML-Quelltext eine Tabelle ergibt. So lange sie an einer Seite programmieren, sollten Sie den Parameter border=1 verwenden. Die Tabellenränder helfen, Fehler zu erkennen. Jeder CD aus musik.xml ist eine Zeile zugeordnet. Die \n verhelfen dem Quelltext zu besserer Lesbarkeit. Für das Abschluss-Tag &lt;/LAENGE&gt; steht vor &lt;/td&gt; noch min. als Erklärung für die in diesem Tag enthaltenen Angaben in Minuten.

Damit die Arrays Wirkung zeigen, schreiben Sie die Funktionen für das Start- und das End-Tag um:

function startTag($parser, $name, $attrs)

{

global $arr_start;

if($arr_start[$name])

{

echo $arr_start[$name];

}

else

{

echo "<$name> ???<br>";

}

}

function endTag($parser, $name)

{

global $arr_end;

if($arr_end[$name])

{

echo $arr_end[$name];

}

else

{

echo "</$name> ???<br>";

}

}

Importieren Sie die jeweils benötigten Arrays mit global in die Funktionen. Danach prüft eine IF-Anweisung, ob für das Tag ein Eintrag im Array vorhanden ist. Wenn ja, fügt die Funktion den entsprechenden HTML-Code ein. Falls nicht, erscheint statt dessen das Tag selbst gefolgt von drei Fragezeichen.

Damit auch tatsächlich die Daten zwischen den Tags dargestellt werden, braucht das Programm eine weitere Funktion:

function zeigeDaten($parser, $data)

{

echo $data;

}

Die einzige Aufgabe in dieser Funktion ist, die ihr übergebenen Zeichendaten auszugeben. Aufgerufen wird sie von einem weiteren Handler:

xml_set_character_data_handler($xml_parser_handle, "zeigeDaten");

Das vollständige Listing finden Sie unter dem Namen xml3.php.

In Array speichern

XML-Daten nimmt PHP auf Wunsch in eine eigene Datenstruktur auf. Damit lassen sich die Daten weiter verarbeiten und aufbereiten. Die Anweisung für die Übernahme in die Datenstruktur ist simpel - die Werte dort wieder heraus zu bekommen, erfordert etwas mehr Umstände. Das folgende Programm zeigt, wie es geht:

<?

$xml_filename="musik.xml";

$xml_parser_handle = xml_parser_create();

if (!($parse_handle = fopen($xml_filename, 'r'))) {

die("FEHLER: Datei $xml_filename nicht gefunden.");

}

while ($xml_data = fread($parse_handle, 4096))

{

if (!xml_parse_into_struct($xml_parser_handle, $xml_data, $werte, $index))

{

die(sprintf('XML error: %s at line %d',

xml_error_string(xml_get_error_code($xml_parser_handle)),

xml_get_current_line_number($xml_parser_handle)));

}

}

xml_parser_free($xml_parser_handle);

echo $werte[$index["BAND"][0]]["value"];

echo "<br>";

echo $werte[$index["TITEL"][0]]["value"];

foreach ($index["BAND"] as $band)

{

echo $werte[$band]["value"]."<br>";

}

echo "<pre>";

echo "Inhalt von \$werte<br>";

print_r($werte);

echo "Inhalt von \$index<br>";

print_r($index);

echo "</pre>";

?>

Für die Datenübernahme zuständig ist xml_parse_into_struct(). Als Parameter arbeiten hier der Parser-Handle, der String mit den XML-Daten sowie zwei Arrays. Das erste Array enthält die Werte aus dem Array, das zweite bildet einen Index, von aus Sie auf die Werte zugreifen.

Wie das geht, zeigen die Zugriffe auf das Array zum Beispiel mit $werte[$index["BAND"][0]]["value"]. Der erste Schlüssel enthält seinerseits das Array $index. Dessen erster Schlüssel ist der Name des gewünschten Tags, der zweite Schlüssel die Nummer des Eintrages beginnend bei 0. Der zweite Schlüssel von $werte enthält den gesuchten Eintrag. In diesem Fall ist es der String, der im ersten Eintrag der Original-Datei zwischen &lt;BAND&gt; und &lt;/BAND&gt; steht.

Die nächste Zeile holt den Eintrag aus dem ersten &lt;NAMEN&gt;-Tag und zeigt ihn. Um mehrere Einträge zu holen, könnte man es mit einer foreach-Schleife probieren. In diesem Beispiel holt sie jeden Eintrag für $index["BAND"]. Dieser Schlüssel entspricht dem XML-Tag <BAND> aus der Beispieldatei.

Wie die Arrays beschaffen sind, sehen Sie mit print_r($werte) und print_r($index). Der besseren Übersicht halber sind die Druckkommandos von &lt;pre&gt;-Tags umgeben.

XML objektorientiert

Einen weiteren Ansatz für den Umgang mit XML liefert DOM. Diese Funktionen sind laut der PHP-Dokumentation noch im Beta-Stadium. Um DOM zu verwenden, müssen Sie es mit --with-dom einkompilieren oder die entsprechende DLL in Windows verwenden. Allerdings berichten User auf Webseiten wie PHPBuilder über Probleme mit der Windows-Version - trotz korrekt eingegebener Pfade meldet PHP, dass die Datei php_domxml.dll nicht zu finden sei. Ein Lösungsansatz: Kopieren Sie die Datei libxml2.dll aus PHP\dlls in das Verzeichnis \WINDOWS\SYSTEM. Damit verschwinden zumindest die Fehlermeldungen. Verwenden Sie eine möglichst aktuelle PHP-Version.

DOM ist objektorientiert und verarbeitet XML-Daten in einer Baumstruktur. Die XML-Daten werden dabei komplett in den Speicher gelesen, aus den Tags werden Nodes -- Knotenpunkte. Für die wie für das Dokument gibt es eine Reihe Eigenschaften und Methoden. Zu den Eigenschaften eines Nodes gehören neben Namen und Typ auch der Inhalt des Tags. Die Methoden geben beispielsweise die darüber liegenden Tags (Eltern) oder die darunter liegenden "Kinder" aus. Die Methoden für das XML-Dokument erschaffen neue Root-Elemente oder liefern die komplette Datei als Dump.

Der Vorteil von DOM ist die Unabhängigkeit von der Ereignissteuerung der XML-API von PHP. Ihr Nachteil: Die Unterstützung muss extra einkompiliert werden, die Arbeit geht etwas langsamer und ist speicherintensiver.

WDDX

WDDX nennt sich die Schnittstelle zum Austausch von XML-Daten zwischen verschiedenen Programmiersprachen. Das Konzept: Die Daten werden nach einem festen Schema codiert oder "serialisiert". Der Empfänger bringt die Daten dann wieder in die richtige Form. Übergeben werden können Daten aller Art.

Ein Beispiel zeigt wie es geht:

<?

$werte = array("Oskar", "Schorsch", "Hannover");

echo "\$werte:<br>";

var_dump($werte);

echo "<br><br>";

$test=wddx_serialize_vars("werte");

echo "\$test nach serialize:<br>";

echo htmlentities($test);

echo "<br><br>";

echo "\$andere_werte liest serialisierte Daten:<br>";

$andere_werte = wddx_deserialize($test);

var_dump($andere_werte);

echo "<br><br>";

echo "\$andere_werte[\"werte\"][1]: ".$andere_werte["werte"][1];

?>

Zunächst entsteht ein simples Array mit drei Einträgen. Zur besseren Ansicht, gibt var_dump dieses Array im Browser aus. Der String $test erhält dann das Ergebnis der serialize-Version. Wichtig: Hierbei wird der Name des zu serialisierenden Arrays in Anführungszeichen übergeben. Das Ergebnis dieser Funktion ist ebenfalls zu sehen -- dank der Funktion htmlentities() bleiben dabei die Tags erhalten.

Der Empfänger der Daten deserialisiert sie wieder. Das passiert hier im selben Programm mit $andere_werte = wddx_deserialize($test);. $andere_werte ist ein Array, in dem hier das Array werte gespeichert wird. Das Ergebnis ist also ein zweidimensionales Array, auf das in der letzten Programmzeile zugegriffen wird. Als Ergebnis ist der zweite Eintrag aus dem Original-Array zu sehen.

Aktualisiert am: 29.11.2005
Dateien
phpmxl.zip


Ihr Kommentar:


Name:



Wie kann man denn CharData bearbeiten mit dem XML-Parser? Ist mir
nicht ganz klar. Denn startTag und endTag haben keine Daten. Und
zeigeDich hat keine Tags. Wenn ich also sagen will: if tag=text, then
str_replace("b","strong",$data), wie mache ich das?
[Heidi | 18.11.2008] Antworten


wie genau kann ich nun mit php eine neue cd in meine xml-datei
hinzufügen
[Marco | 19.08.2004] Antworten