Navigation
 Startseite
 Fachbücher
 Anzeigenmarkt
 Forum
 Webmaster News
 Script Newsletter
 Push Meldungen
 Kontakt
 Script Installation
 Php
 Php Tutorials
 Lernpfade
 Webhoster Vergleich
 Impressum

Community-Bereich
 kostenlos Registrieren
 Anmelden
 Benutzerliste

Script Datenbank
 Script Archiv
 Script Top 20
 Screenshots
 Testberichte

Suche
 

Unsere Php Scripts
 Counter Script
 Umfrage Script
 Bilder Upload Script
 Terminverwaltung
 Simple PHP Forum
 RSS Grabber

Tools und Generatoren
 .htpasswd Generator
 md5 Generator
 base64 Generator
 Markdown to HTML
 Colorpicker
 Unix timestamp Tool
 Unit Test Generator
 TLD Liste
 Webkatalog‑Verzeichnis

Artfiles.de
Bietet Serviceorientierte...
https://www.Artfiles.de
Hosterplus.de
Bekommen Sie Speicherplatz (Webspace), Domains...
https://www.Hosterplus.de
 
 
 

PHP XMLWriter: XML speichersparend schreiben

Sie befinden sich: Home > Php Tutorial > PHP XMLWriter: XML speicher...

PHP XMLWriter: XML speichersparend schreiben
Veröffentlicht am:
07.09.2026
Hits / Besucher:
14
Sprache:
  Deutsch
Tutorial Art:
eigenes
Eingetragen von:
Merkliste:
 
Beschreibung

Ein Marktplatz erwartet jede Nacht eine XML-Datei mit 200.000 Artikeln. Der bisherige Weg baut das Dokument vollständig im Arbeitsspeicher auf und scheitert seit dem letzten Sortimentszuwachs regelmäßig am Limit.

Illustration zum Tutorial: PHP XMLWriter: XML speichersparend schreiben

Im Bild wandern einzelne Datensätze durch eine schmale Düse und werden zu einem fortlaufenden Band aus Elementen. Der Speicherbalken darunter bleibt dabei flach.

Was PHP XMLWriter macht

PHP XMLWriter schreibt XML in dem Moment weiter, in dem es entsteht. Es gibt keinen Baum, keine Struktur im Speicher und keine Möglichkeit, ein bereits geschriebenes Element noch einmal anzufassen. Genau daraus ergibt sich der Vorteil: Der Speicherbedarf hängt nicht davon ab, wie groß das Dokument wird.

<?php

$w = new XMLWriter();
$w->openMemory();
$w->setIndent(true);

$w->startDocument('1.0', 'UTF-8');
$w->startElement('artikel');
$w->writeAttribute('nr', '4711');
$w->writeElement('name', 'Schraube M4');
$w->writeElement('preis', '0.12');
$w->endElement();
$w->endDocument();

echo $w->outputMemory();

/* <?xml version="1.0" encoding="UTF-8"?>
<artikel nr="4711">
<name>Schraube M4</name>
<preis>0.12</preis>
</artikel> */

Zu jeder Methode der Klasse gibt es eine gleichnamige Funktion mit dem Präfix xmlwriter_. Dieses Tutorial nutzt durchgängig die Objektform, weil die Aufrufkette damit deutlich kürzer ausfällt.

Die wichtigsten Methoden im Überblick, mit den Beschreibungen aus dem Handbuch:

XMLWriter::openUri(string $uri): bool

XMLWriter::openMemory(): bool
XMLWriter::startDocument(?string $version, ?string $encoding): bool
XMLWriter::writeElement(string $name, ?string $content): bool
XMLWriter::flush(bool $empty = true): string|int

/* openUri - create a new xmlwriter using a stream or a file path
openMemory - create a new xmlwriter using memory instead
startDocument - start the xml document, version and encoding
writeElement - write a full element in one call
flush - flush the buffer, returns the buffer when using memory

The class provides the same set of methods as the xmlwriter_*
functions. Every call appends to the xml document being built,
nothing can be changed afterwards. See the manual for the full list. */

Der letzte Satz beschreibt die eine Einschränkung, die alles andere erklärt: Geschriebenes lässt sich nicht mehr ändern. Genau deshalb bleibt der Speicherbedarf konstant.

Baum oder Strom: wann PHP XMLWriter die richtige Wahl ist

SimpleXML und DOM bauen das Dokument zuerst vollständig im Speicher auf und schreiben es am Ende in einem Zug. PHP xmlwriter schreibt jedes Element sofort weiter und vergisst es danach. Deshalb bleibt der Speicherbedarf gleich, ob das Dokument hundert oder hunderttausend Elemente hat.

Die Kehrseite ist ebenso klar. Wer ein Dokument nachträglich ändern, umsortieren oder durchsuchen will, braucht den Baum und damit PHP DOMDocument zum Parsen und Manipulieren. Wer XML überhaupt erst kennenlernt oder ein kleines Dokument zusammensetzt, ist mit dem Weg aus dem PHP XML Tutorial schneller am Ziel. Hier geht es um den Fall, in dem von vorn nach hinten geschrieben und nie zurückgesprungen wird.

