XML-Line Tutorial

Autor: Peter Bieling © 2004

 1  Erstes Beispiel

Dieses Tutorial ist bewusst einfach gehalten, um auch Einsteigern die Möglichkeit zu geben, XML mit Hilfe von PHP in ihren Anwendungen zu nutzen. Wer sich unterfordert fühlt, kommt vielleicht über die PDF-Dokumentation, die die Methoden auflistet, schneller zum Ziel.

Wenn Sie die Datei xml-line.zip herunterladen, finden Sie im Ordner examples nicht nur einfache sondern auch weiterführende Beispiele, die teilweise hier nur angerissen oder gar nicht erwähnt sind, weil sie eventuell nachträglich eingefügt wurden. Dem jetzige Downloadpaket liegt die Version 0.3.5 bei, deren neue Methoden noch nicht dokumentiert sind. Infos und Beispiele gibt es im Ordner examples-xml-line03.
Kopieren Sie das entpackte Verzeichnis xml-line (mit den Unterverzeichnissen) am besten in eine lokale Serverumgebung die PHP unterstützt, und die Beispiele sollten lauffähig sein. Möglicherweise müssen noch Schreibrechte für das Verzeichnis output vergeben werden.

Ein erstes vollständiges Beispiel soll zeigen, wie XML-line verwendet wird. Dafür soll uns eine einfache XML-Datei mit ziemlich beliebigem Inhalt als Versuchsobjekt dienen. Es wird dann anschließend für Sie kein großes Problem sein, Ihre eigenes realistisches Testszenario einzurichten.

<?xml version="1.0" encoding="ISO-8859-1"?>
<nahrungsmittel>
  -  <vegetarisch>
  -     -  <obst>Apfel</obst>
  -     -  <obst>Birne</obst>
  -     -  <obst>Himbeere</obst>
  -     -  <gemuese preis="gut">Möhre</gemuese>
  -     -  <gemuese>Kopfsalat</gemuese>
  -     -  <gemuese>Tomate</gemuese>
  -  </vegetarisch>
  -  <fleisch>
  -     -  <schwein>Schnitzel</schwein>
  -     -  <rind>Gulasch</rind>
  -  </fleisch>
</nahrungsmittel>

Bevor wir nun beginnen, dass Dokument zu bearbeiten, Elemente, Attribute und Texte zu ändern, beschränken wir uns für den Anfang erst einmal auf das Auslesen eines Elements. Es soll genau ein obst-Element ausgelesen werden. Da die Datei von vorn bis hinten geparst wird, wird es naturgemäß das erste Element sein, dass wir als Ergebnis erhalten werden.


<?php
require_once("../xml-line.php");
$mylines = new xml_line("xml-files/obst.xml");
$mylines->get_data(1,"obst");
$result = $mylines->xml_stream();
printf("<p>Das Element hat den Wert <b>%s</b>.</p>",
        $result[0][0][0]);
?>

Erfreulicherweise lautet das Ergebnis:

Das Element hat den Wert Apfel.

Sie finden das Beispiel auch im Verzeichnis examples im Downloadpaket als obst1.php

Das Beispiel zeigt, wie einfach unter günstigen Bedingungen die Abfrage eines Wertes sein kann. Gehen wir die Zeilen des Skripts im Einzelnen durch:
Die erste Zeile bindet die Klasse xml-line.php in das laufende Skript ein.
$mylines = new xml_line("xml-files/obst.xml"); Diese Zeile erzeugt eine Instanz dieser Klasse. Der Parameter gibt an, auf welche XML-Datei zugegriffen wird.
Jetzt erst wird es interessant:
$mylines->get_data(1,"obst"); Die Methode get_data() ermöglicht das Auslesen von Elementen. Genau gesagt, wird die Bearbeitung hier erst vorbereitet. Es können beliebig viele weitere Methoden folgen. In diesem Fall wird nur 1 Wert benötigt. Ist dieser erste Parameter auf 0 gesetzt, werden alle obst-Elemente in die Suche einbezogen. Es würden also drei Ergebnisse zurückgeliefert.
$result = $mylines->xml_stream(); startet mit xml_stream() das Parsen des Dokuments und liefert am Ende ein Ergebnisarray zurück, das Informationen zum gesuchten Element enthält. Die Struktur dieses Arrays sehen wir uns später an.

 2  Die Suchparameter im Einzelnen

Ganz so einfach kann und soll es nicht bleiben. Insgesamt können zur Suche eines bestimmten Elements in get_data() und auch in den meisten anderen bis zu 5 Parameter übergeben werden, die für das Auffinden eines oder auch mehrerer Elemente zuständig sind. Und diese können darüber hinaus sehr variabel eingesetzt werden. Selbst gemischte Inhalte, also z.B. Textteile die mit anderen Elementen abwechseln, lassen sich finden.
Die Parameter sind alle optional. Wenn Sie jedoch den 5. Parameter nutzen wollen, müssen die vorangegangenen natürlich ebenfalls gesetzt werden. Mit "" geben Sie an, dass dieser Parameter leer bleiben soll.

