SimpleXML Elemente in einer Schleife durchgehen und löschen

Wenn man mit PHP ganze Dateien oder einzelne Strings im XML Format einlesen möchte, dann kann hierfür sehr schön SimpleXML verwenden. Der Vorteil aus meiner Sicht: die wesentlich einfachere Handhabung und bessere Lesbarkeit des Codes im Vergleich zur klassischen Variante mittels DOMDocument.

Während DOMDocument zum entfernen von Elementen mit Methoden wie DOMNode::removeChild daherkommen, gibt es bei SimpleXML jedoch nur lesende Methoden.

Daher muss man bei SimpleXML ein wenig tricksen – mittels unset()

Hier ein Beispiel-XML:

<?xml version="1.0" encoding="UTF-8"?>
<content>
    <id>12345</id>
    <link>
        <type>cover</type>
        <size>original</size>
        <url>path/to/file/original.jpg</url>
        <height>600</height>
        <width>800</width>
    </link>
    <link>
        <type>author</type>
        <size>original</size>
        <url />
        <height />
        <width />
    </link>
    <link>
        <type>pic1</type>
        <size>original</size>
        <url />
        <height />
        <width />
    </link>
</content>

Das ist jetzt ein sehr stark gekürztes Beispiel. In diesem Format (jedoch mit sage und schreibe festen 365 <link> Blöcken) hatte ich von einem Lieferanten für knapp 5 Millionen Artikel solche Link-Sammlungen erhalten. Dabei gab es in der Regel nur zu 2-5 dieser Link-Blöcke überhaupt eine URL. Sprich: der Rest war völlig wertlos und hat nur unnütz Platz weggenommen: knapp 90 GB in der Datenbank .

So habe ich mich entschlossen diese Daten nur noch bereinigt abzuspeichern und vorher einfach alle nicht benötigten Link Blöcke zu entfernen.

Nun lag bereits vor dem Speichern der Inhalt als SimpleXML Objekt vor. So sah im Endeffekt meine Lösung aus, die mir (nach dem einmaligen zusätzlichen Bereinigen der Daten) über 80 GB Platz auf meiner Platte verschafft hatte:

// load: xml file into SimpleXMLElement Object
$xml = simplexml_load_file($file);

// do a lot of stuff here

foreach ($xml->content as $content) {

    // hack: remove all link nodes with empty url
    for ($i = count($content->link); $i >= 0; -- $i) {
        if (!(string)$content->link[$i]->url) {
            unset($content->link[$i]);
        }
    }

    // save content
}

Die wichtigsten Erkenntnisse zusammengefasst:

  • Damit man über $content->link iterieren kann, wird hieraus ein numerisches Array abgeleitet.
  • Mit foreach geht es jedoch nicht, da es sich nicht um ein klassisches Array zu handeln scheint und ich an den zu löschenden Key so nicht herangekommen bin.
  • Die for Schleife (siehe Zeile 9) muss unbedingt absteigend die Schlüssel durchlaufen, prüfen und ggf. die Elemente löschen, da das Array „dynamisch“ ist.
  • Anders gesagt verhält sich $content->link zwar wie ein numerisches Array (bei 365 Einträgen mit den Keys 0 – 364), jedoch sind die Keys nicht statisch sondern werden nach dem Löschen eines Elements immer wieder neu errechnet. Lösche ich also das Element mit dem Key 5 (von 364), dann rutschen alle Keys nach und es gibt wieder lückenlos 0 – 363. Ist irgendwie auch logisch, damit man bspw. immer auf Element 0 zugreifen kann, muss man aber wissen.
  • Mit einem simplen unset() können dann die gewünschten (überflüssigen) Elemente entfernt werden.

Das könnte dich auch interessieren …

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte beachte die Hinweise zum Datenschutz