Der Ablauf von PHP XMLWriter in sieben Schritten

Die Reihenfolge der Aufrufe in PHP XMLWriter ist immer dieselbe, unabhängig davon, wie groß das Dokument wird.

1. Ziel öffnen openUri() für eine Datei oder einen Ausgabestrom, openMemory() für einen Puffer im Arbeitsspeicher.
2. Dokument beginnen startDocument('1.0', 'UTF-8') schreibt die Kopfzeile. Ohne Angabe der Kodierung fehlt sie, und mancher Empfänger rät dann falsch.
3. bis 6. Elemente schreiben startElement(), dann beliebig viele writeAttribute(), danach text() oder weitere Elemente, zum Schluss endElement(). Für einfache Fälle fasst writeElement() alle drei Schritte zusammen.
7. Dokument abschließen endDocument() schließt alle noch offenen Elemente auf einmal und leert den Puffer.

Der letzte Punkt ist bequemer, als er klingt: Auch bei tief verschachtelten Strukturen muss am Ende nicht jedes offene Element einzeln geschlossen werden.

Sonderzeichen maskiert PHP XMLWriter selbst

Das ist das stärkste Argument gegen selbst zusammengesetzte Zeichenketten. Ein einziges kaufmännisches Und im Produktnamen macht eine von Hand gebaute Datei ungültig.

<?php

$name = 'Hammer & Zange "klein"';

/* Von Hand zusammengesetzt: das Ergebnis ist kein gueltiges XML mehr. */
echo '<name>' . $name . '</name>';
/* <name>Hammer & Zange "klein"</name> */

/* Mit dem Writer: die Umwandlung passiert automatisch. */
$w = new XMLWriter();
$w->openMemory();
$w->writeElement('name', $name);
echo $w->outputMemory();
/* <name>Hammer &amp; Zange "klein"</name> */

Dasselbe gilt für Attributwerte, in denen zusätzlich Anführungszeichen umgewandelt werden müssen. Wer sich darauf verlässt, braucht keine eigene Ersetzungsliste zu pflegen, und genau solche Listen sind es, die irgendwann einen Fall vergessen.

Drei Ziele für PHP XMLWriter

Das Ziel wird beim Öffnen festgelegt, alles danach bleibt gleich.

<?php

/* 1. In eine Datei schreiben */
$w = new XMLWriter();
$w->openUri('/var/export/katalog.xml');

/* 2. Im Speicher sammeln, danach als String abholen */
$w = new XMLWriter();
$w->openMemory();
/* ... */
$inhalt = $w->outputMemory();

/* 3. Direkt in die Antwort schreiben */
header('Content-Type: application/xml; charset=utf-8');
$w = new XMLWriter();
$w->openUri('php://output');

Der Speicherweg ist der einzige, bei dem das Ergebnis wieder komplett im Arbeitsspeicher landet. Für große Dokumente scheidet er damit aus, für ein Dokument, das anschließend signiert oder verschickt werden soll, ist er genau richtig. Zu beachten ist, dass outputMemory() den Puffer standardmäßig leert; ein zweiter Aufruf liefert also nichts mehr, sofern nicht outputMemory(false) verwendet wird.

Ein Export mit 200.000 Datensätzen

So sieht die Aufgabe aus der Einleitung fertig aus.

<?php

$w = new XMLWriter();
$w->openUri('/var/export/katalog.xml');
$w->startDocument('1.0', 'UTF-8');
$w->startElement('katalog');

$stmt = $pdo->query('SELECT nr, name, preis FROM artikel');
$zaehler = 0;

foreach ($stmt as $zeile) {
$w->startElement('artikel');
$w->writeAttribute('nr', (string) $zeile['nr']);
$w->writeElement('name', (string) $zeile['name']);
$w->writeElement('preis', number_format((float) $zeile['preis'], 2, '.', ''));
$w->endElement();

/* Regelmaessig wegschreiben, damit der interne
Puffer nicht anwaechst. */
if (++$zaehler % 1000 === 0) {
$w->flush();
}
}

$w->endDocument();
$w->flush();

Die Einrückung ist hier bewusst nicht eingeschaltet. Bei 200.000 Datensätzen mit je drei Feldern kommen sonst mehrere Megabyte allein an Leerzeichen zusammen. Für die Fehlersuche lässt sie sich mit setIndent(true) und setIndentString(' ') jederzeit zuschalten.

Namensräume mit Präfix schreiben

Im elektronischen Rechnungsaustausch und bei Behördenformaten sind Namensräume Pflicht. PHP XMLWriter bringt dafür eigene Methoden mit.

<?php

$w = new XMLWriter();
$w->openMemory();
$w->startDocument('1.0', 'UTF-8');

$w->startElementNs('inv', 'Rechnung', 'urn:beispiel:rechnung:1');
$w->writeAttributeNs('inv', 'nummer', null, 'R-2026-0815');
$w->writeElementNs('inv', 'Betrag', null, '249.90');
$w->endElement();