Die folgende Tabelle gibt Aufschluss über die Aufgabe der einzelnene Parameter.

Nr. Bedeutung Format Beispielwert Anmerkungen
1 Trefferzahl positive Ganzzahl 3 Zahl der Ergebnisse, die für diese Methode gefunden werden sollen. Im Beispiel 3. Danach wird nicht mehr weiter gesucht. Ist im Moment nur für get_data() oder get_attribute() implementiert. Setzen Sie ansonsten den ersten Parameter auf 0 oder "", wenn alle passenden Ergebnisse geliefert werden sollen.
2 Elementpfad-
Bedingung
String wurzel/vater/kind
kind
wurzel/vater/*
*/vater/*
Es ist nicht erforderlich den gesamten Pfad zum gesuchten Element anzugeben. Z.B. such get_data(0,"strong") alle Inhalte, die mit den strong-Tags ausgezeichnet wurden. Will man alle Elemente suchen, die von vater eingeschlossen sind kann man mit dem * als wildcard arbeiten. Dabei kann der * Stern durchaus für mehrere Elemente stehen, z.B. für "kind/kindeskind" oder vorher für "urgrossvater/grossvater". Will man nur eine Ebene auslesen, kann man sich mit dem 5. Parameter, dem Elementzählpfad behelfen
3 Pattern-
Bedingung
String Flasche Bier Dieser Parameter hat für die Klasse sicherlich noch Ausbaupotential. Im Moment werden z.B. mit get_data(0, "","Flasche Bier") alle Elemente gefunden, die diesen String enthalten, so z.B. auch den denkwürdigen Satz "Hol mir mal ne Flasche Bier". Wer nur die Einträge sucht, die den kompletten Satz enthalten, kann entweder die Ergebnisse filtern oder sich die Klasse entsprechend anpassen, was keine unlösbare Aufgabe ist. Noch einmal: ein leerer Parameter, den Sie mit "" setzen, bedeutet, dass keine Bedingung für den Wert gestellt wird. Es ist also egal, welcher Wert enthalten ist.
4 Attribut-
Bedingung
Array array("zustand"=>"neu", "preis"=>"3,15 EUR";) get_data(0, "Rosis_Kinder/kind", "", array("vater"=>"Robert")) findet alle Kinder von Rosi, deren Vater Robert ist. Wenn mehrere Attribute und Werte angegeben werden, müssen alle Bedingungen erfüllt sein. Der Wert muss exakt sein. "Roberts Freund" würde nicht akzeptiert. Auch hier gibt es theoretisch noch Entwicklungsmöglichkeiten. Da die Attribute aber ohnehin im Ergebnisarray mitgeliefert werden, kann man auch später noch weiter filtern.
5 Element-
Zählpfad-
Bedingung
String 1-3-2
1-3-{3 to 7}
1-3-{3,8,12}
1-3-{*}
1-{*}-2
1-3-2:4
:4
Mit dieser Bedingung steht ein mächtiges Instrument zur Verfügung, um Werte mit Hilfe einer vorher definierten Zahnung auszukämmen. get_data(1, "adressen/eintrag/name", "","","1-3-1") Da es nur ein Wurzelelement gibt, steht dieser Wert naturgemäß auf 1. Darauf folgt das Element eintrag. In diesem Fall interessiert uns aber nur, der dritte Eintrag. (Datensatz) Das Element name kommt vermutlich ohnehin nur einmal pro Eintrag vor. Wir wählen nur den ersten. (Mit dem ersten Parameter haben wir übrigens die Suche eingeschränkt, damit nicht die folgenden 3 Millionen Einträge auch noch durchsucht werden.)
Mit to kann ein Bereich angegeben werden, z.B. 3 to 7 liefert die Einträge 3 bis 7. Auch Gruppierungen sind möglich: {3,8,12} sucht demnach im dritten, achten, und zwölften Adresseintrag nach dem Namen. Der Stern * steht wie gewohnt für einen Joker. Hier ist jeder Wert möglich. Die geschweifte Klammmer muss hier übrigens gesetzt werden.
Der Doppelpunkt mit abgetrennter Zahl dient dazu, Textabschnitte in gemischten Elementen zu finden, wie man sie besonders von (X)HTML her kennt. <p>Ein <b>großes</b> Haus</p> enthält zum Beispiel zwei Textteile innerhalb der p-Tags, die von einem b-Element getrennt werden. Mit get_data(0, "p", "",":2") wäre es möglich, alle zweiten Text-Abschnitte eines beliebigen p-Elements ausfindig zu machen. Natürlich ist auch eine genauere Zähl-Pfadangabe vor dem Doppelpunkt möglich.
Navigation

 3  Beispiel: Speisezettel

Nachdem wir nun die Bedeutung der Suchparameter kennen, sollte es keine schwierige Aufgabe sein, einen zweiten Test zu machen, der auch die übrigen drei Parameter zum Einsatz kommen lässt.
Diesmal begnügen wir uns aber nicht mit einer einzigen Abfrage, sondern führen gleich drei Abfragen auf einmal durch.


<?php
require_once("../xml-line.php");
$mylines = new xml_line("xml-files/obst.xml");
//Abfrage 1 (bekommt Index [0])
//Alles finden, was den String "Him" enthält
$mylines->get_data(1,"","Him");
//Abfrage 2 (bekommt Index [1])
//Genau ein bestimmtes Obst-Element finden
//nahrungsmittel[1]/vegetarisch[1]/obst[2]
$mylines->get_data(1,"nahrungsmittel/vegetarisch/obst","","","1-1-2");
//Abfrage 3 (bekommt Index [2])
//Welches Obst hat einen guten Preis?
$mylines->get_data(1,"gemuese","",array("preis" => "gut"));
$result = $mylines->xml_stream();
?>

Mit der Ergebnisabfrage machen wir es uns einfach, indem wir davon ausgehen, dass tatsächlich etwas gefunden wird. Normalerweise müssen wir natürlich prüfen, ober ein Wert vorhanden ist, bevor wir ihn ausgeben. Außerdem begnügen wir uns der Übersicht halber mit einem Treffer je Abfrage und haben deshalb den ersten Parameter auf 1 gesetzt.


<?php
printf("<p>Welcher Eintrag enthält <i>Him</i>?<br>
           Ergebnis von Suche 1 ist <b>%s</b>.</p>",
           $result[0][0][0]);

printf("<p>Welchen Wert hat das Element
           <i>nahrungsmittel[1]/vegetarisch[1]/obst[2]</i>?<br>
           Ergebnis von Suche 2 ist <b>%s</b>.</p>",
           $result[1][0][0]);

printf("<p>Welches Gemüse hat einen guten Preis (preis=&quot;gut&quot;)?
           <br>Ergebnis von Suche 3 ist <b>%s</b>.</p>",
           $result[2][0][0]);
?>

Wir erhalten folgende Ausgabe:

Welcher Eintrag enthält Him?
Ergebnis von Suche 1 ist Himbeere.

Welchen Wert hat das Element nahrungsmittel[1]/vegetarisch[1]/obst[2]?
Ergebnis von Suche 2 ist Birne.

Welches Gemüse hat einen guten Preis (preis="gut")?
Ergebnis von Suche 3 ist Möhre.

Navigation

 4  Das Ergebnisarray

Dass die Variable $result nicht nur einen einfachen Wert zurückgibt, wird bei diesem Beispiel offenkundig.
Wie beim letzten Beispiel zu sehen war, nummeriert der erste Index einfach nur die Abfragen, wobei mit 0 begonnen wird. Es ist nichts Ungewöhnliches und häufig Absicht, dass mehrere Treffer gelandet werden. Diese Ergebnisse zählt der zweite Index durch.
Der dritte Index beinhaltet mehrere Aspekte des Ergebnisses. Das Wichtigste ist häufig der Inhalt des Elements, dem deshalb der Index [0] vorbehalten ist.

Die Tabelle gibt Aufschluss über die weitere Belegung.

index Bedeutung Format Beispielwert Anmerkungen
0 Elementwert String ein Sack Zement Gibt den Wert eines Elements wieder, also das, was zwischen den Tags steht. Dies kann auch Whitspace sein. (Die Rückgabe von Whitspace lässt sich momentan noch nicht durch eine Methode unterdrücken. Sie müssen daher selbst entscheiden, wie Sie mit Leerzeichen, Tabulatoren und Zeilenumbrüchen umgehen.)
1 Elementname String obst Manchmal ist es nützlich zu wissen, wie das Element heißt, dessen Wert man kennt. In diesem Beispiel könnten Sie z.B. festellen, ob der Apfel in die Kategorie Obst oder Gemüse gehört.
2 Attribut-Array Array [preis] => gut Ein Attribut-Array wird bei dieser Ergebnisform immer mitgeliefert. Wenn es leer ist, hat das Element keine Attribute. Ansonsten werden die Attribute als key-value-Paare gespeichert.
3 Elementpfad String nahrungsmittel/vegetarisch/obst Hier gibt es jetzt den vollständigen Pfad. Zusammen mit dem Zählpfad (nächster Index) hat man dann alle notwendigen Angaben, um z.B. bei der unscharfen Suche in einem Dokument, genaue Zielkoordinaten für eine Operation im zweiten Durchgang zu gewinnen. (Genauere Beispiele folgen später.)

Um unser Ergebnisarray in Aktion zu sehen, bauen wir die Testabfrage etwas um. Wir heben die Beschränkung für die Rückgabe auf, so dass auch mehrere Ergebnise pro Abfrage möglich sind, dadurch erhalten wir für die zweite Abfrage mit Index[1] zwei Werte.


<?php
require_once("../xml-line.php");
$mylines = new xml_line("xml-files/obst.xml");
 //Abfragen:
 //Index [0]
 $mylines->get_data(0,"","Him");
 //Index [1]
 $mylines->get_data(0,"nahrungsmittel/vegetarisch/obst","","","1-1-{1,2}");
 //Index [2]
 $mylines->get_data(0,"gemuese","",array("preis" => "gut"));
$result = $mylines->xml_stream();
echo "<pre>";
print_r($result);
echo "</pre>";
?>

In der Ausgabe lassen wir uns das gesamte Ergebnis-Array mit der print_r()-Funktion anzeigen, wie auch im PHP-Quelltext (oben) zu sehen ist.

Array
(
    [1] => Array
        (
            [0] => Array
                (
                    [0] => Apfel
                    [1] => obst
                    [2] => Array
                        (
                        )

                    [3] => nahrungsmittel/vegetarisch/obst
                    [4] => 1-1-1
                )

            [1] => Array
                (
                    [0] => Birne
                    [1] => obst
                    [2] => Array
                        (
                        )

                    [3] => nahrungsmittel/vegetarisch/obst
                    [4] => 1-1-2
                )

        )

    [0] => Array
        (
            [0] => Array
                (
                    [0] => Himbeere
                    [1] => obst
                    [2] => Array
                        (
                        )

                    [3] => nahrungsmittel/vegetarisch/obst
                    [4] => 1-1-3
                )

        )

    [2] => Array
        (
            [0] => Array
                (
                    [0] => Möhre
                    [1] => gemuese
                    [2] => Array
                        (
                            [preis] => gut
                        )

                    [3] => nahrungsmittel/vegetarisch/gemuese
                    [4] => 1-1-1
                )

        )

)

Wie leicht zu erkennen ist, stimmt die Reihenfolge der ersten Indizes nicht. Das liegt daran, dass jeder Abfrage ihre Ergebnisse so ins Rückgabe-Array einträgt, wie sie gefunden werden. Für die Auswertung spielt es jedoch keine Rolle. Das Abgreifen der einzelnen Ergebnisse lässt sich problemlos mit ein paar Zeilen Skript erschlagen.

Wenn Sie eine Weile mit der get_data()-Methode experimentiert haben, werden Sie vielleicht schnell den Wunsch haben, nicht nur Werte zu lesen, sondern auch Änderungen durchzuführen oder Daten und Elemente zu löschen. Auch der Umgang mit Attributen und deren Werten wird später noch eingehend erklärt.

Navigation

 5  Beispiel: Datei ändern und speichern

Auch diesen Teil des Tutorials wollen wir mit einem vollständigen Beispiel beginnen. Dafür rufen wir uns aber noch einmal kurz die Syntax für das Auffinden von Elementen für die Methode get_data() ins Gedächtnis:
 

$mylines->get_data([[[[[[Trefferzahl],
                         Element(pfad)],
                         Suchstring ],
                         Attribut-Array(name1=>value1, name2=>value2, ... )],
                         Zählpfad]);

Die Reihenfolge der Parameter wurde auch für die Änderungsmethoden beibehalten, mit dem Unterschied, dass diese meist in einem Array übergeben werden. Der zweite Parameter enthält dann den Wert, der geändert werden soll:

$search = array($treffer, $elementpfad, $teilstring, $attributarray, $zaehlpfad);
$mylines->change_data($arg, $value);

Anders als bei get_data() erhalten wir bei change_data() kein Ergebnisarray. (Im Moment gibt es auch noch keine Möglichkeit festzustellen, ob der Ersetzungsvorgang erfolgreich war. Gedacht ist für folgende Versionen daran, die Zahl der Ersetzungen als Ergebnis zurückzuliefern.) Um etwas von den Änderungen zu haben, müssen wir die Änderungen entweder in eine Datei schreiben oder in einer Variablen speichern.

$mylines = new xml_line("obst.xml", // infile,
                        "xml" // outformat
                        "obst-out.xml"); //outfile

Wie wir von unserem Beispiel mit get_data() wissen, ist nur der erste Parameter zwingend erforderlich. Bei Änderungen brauchen wir aber auch ein Ausgabeformat. Das ist normalerweise "xml". Dabei bleibt die Formatierung im Idealfall so erhalten, wie sie vorher war. (Leere Elemente werden allerdings mit Start- und Endtag ausgegeben auch wenn sie vorher kombiniert waren.)
Für die Darstellung von XML-Code auf einer HTML-Seite, habe ich eine Highlight-Funktion integriert. Um mit XML-Line zu experimentieren, ist es einfacher, diese Funktion zu nutzen, als ständig die Ausgabedatei zu öffnen.
Wird der dritte Parameter weggelassen, wird die Ausgabe nicht in eine Datei sondern automatisch in einen Puffer geschrieben. Dieser kann am Ende mit der Methode get_output() angezeigt werden.
(Wer beobachten will, wie der Parser intern arbeitet, kann testweise mal mit "csv" als Outputformat experimentieren. Dies ist vielleicht nützlich für Entwickler, die an der Klasse selbst Änderungen vornehmen wollen.)

Um die Änderung von Werten jetzt auszuprobieren, machen wir aus dem ersten obst-Element eine Zitrone und aus dem Schnitzel ein Würstchen.


<?php
require_once("../xml-line.php");
//Ausgabeformat HTML (Highlight-XML)
$mylines = new xml_line("obst.xml", "hixml");
//Änderungsabfrage Index [0]
$arg=array(0,"obst","","","1-1-1");
$mylines->change_data($arg, "Zitrone");
//Änderungsabfrage Index [1]
$arg=array(0,"","Schnitzel");
$mylines->change_data($arg, "Würstchen");
$mylines->xml_stream();
// Inhalt de Puffers ausgeben:
echo $mylines->get_output();
?>

Und so sieht das Ergebnis aus:

<?xml version="1.0" encoding="ISO-8859-1"?>
<nahrungsmittel>
  -  <vegetarisch>
  -     -  <obst>Zitrone</obst>
  -     -  <obst>Birne</obst>
  -     -  <obst>Himbeere</obst>
  -     -  <gemuese preis="gut">Möhre</gemuese>
  -     -  <gemuese>Kopfsalat</gemuese>
  -     -  <gemuese>Tomate</gemuese>
  -  </vegetarisch>
  -  <fleisch>
  -     -  <schwein>Würstchen</schwein>
  -     -  <rind>Gulasch</rind>
  -  </fleisch>
</nahrungsmittel>

Um eine normale XML-Ausgabedatei zu erzeugen, schreiben Sie einfach, wie bereits oben gezeigt:
$mylines = new xml_line("obst.xml", "xml", "obst-out.xml");
Vergessen Sie bitte nicht, vorher die Schreibrechte entsprechend zu setzen, damit die Datei angelegt werden kann. Ein Überschreiben der Eingabedatei ist übrigens erst hinterher möglich, weil gleichzeitig eine Datei zum Lesen und eine zum Schreiben geöffnet wird. Da die Verarbeitung sequentiell erfolgt, können dadurch auch sehr große Dateien abgearbeitet werden. Soll die Ausgabedatei die Quelldatei ersetzen, können Sie das mit den üblichen Dateifunktionen in PHP erledigen. Häufig möchte man aber vielleicht die Datei erst prüfen (z.B. in einem CMS), bevor man die Ursprungsdatei ersetzt.

Navigation

 6  Die Änderungsmethoden im Überblick

Die folgende Tabelle gibt Aufschluss über die Aufgabe der einzelnene Parameter. (Der Prameter $arg beinhaltet immer das oben beschriebene Array, dass bis zu 5 Elemente enthalten kann.)

Methode Erläuterungen
change_data($arg, $replace) Ändert den gefundenen Wert oder die Werte. Zum Löschen ersetzen Sie einfach mit einem Leerstring: change_data($arg, ""). Achtung: Es werden keine im Element enthaltenen Elemente geändert oder gelöscht sondern nur nackter Text! soll der gesamte Inhalt gelöscht werden, verwenden Sie delete_content() oder replace_content(). (Siehe auch die Erläuterungen dort.)
append_data($arg, $replace) Wie change_data($arg, "") mit dem Unterschied, dass der neue String an den alten angehängt wird.
delete_content($limit, $elementpath, "", $attr_arr, $countpath) Diese Methode löscht den gesamten Inhalt der zwischen den Tags des gesuchten Elements steht. Achtung: Die Abhängigkeit der Aktion von einem enthaltenen Suchwort ist hier nicht möglich, weil der Löschvorgang schon beginnen muss, bevor alle enthaltenen Werte bekannt sind. Braucht man eine solche Bedingung, muss die Datei zweimal geparst werden: Suchen mit get_data() und Verwendung des Ergebnisses für delete_content().
replace_content($arg, $replace) Für diese Methode gilt das gleiche, wie für delete_content(). Zusätzlich wird ein Ersetzungsstring übergeben. Dieser kann auch XML-Code enthalten, was einen einfachen Umbau des Dokuments erlaubt.
delete_element($limit, $elementpath, "", $attr_arr, $countpath) Ähnlich wie replace_content() (s.o.), nur wird hier das gesamte Element, inkl. Start- und Endtag, entfernt.
replace_element($arg, $replace) Ähnlich wie replace_content() (s.o.), jedoch wird das gesamte Element entfernt.
insert_element($arg, $insert) Mit dieser Methode fügen Sie Text oder XML-Code direkt hinter dem Start-Tag ein. Auch hier kann kein String als Bedingung (3. Parameter) mitgegeben werden.
insert_before_end ($arg, $insert) Wie insert_element() nur erfolgt die Einfügung vor dem Endtag.
insert_after_element($arg, $insert) Mit dieser Methode fügen Sie Text oder XML-Code direkt hinter dem End-Tag des gesuchten Elements ein. (Der Methodenname insert_element_after_element wurde aus logischen Gründen geändert. Der Bug der vorigen Versionen wurde beseitigt.)

Im Verzeichnis examples im Downloadpaket finden Sie verschiedene Beispiel, auch zu oben gezeigten Methoden. Diese Dateien können Sie als Ausgansbasis für eigene Experimente verwenden. Das Ergänzungsverzeichnis für Version 0.3 zeigt auch neue Methoden, die hier noch nicht dokumentiert sind.

Navigation

 7  Die Arbeit mit Attributen

Für die Arbeit mit Attributen gibt es einige eigene Funktionen.
Die Parameter für die Attributabfrage sind nicht völlig identisch mit den für die Elementabfrage verwendeten Parameter. Unterschiede sind markiert:

$mylines->get_attribute($limit,  //Maximale Trefferzahl (wie bei Elementen)
                        $path,   //Element oder Elementpfad (wie bei Elementen)
                        $attrvalue, //Wert des Attributs. - Vollständiger String!
                        $attrn,   //Name des Attributs. - Kein Array!
                        $elcount);//Element-Zählpfad (wie bei Elementen)

Die Parameter sind auch hier alle optional. Bei den Attribut-Methoden mit Änderungsparameter, werden die Suchparameter wie bei den für Elementsuche bekannten Beispiele in einem Array zusammengefasst, dass wir hier $arg nennen wollen.

Grenzen der Attributmethode:

Das Ergebnis wird genauso abgefragt, wie für get_data() beschrieben. Das Ergebnisarray sieht allerdings etwas anders aus. Unterschiede sind markiert:

Array
(
    [0] => Array   //Funktionsindex, bei nur einer Abfrage immer 0
        (
            [0] => Array   // Index für gefundene Treffer
                (
                    [0] => gut    //Wert des Attributs
                    [1] => gemuese //Name des Elements
                    [2] => preis   //Name des Attributs //(kein Array)
                    [3] => nahrungsmittel/vegetarisch/gemuese   //Elementpfad
                    [4] => 1-1-1   //Elementzählpfad
                )

        )

)

Wie man sieht, enthält das Ergebnisarray wie bei den Element-Methoden Informationen, die auch noch für weitere Aktionen verwendet werden können.

Natürlich gibt es auch bei den Attributen die Möglichkeiten zum Löschen und Ändern von Namen und Werten.

Methode Erläuterungen
$mylines->delete_attribute(0, $path, $attrvalue, $attrn, $elcount); Löscht das gesamte Attribut (Name und Wert).
$mylines->replace_attribute($arg, array("neuname"=>"neuwert")); Ersetzt ein gesuchtes Attribut durch ein anderes name-value-Paar.
$mylines->change_attribute_value($arg, $value); Ersetzt nur den Wert eines Attributs, wenn die Suchbedingung stimmt.

 8  Attribute hinzufügen

Will man ein oder mehrere Attribute zum Element hinzufügen, wird intern die Elementsuchfunktion genutzt. Daher können auch den Elementen Attribute hinzugefügt werden, die noch keine anderen Attribute haben. Die Parameterliste für die Elementsuche ist also fast die selbe wie z.B. bei get_element(). Da die Funktion aber schon beim Starttag einsetzt, kann der dritte Parameter, Suchtext im Element, nicht genutzt werden! (Sollte man in die Verlegenheit kommen, das Einfügen von Attributen vom vorhanden Elementinhalt abhängig zu machen, muss man leider mit zwei Durchgängen arbeiten. Diese Einschränkung kennen wir ja bereits von anderen Methoden.

Syntax:

$search_array= array(0, $path, "", $attr_array, $elcount);
$insert_array= array("name1"=>"wert1",
                     "name2"=>"wert2",
                     "name3"=>"wert3"); //usw.
$mylines->add_attributes($search_array, $insert_array);

Code-Beispiel

[...]
$search_array = array(0, "obst", "", "","1-1-1");
$insert_array = array("geschmack"=>"lecker", "farbe"=>"grün");
$mylines->add_attributes($search_array, $insert_array);
$mylines->xml_stream();
[...]

Hier werden dem ersten obst-Element zwei Attribute hinzugefügt. Das vollständige Beispiel finden Sie im Downloadpaket im Verzeichnis examples

Navigation

 9  "Shortcut"-Methoden

Häufig haben XML-Elemente ein einmaliges Identifikations-Attribut: id="[nummer]"
Ein solches Merkmal erleichtert den Zugriff auf ein Element. Daher enthält XML-Line auch einige Abkürzungsmethoden, die den Zugriff weiter vereinfachen:

Methode Erläuterungen
find_data_by_id( string $value[, string $idname]) Mit diesr Methode kann direkt der Wert eines Elements gefunden werden, dessen Attribut den Namen "id" hat. Der erste Parameter enthält den Wert von "id". Wird ein zweiter Parameter angegeben, steht dieser anstelle von "id". Beispiel: find_data_by_id("133", "nr") findet z.B. <datensatz nr="133">.
change_data_by_id( string $value, string $replace[, string $idname ]) Das gleiche mit der Möglichkeit zur Änderung des gefundenen Wertes. Auch hier kann optional ein Parameter übergeben werden, der den Default-Attributnamen "id" überschreibt.

Die vorgestellten Methoden sind ein Beleg dafür, dass für spezielle Aufgaben auch Methoden geschrieben werden, die

Navigation

 10  "Datensätze" lesen - Methode: get_record()

Diese Methode ist besonders dazu gedacht, einen schnellen Zugriff auf XML-Dateien zu erhalten, die im Grunde flache Tabellen sind, und eventuell sogar nichts anderes als ein XML-Export aus einer Datenbanktabelle oder Tabellenkalkulation sind.

<?xml version="1.0" encoding="ISO-8859-1"?>
<adressen>
  -  <eintrag id="1" datum="2002-05-24">
  -     -  <name>Klausmann</name>
  -     -  <vorname>Heinz</vorname>
  -     -  <anschrift>
  -     -     -  <strasse info="3 x klingeln!">Feldstrasse 5</strasse>
  -     -     -  <plz>23456</plz>
  -     -     -  <ort>Teststadt</ort>
  -     -  </anschrift>
  -  </eintrag>
  -  <eintrag id="2" datum="2002-07-03">
  -     -  <name>Meyer</name>
  -     -  <vorname>Guste</vorname>
  -     -  <anschrift>
  -     -     -  <strasse>Tannhäuserweg 12</strasse>
  -     -     -  <plz>98765</plz>
  -     -     -  <ort>Nulldorf</ort>
  -     -  </anschrift>
  -  </eintrag>
  -  <eintrag id="3" datum="2003-02-01">
  -     -  <name>Katzbeck</name>
  -     -  <vorname>Karl-Heinz</vorname>
  -     -  <anschrift>
  -     -     -  <strasse>An der Teststrecke 123a</strasse>
  -     -     -  <plz>63555</plz>
  -     -     -  <ort>Netzbach</ort>
  -     -  </anschrift>
  -  </eintrag>
</adressen>

Dieses Beispiel macht es sich etwas schwerer, da auch Attribute und sogar ein Element mit untergeordneten Elementen enthalten sind.

Aufgabe: Alle Datensätze aus der Datei adressen.xml einlesen, die mit dem Tag eintrag eingeschlossen sind.


<?php
require_once("../xml-line.php");
$mylines = new xml_line("xml-files/adressen.xml", "hixml");
$mylines->get_record(0,"eintrag");
$mylines->xml_stream();
print $mylines->get_output();
?>

Ergebnis-Array:


<?php
echo "<pre>";
print_r($mylines->table_result);
echo "</pre>";
?>

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [name] => Klausmann
                    [vorname] => Heinz
                    [anschrift/strasse] => Feldstrasse 5
                    [anschrift/strasse@info] => 3 x klingeln!
                    [anschrift/plz] => 23456
                    [anschrift/ort] => Teststadt
                )

            [1] => Array
                (
                    [name] => Meyer
                    [vorname] => Guste
                    [anschrift/strasse] => Tannhäuserweg 12
                    [anschrift/plz] => 98765
                    [anschrift/ort] => Nulldorf
                )

            [2] => Array
                (
                    [name] => Katzbeck
                    [vorname] => Karl-Heinz
                    [anschrift/strasse] => An der Teststrecke 123a
                    [anschrift/plz] => 63555
                    [anschrift/ort] => Netzbach
                )

        )

)

Besonderheit: Die Zuordung von Attributen geschieht folgendermaßen:
['eintrag@datum']

Enthält ein Element des Datensatzes ein weiteres Element, wird dies folgendermaßen in die Tabellenstruktur eingegliedert:
['anschrift/strasse']

Die get-record-Methode hat natürlich ihre Grenzen und eignet sich vor allem für Tabellen- oder Datenbankexporte. Wie in einer Datenbanktabelle muss jede Spalte einen anderen Namen haben. Würde hier ein gleichnamiges Element auftauchen, würde der vorige Wert überschrieben werden.

Zugriff auf einzelne Werte:

Aufgabe: Welchen Wert hat das Element name im zweiten Datensatz? Das Ergebnis-Array steckt in der Variablen table_result.


<?php
printf("<p>Der Wert ist: <b>%s</b></p>",
       $mylines->table_result[0][1]['name']);
?>

Der Wert ist: Meyer

Noch einmal zur Erinnerung: Der erste Index zählt die aufgerufenen Methoden. In diesem Beispiel gibt es nur die eine, also [0]. Der Index [1] ist das zweite zurückgelieferte Ergebnis, da wir auch hier bei 0 anfangen zu zählen.

Navigation

 11  Elemente mit gemischtem Inhalt

Diese Klasse ist nicht zuletzt für die Möglichkeit entwickelt worden, direkt auf XHTML-Dokumente zugreifen zu können, die ja gültige XML-Dokumente sind. Dort wird man häufig den Fall finden, dass Elemente nicht nur andere Elemente oder Werte enthalten sondern das Werte und Elemente nebeneinander stehen und sich abwechseln können. Das folgende Beispiel ist natürlich kein vollwertiges XHTML-Dokument, zeigt aber das Problem.

<?xml version="1.0" encoding="ISO-8859-1"?>
<html>
  -  <p>Herr Müller hatte am
  -     -  <b>Dienstag</b>
  -  Bauchschmerzen.
  -  </p>
</html>

<?php
$mylines = new xml_line("xml-files/mixed-content1.xml");
$mylines->get_data(0, "p");
$result=$mylines->xml_stream();
echo "<pre>";
print_r($result);
echo "</pre>";
?>

Inhalt von p auslesen:

Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => Herr Müller hatte am
                    [1] => p
                    [2] => Array
                        (
                        )

                    [3] => html/p
                    [4] => 1-1
                )

            [1] => Array
                (
                    [0] =>  Bauchschmerzen.
                    [1] => p
                    [2] => Array
                        (
                        )

                    [3] => html/p
                    [4] => 1-1
                )

        )

)

Wir erhalten zwei Werte für p. Das Element enthält zwei Textknoten und ein weiteres untergeordnetes Element mit eigenem Inhalt.

Aufgabe: Ersetzen von Bauschmerzen durch Zahnschmerzen

Lösungsweg: Wir müssen auf den zweiten Textknoten innerhalb des p-Elements zugreifen und diesen ersetzen:


<?php
$mylines = new xml_line("xml-files/mixed-content1.xml", "hixml");
$arg=array(0, "p", "", "","1-1:2");
$replace= " Zahnschmerzen";
$mylines->change_data($arg,$replace);
$mylines->xml_stream();
echo $mylines->get_output();
?>

Keine Frage, dass man dieses Problem mit einer str_replace-Funktion schneller erschlagen hätte, aber es gibt sicherlich Aufgabenstellungen, in denen man sich über die Möglichkeiten des gezielten Zugriffs auf bestimmte Textknoten freut.

Navigation

 12  Beispiel: Mini-CMS mit XML-Line

Wegen seiner XHTML-Tauglichkeit bietet sich XML-Line auch als Werkzeug für kleine CMS-Lösungen an. Je nach Anspruch kann dabei der Umweg über XML/XSLT oder Templates entfallen, indem man direkt auf die XHTML-Datei zugreift, eine veränderte Vorschaudatei generiert und diese nach Prüfung an die Stelle der Originaldatei setzt.

Ein Beispiel mit einer fingierten Nachrichtenseite finden Sie als xhtml-test.php im Verzeichnis examples im Downloadpaket. (Quelldatei: examples/xml-files/xhtml-test.html). Ausgegeben wird sie als outxhtml.html im Verzeichnis examples/output.

Es würde zu weit führen, das Beispiel hier komplett zu erläutern. Für Fortgeschrittene PHP-Programmierer sollte es keine große Kunst sein, sich im Quelltext zurecht zu finden.
Hier nur der Vorgang im Groben:
Die untergeordneten Element von body werden zunächst eingelesen.

$mylines->get_data(0,"html/body/*"); 

Mit Hilfe der Ergebnisse im $result-Array wird dann ein Formular gebaut, dass alle Elementinhalte editierbar macht.
Die geänderten Werte werden dann wieder an das Skript geschickt, das nun die Änderungsmethoden in einer Schleife ausgibt und anschließend die Änderungen durchführt:


<?php
require_once("../xml-line.php");
if (count($_POST) and isset($_POST['check'])) {
   $mylines = new xml_line("xml-files/xhtml-test.html",
                           "xml",
                           "output/outxhtml.html");
   foreach ($_POST['check'] as $k => $v) {
      list($el, $z) = split(',',$v);
      $w = $_POST['wert'][$k];
      $mylines->change_data(array(0,$el,"","",$z), $w);

   }
   $mylines->xml_stream();
}
?>

Durch den ersten Parsevorgang sind alle Element- und Zählpfade bekannt. Dadurch ist eine präzise Ansteuerung der Elementwerte im zweiten Durchgang möglich.

Der Vorgang wird Ihnen sicherlich klarer, wenn Sie sich das Beispiel direkt ansehen und damit experimentieren. Bei einiger Beschäftigung mit XML-Line sollten Sie auch in der Lage sein, dieses Beispiel so zu ändern, dass Nachrichten ganz gelöscht und weitere Nachrichten eingefügt werden können.

Sehr häufig kommt es vor, dass nur Kleinigkeiten in einer Webseite geändert werden sollen. Möglichkeiten für solche Aufgabenstellungen lassen sich mit XML-line im Handumdrehen realisieren.

Navigation

 13  Nachwort und Haftungsausschluss

Auch wenn sich mit XML-Line viele Aufgaben im Bereich XML und speziell auch bei der Arbeit mit XHTML bewältigen lassen, soll nicht der Eindruck entstehen, dass es sich bei dieser Klasse bereits um ein vollkommen ausgereiftes Werkzeug handelt. Wer es einsetzt, sollte genau prüfen, ob es auch das macht, was es soll. Für die Tauglichkeit der Klasse technische und wirtschaftliche Folgen wird ausdrücklich keine Garantie übernommen. Das gleiche gilt für diese Einführung, die sicherlich noch einige Verbesserungen erfahren wird. Sachliche Kritik und Anregungen und Rückmeldungen (= Feedback ;-)) sind stets willkommen. Bitte verwenden Sie dafür mein Kontaktformular.

Navigation