W3docs

PHP XML Expat

Scopri il parser XML Expat di PHP: parsing basato su eventi con callback, lettura di attributi, gestione degli errori ed esempi eseguibili.

PHP include un parser XML integrato basato sulla libreria Expat. A differenza dei parser ad albero come SimpleXML o il DOM, Expat è un parser basato su eventi (o in stile "SAX"): invece di caricare l'intero documento in memoria come un albero, scorre il XML in streaming e richiama le tue funzioni di callback quando incontra ogni tag di apertura, tag di chiusura e frammento di testo.

Questa pagina spiega come funziona Expat, quando dovresti usarlo, e illustra un esempio completo ed eseguibile con gestione degli attributi e controllo degli errori.

Cos'è il parser Expat?

Expat è un parser XML veloce, leggero e non validante scritto in C. L'estensione PHP xml lo racchiude attraverso la famiglia di funzioni xml_parser_*. Le sue caratteristiche principali:

  • Basato su eventi — registri callback; il parser li chiama mentre legge.
  • Streaming — il XML può essere fornito in pezzi (xml_parse() può essere chiamato ripetutamente), quindi l'utilizzo della memoria rimane basso anche per file di grandi dimensioni.
  • Non validante — verifica che il documento sia ben formato, ma non lo valida rispetto a un DTD o uno schema.

Questo è il compromesso opposto rispetto a un parser ad albero. Un parser ad albero è comodo (puoi navigare l'intero documento con $xml->note->message) ma mantiene tutto in memoria. Expat mantiene la memoria piatta al costo di costringerti a tracciare lo stato mentre gli eventi scorrono.

Quando dovresti usare Expat?

  • Documenti di grandi dimensioni — feed o esportazioni da diversi megabyte dove costruire un albero DOM completo sarebbe dispendioso.
  • Sorgenti in streaming — dati in arrivo tramite un socket o in blocchi, dove non puoi aspettare l'intero documento.
  • Estrai e scarta — hai bisogno solo di alcuni valori e non vuoi il sovraccarico di un albero.

Per i documenti piccoli che vuoi semplicemente leggere, SimpleXML richiede molto meno codice. Consulta Tipi di parser XML PHP per un confronto affiancato.

Configurazione dell'estensione

L'estensione xml è inclusa in PHP e abilitata per impostazione predefinita nella maggior parte delle build. Se le funzioni del parser sono assenti, abilitala in php.ini:

extension=xml        ; Linux/macOS
extension=php_xml.dll ; Windows

Puoi confermare che sia disponibile in fase di esecuzione:

<?php
var_dump(function_exists('xml_parser_create')); // bool(true)

Un esempio completo con Expat

L'esempio seguente analizza una stringa XML e stampa ogni evento. Registra tre handler — per i tag di apertura, i tag di chiusura e il testo — e termina con un corretto controllo degli errori. La lettura da una stringa lo rende autonomo; segue la variante basata su file.

<?php
$xml = <<<XML
<?xml version="1.0" encoding="UTF-8"?>
<note importance="high">
  <to>User</to>
  <from>System</from>
  <message>Hello from Expat</message>
</note>
XML;

// 1. Create the parser.
$parser = xml_parser_create();

// Keep tag names in their original case (Expat upper-cases them by default).
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);

// 2. Register handlers for start/end tags and for character data.
xml_set_element_handler(
    $parser,
    function ($parser, $name, $attrs) {
        echo "Start: $name";
        foreach ($attrs as $key => $value) {
            echo " [$key=$value]";
        }
        echo "\n";
    },
    function ($parser, $name) {
        echo "End:   $name\n";
    }
);

xml_set_character_data_handler($parser, function ($parser, $data) {
    $data = trim($data);           // text between tags includes whitespace
    if ($data !== '') {
        echo "Text:  $data\n";
    }
});

// 3. Feed the document to the parser (true = this is the final chunk).
if (!xml_parse($parser, $xml, true)) {
    $code = xml_get_error_code($parser);
    die(sprintf(
        "XML error: %s at line %d\n",
        xml_error_string($code),
        xml_get_current_line_number($parser)
    ));
}