$w->endDocument();
echo $w->outputMemory();

/* <inv:Rechnung xmlns:inv="urn:beispiel:rechnung:1" inv:nummer="R-2026-0815">
<inv:Betrag>249.90</inv:Betrag>
</inv:Rechnung> */

Der Namensraum wird beim ersten Element deklariert, danach genügt das Präfix. Deshalb steht bei den folgenden Aufrufen null als URI: Eine erneute Deklaration wäre überflüssig und würde das Dokument nur aufblähen.

CDATA für Inhalte, die nicht maskiert werden sollen

Manche Empfänger erwarten HTML-Fragmente im Original, nicht in maskierter Form. Dafür gibt es den CDATA-Abschnitt.

<?php

$w->startElement('beschreibung');
$w->writeCdata('<p>Robuster Hammer mit <strong>Stielschutz</strong>.</p>');
$w->endElement();

Eine Warnung gehört dazu: Enthält der Inhalt selbst die Zeichenfolge, die einen CDATA-Abschnitt beendet, bricht das Dokument an dieser Stelle auseinander. Bei Texten aus fremder Quelle sollte darauf geprüft und die Folge zerlegt werden. Wer die Wahl hat, fährt mit der gewöhnlichen Maskierung sicherer.

Wenn die Datei leer bleibt

Das häufigste Fehlerbild bei PHP xmlwriter ist eine Datei, die nach einem fehlerfreien Durchlauf keinen Inhalt hat. Die Ursache steht fast immer am Ende des Skripts.

<?php

$w = new XMLWriter();
$w->openUri('export.xml');
$w->startDocument('1.0', 'UTF-8');
$w->writeElement('hinweis', 'Test');

/* Ohne diese beiden Zeilen bleibt der Inhalt im Puffer. */
$w->endDocument();
$w->flush();

Der Writer sammelt in einem internen Puffer und schreibt ihn erst weg, wenn er dazu aufgefordert wird. PHP holt das beim Aufräumen zwar meist nach, verlassen sollte man sich darauf nicht. Zwei Zeilen am Ende ersparen eine lange Suche.

flowchart TD
  A[Ziel waehlen] --> B{Datei oder Ausgabe}
  B -->|Datei| C[openUri mit Pfad]
  B -->|Antwort| D[openUri php://output]
  C --> E[startDocument]
  D --> E[startDocument]
  E --> F[startElement und text]
  F --> G{Weitere Daten}
  G -->|ja| F
  G -->|nein| H[endDocument und flush]

Die Schleife in der Mitte ist der Teil, der beliebig oft durchlaufen wird, ohne dass der Speicherbedarf steigt.

XMLWriter gegen DOMDocument

Der Unterschied zwischen PHP XMLWriter und dem Baum lässt sich messen. Beide Verfahren erzeugen dasselbe Dokument mit 100.000 Elementen.

<?php

/* Mit DOM: jedes Element bleibt bis zum Schluss im Speicher */
$dom = new DOMDocument('1.0', 'UTF-8');
$wurzel = $dom->createElement('liste');
$dom->appendChild($wurzel);

for ($i = 0; $i < 100000; $i++) {
$wurzel->appendChild($dom->createElement('eintrag', 'Nummer ' . $i));
}

$dom->save('dom.xml');
echo round(memory_get_peak_usage(true) / 1048576) . ' MB';
/* dreistelliger MB-Bereich */

/* Mit dem Writer: nichts bleibt liegen */
$w = new XMLWriter();
$w->openUri('writer.xml');
$w->startDocument('1.0', 'UTF-8');
$w->startElement('liste');

for ($i = 0; $i < 100000; $i++) {
$w->writeElement('eintrag', 'Nummer ' . $i);
}

$w->endDocument();
$w->flush();
echo round(memory_get_peak_usage(true) / 1048576) . ' MB';
/* einstelliger MB-Bereich */

Damit ist die Entscheidung meist gefallen, aber nicht immer. DOM bleibt überlegen, sobald ein Dokument nachträglich geändert, mit einem zweiten zusammengeführt oder per XPath durchsucht werden soll. Der Writer kann nur vorwärts, und diese Einschränkung ist der Preis für den flachen Speicherverlauf.

Fazit

PHP xmlwriter ist das Werkzeug für XML-Dateien, die groß werden dürfen. Der Ablauf ist immer derselbe, die Maskierung passiert von selbst, und der Speicherbedarf bleibt konstant. Wer beim Schreiben regelmäßig wegschreibt, die Einrückung bei Massendaten abschaltet und am Ende an endDocument() denkt, hat alles beisammen, was ein täglicher Export braucht.

 

Tags:

 

 

Kommentare (0)

Noch keine Kommentare. Sei der Erste!

Melde dich an, um einen Kommentar zu schreiben.
Bücherregal mit drei Büchern: 'PHP 4 - Grundlagen und Profiwissen' von Hanser Verlag, 'Webdesign in a Nutshell' von O'Reilly Verlag, und 'Webgestaltung' von Galileo Computing.