W3docs

xml_parse()

La funzione xml_parse() è una funzione built-in di PHP che analizza dati XML con un parser in stile SAX, efficiente per file di grandi dimensioni.

Cos'è xml_parse()?

La funzione xml_parse() è una funzione built-in di PHP che analizza dati XML. Appartiene all'estensione XML Parser di PHP e implementa un parser in streaming in stile SAX (Simple API for XML). A differenza dei parser ad albero, elabora l'XML in modo sequenziale, attivando funzioni di callback quando incontra elementi, attributi e dati di testo. Questo la rende molto efficiente per analizzare file XML di grandi dimensioni senza caricare l'intero documento in memoria.

La funzione xml_parse() è utile quando si deve analizzare dati XML in PHP, ad esempio per estrarre dati da un file XML, trasformare dati XML in un altro formato oppure elaborare stream XML in tempo reale.

Sintassi

La sintassi della funzione xml_parse() è la seguente:

xml_parse($parser, $data, $is_final = false): int

Parametri

  • $parser — l'handle del parser XML restituito da xml_parser_create(). È l'oggetto che mantiene lo stato del parsing.
  • $data — un frammento (o tutto) del testo XML da passare al parser.
  • $is_final — impostato su true quando si passa l'ultimo frammento di dati. Finché è false, il parser mantiene il suo stato in modo da poter chiamare nuovamente xml_parse() con il frammento successivo.

Valore restituito

xml_parse() restituisce 1 (truthy) in caso di successo e 0 (falsy) in caso di errore. Non restituisce i dati analizzati — il contenuto analizzato viene consegnato alle callback degli handler registrate. Quando restituisce 0, ispezionare l'errore con xml_get_error_code() e xml_error_string().

Esempi d'uso

Vediamo alcuni esempi pratici di utilizzo di xml_parse() in PHP.

Esempio 1: Analisi di XML con gli handler degli eventi

xml_parse() è un parser SAX: non costruisce un documento per te, ma genera eventi. Per ottenere qualsiasi output è necessario registrare le funzioni handler. L'esempio seguente utilizza una stringa XML inline in modo da poter essere eseguito così com'è, senza file esterni:

<?php
$xml = <<<XML
<?xml version="1.0"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
</note>
XML;

$parser = xml_parser_create();
// Keep element names in their original case instead of upper-casing them.
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

xml_set_element_handler(
    $parser,
    fn($p, $name, $attrs) => print("Start: $name\n"),
    fn($p, $name)         => print("End:   $name\n")
);
xml_set_character_data_handler($parser, function ($p, $data) {
    $data = trim($data);
    if ($data !== "") {
        echo "Text:  $data\n";
    }
});

if (!xml_parse($parser, $xml, true)) {
    $code = xml_get_error_code($parser);
    echo "Error: " . xml_error_string($code)
       . " at line " . xml_get_current_line_number($parser);
}

xml_parser_free($parser);

Questo stampa:

Start: note
Start: to
Text:  Tove
End:   to
Start: from
Text:  Jani
End:   from
End:   note

Il parser percorre il documento dall'alto verso il basso e chiama gli handler start-element, character-data ed end-element nell'ordine del documento. Li registriamo con xml_set_element_handler() e xml_set_character_data_handler(), passiamo tutto in una singola chiamata ($is_final = true) e liberiamo il parser con xml_parser_free().

Esempio 2: Analisi di un file o stream di dati a frammenti

Il vero punto di forza di xml_parse() è il parsing incrementale: si passa il documento un pezzo alla volta in modo che anche un file da più gigabyte non debba mai stare in memoria. Passare $is_final = false per ogni frammento eccetto l'ultimo:

<?php
$parser = xml_parser_create();
xml_set_element_handler(
    $parser,
    fn($p, $name, $attrs) => print("<$name>\n"),
    fn($p, $name)         => print("</$name>\n")
);