// 4. Release the parser.
xml_parser_free($parser);

Output:

Start: note [importance=high]
Start: to
Text:  User
End:   to
Start: from
Text:  System
End:   from
Start: message
Text:  Hello from Expat
End:   message
End:   note

Come funziona. xml_parser_create() costruisce il parser. xml_set_element_handler() (riferimento) registra i callback per i tag di apertura e chiusura, e xml_set_character_data_handler() (riferimento) registra il callback per il testo. xml_parse() poi gestisce tutto, chiamando quelle funzioni nell'ordine del documento. L'handler di apertura riceve un array $attrs, quindi leggere un attributo come importance è semplicemente $attrs['importance'].

Due dettagli che creano confusione:

  • Lo spazio bianco è un dato di tipo carattere. Le nuove righe e il rientro tra i tag attivano anch'essi l'handler dei dati di tipo carattere — ecco perché l'esempio chiama trim() e salta le stringhe vuote.
  • Case folding. Per impostazione predefinita Expat converte i nomi degli elementi in maiuscolo. Impostando XML_OPTION_CASE_FOLDING a false si mantiene la maiuscolazione originale, che è quasi sempre quello che si desidera.

Analisi di un file in blocchi

Il vero punto di forza di Expat è lo streaming. Leggi il file un blocco alla volta e fornisci ogni blocco a xml_parse(), passando true solo nell'ultimo blocco (rilevato con feof()):

<?php
$parser = xml_parser_create();
xml_set_element_handler($parser, 'startElement', 'endElement');
xml_set_character_data_handler($parser, 'characterData');

$fp = fopen('example.xml', 'r') or die("Could not open file\n");

while ($data = fread($fp, 4096)) {
    if (!xml_parse($parser, $data, feof($fp))) {
        $code = xml_get_error_code($parser);
        die(sprintf(
            "XML error: %s at line %d\n",
            xml_error_string($code),
            xml_get_current_line_number($parser)
        ));
    }
}

fclose($fp);
xml_parser_free($parser);

function startElement($parser, $name, $attrs) { echo "Start: $name\n"; }
function endElement($parser, $name)           { echo "End:   $name\n"; }
function characterData($parser, $data) {
    $data = trim($data);
    if ($data !== '') echo "Text:  $data\n";
}

Poiché il documento non viene mai mantenuto interamente in memoria, questo schema gestisce file molto più grandi del limite di memoria. Gli handler possono essere nomi di funzione normali (come qui), closure, o callable [$object, 'method'] tramite xml_set_object().

Gestione degli errori

Controlla sempre il valore restituito da xml_parse() — restituisce false su un documento mal formato. Il codice di errore restituito da xml_get_error_code() può essere convertito in un messaggio leggibile con xml_error_string(), e puoi individuare la posizione con xml_get_current_line_number() e xml_get_current_column_number(). Saltare questo controllo significa che un feed non valido fallisce silenziosamente.

Vantaggi di Expat

  • Basso consumo di memoria — elabora il documento in streaming invece di costruire un albero, quindi la memoria rimane piatta indipendentemente dalla dimensione del file.
  • Veloce — il parser C sottostante è altamente ottimizzato.
  • Multi-piattaforma — incluso con PHP su ogni sistema operativo supportato.
  • Controllo granulare — decidi esattamente cosa fare a ogni evento, ignorando tutto ciò di cui non hai bisogno.

Il compromesso: poiché non esiste un albero, tu devi tracciare il contesto (in quale elemento ti trovi) da solo. Se questa contabilità diventa pesante, un parser ad albero come SimpleXML o DOM è la scelta migliore.

Conclusione

Il parser XML basato su Expat offre a PHP un modo veloce, efficiente in termini di memoria e guidato da eventi per leggere XML. Registra gli handler per gli eventi che ti interessano, fornisci il documento con xml_parse(), controlla gli errori e libera il parser al termine. Usalo per documenti grandi o in streaming; per quelli piccoli, SimpleXML è generalmente la scelta più semplice.

Pratica

Pratica
Quali sono le caratteristiche del parser Expat in PHP?
Quali sono le caratteristiche del parser Expat in PHP?
Was this page helpful?