• PHP5 - Einführung XML mittels SimpleXML und DOM

    1.Einleitung

    In diesem Tutorial will ich euch zeigen, wie ihr mittels PHP auf XML-Dateien zu greift, diese auslest, editiert und schreiben könnt. Ich werde versuchen ein News-System mit XML zu erstellen, so dass ihr es in der Praxis sehen könnt.

    Ich versuche euch mittels SimpleXML und DOM euch zu erklären, wie man neue Techniken nutzt und damit ein Newssystem verwalten kann. Ich betone aber hier, dass ich nicht vorhabe ein komplettes System auf die Beine zu stellen, sondern nur die wichtigsten Fkt. zu erklären, wie Schreiben, Lesen, Editieren.

    Alle Sachen, was XML und PHP entspricht, sind nachzulesen auf www.php.net und http://www.w3.org/XML/

    Ich möchte euch auch nochmal darauf hinweisen, dass dieses Tutorial das Verstehen von PHP und den Umgang mit www.php.net vorraussetzt.


    2.Das erste Skript mit SimpleXML

    So damit wir nicht Zeit verlieren erstellen wir uns erstmal eine XML-Datei, die später erweitert werden soll, um sie in unserem News-System zu nutzen.

    Die XML-Datei (news.xml):

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    
    <?xml version='1.0'?>
    <news>
        <id>1</id>
        <title>News-System</title>
        <author>KoMtuR</author>
        <description>Wir bauen uns ein News-System mittels XML</description>
        <date>1097688183</date>
    </news>

    Nun wollen wir einfach diese Datei mittels php und SimpleXML formatiert ausgeben.

    Die PHP-Datei:

    PHP-Code:
    <html>
        <head>
            <title>tutorial1.php - XML-Dateien lesen</title>
        </head>
        <body>
    <?php
        $filename 
    'news.xml';
        
        if(
    file_exists($filename)) {
            
    $xml simplexml_load_file($filename);     //Lädt die XML-Datei in ein 
                                        //Objekt
            
    if($xml) {    //Prüfen, ob Datei Fehler enthielt
                
    ?>
            <table>
                <tr>
                    <td>Überschrift</td>
                    <td><?php echo $xml->title ?></td>
                </tr>
                <tr>
                    <td></td>
                    <td><?php echo $xml->description ?></td>
                </tr>
                <tr>
                    <td><?php echo date("H:i:s d.m.y"$xml->date?></td>
                    <td><?php echo $xml->author ?></td>
                </tr>
            </table>
    <?php    
            
    } else {
                echo 
    '        <p>Die Datei names '$filename .' konnte nicht geöffnet werden</p>';
            }
        }
    ?>
    Dieses Script lädt die Datei „news.xml“ in ein Objekt (hier: $xml) und gibt die einzelnen Tags formatiert in einer Tabelle aus. Für jedes Kindtag in der XML-Datei wird ein neue Variable im Objekt erstellt.

    Weiteres Beispiel:

    Code :
    1
    2
    3
    4
    5
    
    <parent>
        <child1>
            <child2>childNode</child2>
        </child1>
    </parent>

    Bei dieser Verschachtelung könnte man auf den Inhalt von <child2></child2> so zugreifen:

    PHP-Code:
    <?php
    //[...]
        
    echo $xml->parent->child1->child2;
    ?>
    Es wird deutlich, dass eine Verbindung zwischen XML-Datei und dem erstellten Objekt entsteht.

    3.Das zweite Skript mit SimpleXML

    Nun wollen wir eine XML-Datei auslesen, welche mehrere Datensätze enthält. Also können wir unserem News-System schon eine Funktion hinzufügen – Auslesen der News.

    Dies geschieht in Grundzügen genauso wie das erste Skript, nur das wir als Objekt ein Array erhalten


    Hier die XML-Datei (news.xml):

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    <?xml version='1.0'?>
    <system>
        <news>
            <id>2</id>
            <title>News-System die 2.</title>
            <author>KoMtuR</author>
            <description>Wir lesen mehrere Datensätze aus einer XML-Datei</description>
            <date>1097688200</date>
        </news>
        <news>
            <id>1</id>
            <title>News-System</title>
            <author>KoMtuR</author>
            <description>Wir bauen uns ein News-System mittels XML</description>
            <date>1097688183</date>
        </news>
    </system>

    Und hier der PHP-Code:

    PHP-Code:
    <?php
        $filename 
    'news.xml';
        
        if(
    file_exists($filename)) {
            
    $xml simplexml_load_file($filename);
            if(
    $xml) {
                foreach(
    $xml->news as $news) {
                
    ?>
            <table>
                <tr>
                    <td>Überschrift</td>
                    <td><?php echo $news->title ?></td>
                </tr>
                <tr>
                    <td></td>
                    <td><?php echo $news->description ?></td>
                </tr>
                <tr>
                    <td><?php echo date("H:i:s d.m.y"$news->date?></td>
                    <td><?php echo $news->author ?></td>
                </tr>
            </table>
    <?php    
                
    }
            } else {
                echo 
    '        <p>Die Datei names '$filename .' konnte nicht geöffnet werden</p>';
            }
        }
    ?>
    Der Code entspricht fast genau dem ersten Beispiels. Diesmal wird aber nur eine foreach-Schleife eingefügt, die nun durch die Datensätze geht.

    Nun ist bekannt, wie man mit SimpleXML eine einfache XML-Datei ausliest und weiterverarbeitet.

    Das nächste Thema wird sein eine Datei zu editieren und neue Datensätze hinzuzufügen.

    4.XML-Datei editieren mittels DOM

    In diesem Kapitel will ich euch mit den integrierten DOM Funktionen von PHP5 erklären, wie ihr eine Datei editieren könnt und dazu auch neue Datensätze hinzufügen könnt. Man könnte dies zwar auch mit SimpleXML bewerksetlligen, aber ich empfinde die DMO-Funktionen für sinnvoller, weil diese eine leichtere Handhabung mit der XML-Datei besitzen.

    Weil wir natürlich in unserem News-System auch neue Datensätze hinzufügen wollen und vielleicht einem Admin die Möglichkeit bieten wollen Datensätze zu editieren will ich euch die DOM näher bringen.

    Um eine XML-Datei mittels der DOM-Funktionen zu öffnen brauchen wir die Fkt. domxml_open_file(). Wir wollen nun ein Datensatz auslesen mit einer bestimmten ID und Diesen erstmal anzeigen lassen.


    PHP-Code:
    <?php
    $doc 
    DOMDocument::load("news.xml");

    $xp = new domxpath($doc);
    $titles $xp->query('news[id="1"]');
    foreach (
    $titles as $node) {
        foreach(
    $node->childNodes as $cnode) {
            if(
    $cnode->nodeType == 1) {
                print 
    $cnode->nodeName .": "$cnode->textContent "\n";
            }
        }
    }
    ?>
    Wir öffnen eine XML-Datei und erhalten ein DOM-Objekt. Mit Diesem erstellen wir ein XPath-Objekt.
    Dieses XPath-Objekt ist in diesem Sinne sinnvoll, weil wir dadurch spezielle Datensätz raussuchen können.
    PHP-Code:
    <?php
    $titles 
    $xp->query('news[id="1"]');
    ?>
    Dort erstellen wir ein Query und lassen uns die "News"-Teile ausgeben, wo der ID-Tag gleich "1" ist. Nähere Informationen zu diesen Queries erhaltet ihr unter http://www.w3.org/TR/xpath.

    Nun wollen wir aber nicht nur ein bestimmten Datensatz anzeigen lassen sondern ihn auch bearbeiten.
    Sagen wir, dass wir den Inhalt von <description></description> der News, mit der ID= "1", ändern wollen.

    Hier erstmal der Code.
    PHP-Code:
    <?php
    $edit 
    "description";
    $edit_content "Dieser Text wurde editiert";


    $doc DOMDocument::load("news.xml");

    switch(
    $edit) {
        case 
    "description":
            
    $newtag $doc->createElement("description");
            
    $newtag_content $doc->createTextNode($edit_content);
            
    $newtag->appendChild($newtag_content);
            break;
        default:
            break;
    }

    $xp = new domxpath($doc);
    $titles $xp->query('news[id="1"]');
    foreach (
    $titles as $node) {
        foreach(
    $node->childNodes as $cnode) {
            if(
    $cnode->nodeType == 1) {
                if(
    $cnode->nodeName == $edit) {
                    
    $cnode->parentNode->replaceChild($newtag$cnode);
                }
            }
        }
    }
    echo 
    $doc->saveXML();
    ?>
    Sieht jetzt vielleicht ein wenig unübersichtlich aus aber ich hoffe ich kann es klären.

    Die Funktion createElement() erstellt ein neues XML-Tag und createTextNode() erstellt den Inhalt dafür. Mit appendChild() wird dem erstellten XML-Tag die Zeichenkette zugewiesen. Per XML würde das Beispiel so aussehen:

    Code :
    1
    
    <description>Dieser Text wurde editiert</description>

    Nun müssen wir nur noch den speziellen Tag herausfinden, welchen ihr editieren wollt. Dies geschieht in diesem Abschnitt. Dort wird dann der alte Tag durch den neuen ersetzt.

    PHP-Code:
                if($cnode->nodeName == $edit) {
                    
    $cnode->parentNode->replaceChild($newtag$cnode);
                } 
    So mit saveXML() geben wir nun die komplette XML-Struktur aus.

    Somit haben wir diese Datei fast komplett editiert und müssen die nur noch speichern. Entweder ihr macht das mit fopen() und saveXML(). Oder ihr verwendet die mitgelieferte DOM-Funktion save().

    PHP-Code:
    <?php
        $doc
    ->save('news.xml');
    ?>
    5.XML-Datensatz hinzufügen mit DOM

    Nun kommen wir zu einem inertessanten Thema, aber zugleich auch Schwierigen. Wir wollen einen Datensatz hinzufügen in der Datei.
    Ich werde es wieder auf dieses Newssystem beziehen und werde den neuen Datensatz somit immer am Anfang der XML-Datei einfügen, damit die letzte News immer an erster Stelle eingefügt wird und somit wir dem „ausgebe“-Skript schon die nötige Arbeit abnehmen.
    Mir ist eine Sache bei diesem Script aufgefallen, dass ihr selber lösen könnt. Ich hab erstmal ne ID mit nur einer Zahl vergeben, indem er die Einträge zählt. Dies ist aber nicht gut, weil wenn man ein Datensatz löscht 2 News die gleiche ID haben können. Ihr könnt dies entweder mit einer anderen ID lösen, also mehrere Sachen für eine ID verwenden (zb. Timestap+anzahl der Datensätze) oder ihr speichert euch einen Zähler in diese XML-Datei.


    Hier ist erstmal der Code, wie wir einen neuen Newsbeitrag in diese Datei speichern. Nicht erschrecken. Es wird alles noch erklärt danach.

    PHP-Code:
    <?php
    $title  
    "News Nr. 3";
    $author "KoMtuR";
    $date   time();
    $desc   "Von uns erstellt";

    $doc DOMDocument::load("news.xml");

    $xp = new domxpath($doc);

    $id 0;
    foreach (
    $doc->documentElement->childNodes as $articles) {
        if (
    $articles->nodeType == && $articles->nodeName == "news") {
            
    $id++;
        }
    }

    $id++;
    $newxml = <<< XML
    <?xml version='1.0'?>
    <news>
        <id>
    $id</id>
        <title>
    $title</title>
        <author>
    $author</author>
        <description>
    $desc</description>
        <date>
    $date</date>
    </news>
    XML;

    $doc2 DOMDocument::loadXML($newxml);
    $xpath = new DOMXPath($doc2);

    $query $xpath->query("/news");

    foreach(
    $query as $node) {
        
    $import $node->cloneNode(true);
    }

    $onewxml $doc->importNode($importtrue);

    $firstNode $doc->getElementsByTagName("news");

    $doc->documentElement->insertBefore($onewxml$firstNode->item(0));
    $doc->save("news.xml");
    ?>
    Die Variablen $title, $author, $desc und $date speichern die Inhalte des neuen Datensatzes. Die Variable $newxml speichert die Struktur des neuen Datensatzes.

    PHP-Code:
    $id 0;
    foreach (
    $doc->documentElement->childNodes as $news) {
        if ((
    $news->nodeType == 1) && ($news->nodeName == "news")) {
            
    $id++;
        }
    }

    $id++; 
    Hier wird die ID für den neuen Datensatz bestimmt. es werden alle Kinderobjekte ausgelesen. Wenn der Tagname "news" lautet wird die id inkrementiert. Zum Schluss wird die ID noch um 1 erhöht, damit der neue Datensatz die nächsthöhere ID bekommt.

    PHP-Code:
    $doc2 DOMDocument::loadXML($newxml);
    $xpath = new DOMXPath($doc2);

    $query $xpath->query("/news");

    foreach(
    $query as $node) {
        
    $import $node->cloneNode(true);
    }

    $onewxml $doc->importNode($importtrue); 
    Hier wird ein neues DOMDocument-Objekt mittels loadXML() erstellt. Diese Funktion hat als Parameter eine Zeichenkette, welche unser neuer Datensatz ist.

    Dann wird mittels dem DOMXPath-Objekt eine neue Abfrage gestartet und unseren neuen Datensatz nach dem Tag "news" durchsucht.

    Dieser wird mittels cloneNode(true) kopiert und in $import gespeichert. Das true gibt an, dass auch die Kinderelemente von <news></news> mitkopiert werden sollen.

    mit $doc->importNode($import, true) veranlasst man im Originaldokument, dass ein neuer Datensatz importiert wird. Dieser wird aber noch nicht an der Stelle eingefügt. Er wird nur auf das Einfügen vorbereitet.

    Das Einfügen geschieht mit:
    PHP-Code:
    $firstNode $doc->getElementsByTagName("news");

    $doc->documentElement->insertBefore($onewxml$firstNode->item(0));
    $doc->save("news.xml");
    ?> 
    Hier wird erst wieder nach Elementen gesucht, die "News" heissen und dann wird mittels $doc->documentElement->insertBefore($onewxml, $firstNode->item(0)) der neue Datensatz vor das erste Element geschrieben.

    Somit haben wir nun unsere neue News an erster Stelle, wenn die XML-Datei wieder ausgelesen wird.


    Im Großen und Ganzen war es das erstmal für die Einführung. Speziellere Wünsche überbleiben eurer Phantasie und die ganzen Fkt. kann man auch auf www.php.net nachlesen.
     


     
    Kommentare 4 Kommentare
    1. Avatar von creep3007
      creep3007 -
      Super geniales Tutorial, sehr einfach nachzustellen und sehr simpel erklärt.

      Finde ich persönlich einen knackigen Einstieg in die Materie.

      Das nächste soll bitte nicht als Kritik gelten, da ich selbst weiß wie schnell man sich mal vertippen kann, doch sollte jedem "Anfänger" zumindest die Mühe erspart bleiben, auf Fehlersuche in einem Tutorialscript zu gehen, daher diese ...

      ... kleine Anmerkung:
      in deinem / dem aller ersten XML Code hast du "ausversehen" 2 unterschiedliche Tags verwendet.

      <Author> != </author>
      Code :
      1
      
      ([...] Opening and ending tag mismatch: Author line 15 and author in line 16 [...])

      Passiert halt.

      So noch ein Lob:
      Gut durchdacht!
      Sauber und aufbauend geschrieben!
      Sehr gute Arbeit!
      Weiter so, freu mich auf mehr

      LG,
      Alexander
    1. Avatar von ComFreek
      ComFreek -
      @creep3007: Tippfehler verbessert

      Man kann übrigens auch Datensätze mittels SimpleXML und SimpleXMLElement::addChild(...) hinzufügen.
    1. Avatar von spirit
      spirit -
      Ich bin gerade dabei in XML einzusteigen und habe den Code 1:1 übernommen. Jedoch bekomme ich eine Fehlermeldung:

      Warning: date() expects parameter 2 to be long, object given in C:\xampp\htdocs\xml\index.php on line 25

      In der Linie 25 steht: <td><?php echo date("H:i:s d.m.y", $xml->date) ?></td>

      Habe die line 25 folgender Weise verändert:

      In der Linie 25 steht: <td><?php echo date("H:i:s d.m.y", (int) $xml->date) ?></td>

      Jetzt funktioniert es

      Gruß
      Spirit
    1. Avatar von Andre Lieske
      Andre Lieske -
      Hallo,
      ich habe den Code zum testen 1:1 aus Rubrik 5.XML-Datensatz hinzufügen mit DOM kopiert.
      Erhalte folgende Fehlermeldung
      Code :
      1
      2
      3
      4
      5
      
      Strict Standards: Non-static method DOMDocument::load() should not be called statically in C:\xampp\htdocs\test\test.php on line 7
       
      Warning: DOMDocument::load(): Input is not proper UTF-8, indicate encoding ! Bytes: 0xE4 0x74 0x7A 0x65 in file:///C:/xampp/htdocs/test/news.xml, line: 7 in C:\xampp\htdocs\test\test.php on line 7
       
      Catchable fatal error: Argument 1 passed to DOMXPath::__construct() must be an instance of DOMDocument, boolean given in C:\xampp\htdocs\test\test.php on line 9

      Was läuft da bei mir verkehrt?
      Besten dank im voraus.
      Gruss André