W3docs

PHP XML Expat

Aprende el analizador XML Expat de PHP: análisis basado en eventos con callbacks, lectura de atributos, manejo de errores y ejemplos ejecutables.

PHP incluye un analizador XML integrado basado en la biblioteca Expat. A diferencia de los analizadores basados en árbol como SimpleXML o el DOM, Expat es un analizador basado en eventos (o estilo "SAX"): en lugar de cargar todo el documento en memoria como un árbol, lo recorre en forma de flujo y llama a tus funciones de callback cuando encuentra cada etiqueta de apertura, etiqueta de cierre y fragmento de texto.

Esta página explica cómo funciona Expat, cuándo debes usarlo y recorre un ejemplo completo y ejecutable con manejo de atributos y verificación de errores.

¿Qué es el analizador Expat?

Expat es un analizador XML rápido, ligero y no validador escrito en C. La extensión xml de PHP lo envuelve a través de la familia de funciones xml_parser_*. Sus características definitorias:

  • Basado en eventos — registras callbacks; el analizador los llama a medida que lee.
  • Streaming — el XML puede alimentarse en fragmentos (xml_parse() puede llamarse repetidamente), por lo que el uso de memoria se mantiene bajo incluso para archivos grandes.
  • No validador — comprueba que el documento esté bien formado, pero no valida contra un DTD o esquema.

Este es el intercambio opuesto al de un analizador de árbol. Un analizador de árbol es conveniente (puedes navegar todo el documento con $xml->note->message) pero mantiene todo en memoria. Expat mantiene la memoria plana a costa de obligarte a ti a rastrear el estado a medida que los eventos fluyen.

¿Cuándo deberías usar Expat?

  • Documentos grandes — feeds o exportaciones de varios megabytes donde construir un árbol DOM completo sería un desperdicio.
  • Fuentes en streaming — datos que llegan por un socket o en fragmentos, donde no puedes esperar al documento completo.
  • Extraer y descartar — solo necesitas algunos valores y no quieres la sobrecarga de un árbol.

Para documentos pequeños que simplemente quieres leer, SimpleXML requiere mucho menos código. Consulta Tipos de analizadores XML de PHP para una comparación lado a lado.

Configurar la extensión

La extensión xml se incluye con PHP y está habilitada por defecto en la mayoría de las compilaciones. Si las funciones del analizador no están disponibles, habilítala en php.ini:

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

Puedes confirmar que está disponible en tiempo de ejecución:

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

Un ejemplo completo de Expat

El siguiente ejemplo analiza una cadena XML e imprime cada evento. Registra tres handlers — para etiquetas de apertura, etiquetas de cierre y texto — y termina con una verificación de errores adecuada. Leer desde una cadena lo mantiene autocontenido; la variante basada en archivos viene a continuación.

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

Salida:

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

Cómo funciona. xml_parser_create() construye el analizador. xml_set_element_handler() (referencia) registra los callbacks de etiquetas de apertura y cierre, y xml_set_character_data_handler() (referencia) registra el callback de texto. xml_parse() luego impulsa todo el proceso, llamando a esas funciones en el orden del documento. El handler de apertura recibe un array $attrs, por lo que leer un atributo como importance es simplemente $attrs['importance'].

Dos detalles que confunden a la gente:

  • Los espacios en blanco son datos de carácter. Los saltos de línea y la sangría entre etiquetas también activan el handler de datos de carácter — por eso el ejemplo llama a trim() y omite las cadenas vacías.
  • Plegado de mayúsculas. Por defecto, Expat convierte los nombres de elementos a mayúsculas. XML_OPTION_CASE_FOLDING establecido en false mantiene el uso de mayúsculas original, que es casi siempre lo que quieres.

Analizar un archivo en fragmentos

La verdadera fortaleza de Expat es el streaming. Lee el archivo un bloque a la vez y alimenta cada bloque a xml_parse(), pasando true solo en el último fragmento (detectado 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";
}

Dado que el documento nunca se mantiene completamente en memoria, este patrón maneja archivos mucho más grandes que tu límite de memoria. Los handlers pueden ser nombres de funciones simples (como aquí), closures o callables [$object, 'method'] mediante xml_set_object().

Manejo de errores

Siempre comprueba el valor de retorno de xml_parse() — devuelve false en un documento mal formado. El código de error de xml_get_error_code() puede convertirse en un mensaje legible con xml_error_string(), y puedes localizar el error con xml_get_current_line_number() y xml_get_current_column_number(). Omitir esta comprobación significa que un feed defectuoso falla silenciosamente.

Ventajas de Expat

  • Bajo consumo de memoria — transmite el documento en lugar de construir un árbol, por lo que la memoria se mantiene constante independientemente del tamaño del archivo.
  • Rápido — el analizador C subyacente está altamente optimizado.
  • Multiplataforma — incluido con PHP en todos los sistemas operativos compatibles.
  • Control detallado — decides exactamente qué hacer en cada evento, ignorando todo lo que no necesitas.

El intercambio: como no hay árbol, debes rastrear el contexto (en qué elemento estás) por tu cuenta. Si ese seguimiento se vuelve pesado, un analizador de árbol como SimpleXML o DOM es la mejor opción.

Conclusión

El analizador XML basado en Expat le ofrece a PHP una forma rápida, eficiente en memoria y basada en eventos de leer XML. Registra handlers para los eventos que te interesan, alimenta el documento con xml_parse(), comprueba los errores y libera el analizador cuando termines. Úsalo para documentos grandes o en streaming; para los pequeños, SimpleXML suele ser la opción más sencilla.

Práctica

Práctica
¿Cuáles son las características del analizador Expat en PHP?
¿Cuáles son las características del analizador Expat en PHP?
Was this page helpful?