W3docs

PHP XML Expat

Découvrez le parseur XML Expat de PHP : analyse événementielle en streaming avec callbacks, lecture d'attributs, gestion des erreurs et exemples.

PHP intègre un parseur XML natif basé sur la bibliothèque Expat. Contrairement aux parseurs arborescents comme SimpleXML ou le DOM, Expat est un parseur événementiel (ou « style SAX ») : au lieu de charger tout le document en mémoire sous forme d'arbre, il parcourt le XML en streaming et déclenche vos fonctions de rappel dès qu'il rencontre une balise ouvrante, une balise fermante ou un fragment de texte.

Cette page explique le fonctionnement d'Expat, dans quels cas l'utiliser, et présente un exemple complet et exécutable avec gestion des attributs et vérification des erreurs.

Qu'est-ce que le parseur Expat ?

Expat est un parseur XML rapide, léger et non validant écrit en C. L'extension PHP xml l'expose via la famille de fonctions xml_parser_*. Ses caractéristiques principales :

  • Événementiel — vous enregistrez des callbacks ; le parseur les appelle au fil de la lecture.
  • Streaming — le XML peut lui être soumis par morceaux (xml_parse() peut être appelé plusieurs fois), ce qui maintient une faible empreinte mémoire même pour de gros fichiers.
  • Non validant — il vérifie que le document est bien formé, mais ne le valide pas par rapport à une DTD ou à un schéma.

C'est le compromis inverse d'un parseur arborescent. Un parseur arborescent est pratique (vous pouvez naviguer dans tout le document avec $xml->note->message) mais charge tout en mémoire. Expat maintient une empreinte mémoire constante, au prix de vous obliger à gérer l'état au fil des événements.

Quand utiliser Expat ?

  • Grands documents — flux ou exports de plusieurs mégaoctets pour lesquels construire un arbre DOM complet serait coûteux.
  • Sources en streaming — données arrivant via un socket ou par morceaux, sans pouvoir attendre la totalité du document.
  • Extraction et rejet — vous n'avez besoin que de quelques valeurs et souhaitez éviter le surcoût d'un arbre.

Pour de petits documents à lire simplement, SimpleXML demande bien moins de code. Consultez Types de parseurs XML PHP pour une comparaison côte à côte.

Configurer l'extension

L'extension xml est fournie avec PHP et activée par défaut dans la plupart des compilations. Si les fonctions du parseur sont absentes, activez-la dans php.ini :

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

Vous pouvez vérifier sa disponibilité à l'exécution :

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

Un exemple Expat complet

L'exemple ci-dessous analyse une chaîne XML et affiche chaque événement. Il enregistre trois handlers — pour les balises ouvrantes, les balises fermantes et le texte — et se termine par une vérification correcte des erreurs. La lecture depuis une chaîne le rend autonome ; la variante basée sur un fichier suit.

<?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);

Résultat :

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

Comment ça fonctionne. xml_parser_create() crée le parseur. xml_set_element_handler() (référence) enregistre les callbacks de balises ouvrantes et fermantes, et xml_set_character_data_handler() (référence) enregistre le callback de texte. xml_parse() pilote ensuite l'ensemble, en appelant ces fonctions dans l'ordre du document. Le handler de début reçoit un tableau $attrs, donc lire un attribut comme importance revient simplement à écrire $attrs['importance'].

Deux points qui piègent souvent les développeurs :

  • Les espaces blancs sont des données de caractères. Les sauts de ligne et l'indentation entre les balises déclenchent aussi le handler de données de caractères — c'est pourquoi l'exemple appelle trim() et ignore les chaînes vides.
  • Le repliement de casse. Par défaut, Expat met les noms d'éléments en majuscules. XML_OPTION_CASE_FOLDING défini à false conserve la casse d'origine, ce qui est presque toujours ce que vous souhaitez.

Analyser un fichier par morceaux

La vraie force d'Expat est le streaming. Lisez le fichier bloc par bloc et soumettez chaque bloc à xml_parse(), en passant true uniquement pour le dernier morceau (détecté avec 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";
}

Le document n'étant jamais intégralement chargé en mémoire, ce modèle gère des fichiers bien plus grands que votre limite mémoire. Les handlers peuvent être des noms de fonctions simples (comme ici), des closures, ou des callables [$object, 'method'] via xml_set_object().

Gestion des erreurs

Vérifiez toujours la valeur de retour de xml_parse() — elle retourne false en cas de document malformé. Le code d'erreur fourni par xml_get_error_code() peut être converti en message lisible avec xml_error_string(), et vous pouvez localiser précisément l'erreur avec xml_get_current_line_number() et xml_get_current_column_number(). Omettre cette vérification signifie qu'un flux corrompu échoue silencieusement.

Avantages d'Expat

  • Faible empreinte mémoire — analyse le document en streaming plutôt qu'en construisant un arbre, ce qui maintient la mémoire constante quelle que soit la taille du fichier.
  • Rapide — le parseur C sous-jacent est très optimisé.
  • Multiplateforme — fourni avec PHP sur chaque système d'exploitation supporté.
  • Contrôle fin — vous décidez exactement quoi faire à chaque événement, en ignorant tout ce dont vous n'avez pas besoin.

Le compromis : puisqu'il n'y a pas d'arbre, vous devez gérer le contexte (dans quel élément vous vous trouvez) vous-même. Si cette gestion devient lourde, un parseur arborescent comme SimpleXML ou DOM est plus adapté.

Conclusion

Le parseur XML basé sur Expat offre à PHP un moyen rapide, économe en mémoire et événementiel de lire du XML. Enregistrez des handlers pour les événements qui vous intéressent, soumettez le document avec xml_parse(), vérifiez les erreurs et libérez le parseur une fois terminé. Utilisez-le pour les grands documents ou les sources en streaming ; pour les petits, SimpleXML est généralement le choix le plus simple.

Pratique

Pratique
Quelles sont les caractéristiques du parseur Expat en PHP ?
Quelles sont les caractéristiques du parseur Expat en PHP ?
Was this page helpful?