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.