Start · Sprachen · PHP · Referenz · XMLReader

XMLReader

Klasse

Fortschreitender XML-Pull-Parser, der einen XML-Datenstrom knotenweise durchläuft, ohne den gesamten Baum im Speicher zu halten.

seit PHP 5.1.0 Kategorie: xml

Signatur

class XMLReader

Beschreibung

XMLReader implementiert einen XML-Pull-Parser, bei dem die Anwendung aktiv von Knoten zu Knoten springt (z. B. mit read() oder next()), anstatt passive Callbacks zu registrieren wie bei SAX. Der Parser hält jeweils nur den aktuellen Knoten im Speicher, was ihn ideal für sehr große XML-Dokumente macht, die nicht vollständig als DOM geladen werden können.

Der typische Anwendungsfall ist das sequenzielle Verarbeiten großer Feeds, Exporte oder Konfigurationsdateien: Man öffnet eine Datei oder einen XML-String, iteriert mit einer Schleife über die Knoten und reagiert je nach nodeType und name auf relevante Elemente. Im Vergleich zu SimpleXML oder DOMDocument benötigt XMLReader einen Bruchteil des Arbeitsspeichers.

Für komplexe Teilbäume kann der aktuelle Knoten jederzeit in ein DOMNode-Objekt expandiert werden (expand()), sodass man gezielt komfortable DOM-Methoden auf einzelne Abschnitte anwenden kann, ohne das gesamte Dokument zu laden. Optionale Schema- oder RelaxNG-Validierung ist integriert.

Wichtig: XMLReader ist nicht thread-sicher und unterstützt kein Zurückspulen; eine einmal gelesene Position kann nicht erneut angefahren werden, ohne den Parser neu zu öffnen.

Parameter

Name Typ Default Beschreibung
$encoding string|null null Zeichenkodierung des Dokuments (z. B. 'UTF-8'). Wird nur im Konstruktor oder beim Öffnen via open() / XML() übergeben.
$options int 0 Bitmaske aus LIBXML_*-Konstanten, z. B. LIBXML_NONET, um Netzwerkzugriffe während des Parsens zu unterbinden.

Beispiele

Große XML-Datei knotenweise einlesen

<?php
$reader = new XMLReader();
$reader->open('katalog.xml', null, LIBXML_NONET);

while ($reader->read()) {
    // Nur öffnende Element-Knoten mit dem Namen "produkt" verarbeiten
    if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'produkt') {
        $id    = $reader->getAttribute('id');
        $reader->read(); // In den Text-Knoten vorrücken
        $titel = $reader->value;
        echo "Produkt $id: $titel\n";
    }
}

$reader->close();
Produkt 1: Hammerschloss 200g Produkt 2: Schraubenzieher Kreuz PH2 ...

XML-String parsen und Teilbaum als DOM expandieren

<?php
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<bestellungen>
  <bestellung id="42">
    <kunde>Max Mustermann</kunde>
    <betrag>199.99</betrag>
  </bestellung>
  <bestellung id="43">
    <kunde>Erika Musterfrau</kunde>
    <betrag>49.00</betrag>
  </bestellung>
</bestellungen>
XML;

$reader = new XMLReader();
$reader->XML($xml, 'UTF-8', LIBXML_NONET);

$dom = new DOMDocument();

while ($reader->read()) {
    if ($reader->nodeType === XMLReader::ELEMENT && $reader->name === 'bestellung') {
        // Aktuellen Teilbaum in DOM-Knoten expandieren
        $node   = $reader->expand($dom);
        $xpath  = new DOMXPath($dom);
        $kunde  = $xpath->evaluate('string(kunde)', $node);
        $betrag = $xpath->evaluate('string(betrag)', $node);
        $id     = $node->getAttribute('id');
        printf("Bestellung #%s — %s — %.2f EUR\n", $id, $kunde, (float)$betrag);
    }
}

$reader->close();
Bestellung #42 — Max Mustermann — 199.99 EUR Bestellung #43 — Erika Musterfrau — 49.00 EUR

Schema-Validierung während des Parsens

<?php
$reader = new XMLReader();
$reader->open('daten.xml', null, LIBXML_NONET);

// RelaxNG-Schema zur Validierung einsetzen
if (!$reader->setRelaxNGSchema('schema.rng')) {
    die('Schema konnte nicht geladen werden.');
}

while ($reader->read()) {
    if (!$reader->isValid()) {
        echo 'Ungültiger Knoten: ' . $reader->name . "\n";
    }
}

$reader->close();
echo 'Parsing abgeschlossen.';
Parsing abgeschlossen.

// Wichtig · Fallstricke

Sicherheit: Verwende immer LIBXML_NONET als Option, wenn das XML aus nicht vertrauenswürdigen Quellen stammt, um XXE-Angriffe (XML External Entity) und SSRF zu verhindern. Externe Entitäten und DTDs können ohne diese Maßnahme gefährliche Netzwerkanfragen auslösen.

Speicher: XMLReader ist bewusst kein Baum-Parser. Wer versehentlich expand() in einer Schleife über tausende Knoten aufruft, ohne den DOM-Knoten danach zu verwerfen, kann dennoch hohen Speicherbedarf verursachen.

Fehlerbehandlung: Parsing-Fehler werden standardmäßig als PHP-Warnungen ausgegeben. Mit libxml_use_internal_errors(true) und libxml_get_errors() lassen sie sich strukturiert abfangen.

Ab PHP 8.0 gibt open() bei Fehlern false zurück oder löst eine ValueError aus, statt nur eine Warnung zu erzeugen. In PHP 8.4 wurden statische Fabrikmethoden (XMLReader::fromUri(), XMLReader::fromString()) eingeführt, die sauberer mit Exceptions arbeiten.