$handle = fopen("data.xml", "r");          // or php://stdin for a stream
while (($chunk = fread($handle, 4096)) !== false) {
    $isFinal = feof($handle);
    if (!xml_parse($parser, $chunk, $isFinal)) {
        $code = xml_get_error_code($parser);
        echo "XML error: " . xml_error_string($code)
           . " at line " . xml_get_current_line_number($parser);
        break;
    }
    if ($isFinal) {
        break;
    }
}
fclose($handle);
xml_parser_free($parser);

Poiché il parser mantiene il suo stato tra una chiamata e l'altra, un elemento può iniziare in un frammento e terminare in un altro — xml_parse() assembla correttamente gli eventi. È questo che rende la funzione adatta a file XML di grandi dimensioni dove un approccio ad albero come SimpleXML esaurirebbe la memoria.

Esempio 3 (riferimento): analisi di un file in una sola operazione

Se il proprio XML è abbastanza piccolo da stare in memoria, è possibile leggerlo con file_get_contents() e passare l'intera stringa a xml_parse() in una singola chiamata. Gli handler possono essere anche funzioni con nome (stringhe) invece di closure:

$xml_parser = xml_parser_create();
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0);

// Define handler functions
function startElement($parser, $name, $attrs) {
    echo "Start element: $name\n";
}
function endElement($parser, $name) {
    echo "End element: $name\n";
}
function characterData($parser, $data) {
    echo "Data: $data\n";
}

// Set handlers
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");

$xml_data = file_get_contents("data.xml");
if (!xml_parse($xml_parser, $xml_data, true)) {
    $error_message = xml_error_string(xml_get_error_code($xml_parser));
    $error_line = xml_get_current_line_number($xml_parser);
    echo "XML Parsing Error: $error_message at line $error_line";
}
xml_parser_free($xml_parser);

Questo codice crea un parser XML usando xml_parser_create() e imposta un'opzione per disattivare il case folding. Quindi definisce tre funzioni di callback: startElement() per i tag di apertura, endElement() per i tag di chiusura e characterData() per il contenuto testuale. Questi handler vengono registrati usando xml_set_element_handler() e xml_set_character_data_handler().

Lo script legge "data.xml" e lo passa a xml_parse(). Mentre il parser scorre l'XML, chiama automaticamente gli handler registrati. Se si verifica un errore durante il parsing, il codice recupera il codice di errore e il messaggio usando xml_get_error_code() e xml_error_string(), e restituisce un errore descrittivo. Infine, libera la memoria del parser con xml_parser_free().

Problemi comuni

  • Nessun handler, nessun output. xml_parse() attiva solo le callback registrate. Senza handler impostati, il parsing ha successo ma non produce nulla di visibile.
  • I dati di tipo character arrivano a pezzi. Un singolo nodo di testo può attivare xml_set_character_data_handler più di una volta (ad esempio, attorno a riferimenti ad entità), quindi accumulare il testo in un buffer invece di assumere di riceverlo tutto in una volta.
  • Gli spazi bianchi contano come dati di testo. L'indentazione tra i tag attiva l'handler dei dati di testo. Usare trim() (come nell'Esempio 1) se si vuole considerare solo il testo reale.
  • Liberare sempre il parser. Chiamare xml_parser_free() al termine; su PHP 8+ l'handle è un oggetto XMLParser che viene raccolto dal garbage collector, ma liberarlo esplicitamente mantiene la memoria sotto controllo negli script a lunga esecuzione.

Funzioni correlate

Conclusione

La funzione xml_parse() di PHP è il motore dell'estensione XML Parser in stile SAX. Invece di restituire un documento, scorre l'XML e attiva gli handler registrati, restituendo 1 in caso di successo e 0 in caso di errore. Il suo parametro $is_final consente di analizzare i dati a frammenti, motivo per cui rimane efficiente in termini di memoria anche su file di grandi dimensioni. Abbinata a xml_parser_create(), alle funzioni di configurazione degli handler e a xml_parser_free(), offre un modo veloce e a basso consumo di memoria per elaborare XML in PHP.

Pratica

Pratica
Cos'è XML Parser in PHP?
Cos'è XML Parser in PHP?
Was this page helpful?