W3docs

PHP XML Expat

Aprenda o parser XML Expat do PHP: modo orientado a eventos, streaming, callbacks, leitura de atributos, verificação de erros e exemplos práticos.

O PHP inclui um parser XML integrado baseado na biblioteca Expat. Ao contrário de parsers baseados em árvore, como o SimpleXML ou o DOM, o Expat é um parser orientado a eventos (estilo "SAX"): em vez de carregar o documento inteiro na memória como uma árvore, ele percorre o XML em streaming e aciona suas funções de callback ao encontrar cada tag de início, tag de fim e trecho de texto.

Esta página explica como o Expat funciona, quando você deve utilizá-lo e apresenta um exemplo completo e executável com tratamento de atributos e verificação de erros.

O que é o parser Expat?

O Expat é um parser XML rápido, leve e não validador escrito em C. A extensão PHP xml o envolve por meio da família de funções xml_parser_*. Suas características principais:

  • Orientado a eventos — você registra callbacks de handler; o parser os chama conforme realiza a leitura.
  • Streaming — o XML pode ser alimentado em partes (xml_parse() pode ser chamado repetidamente), mantendo o uso de memória baixo mesmo para arquivos grandes.
  • Não validador — verifica se o documento está bem formado, mas não valida contra um DTD ou esquema.

Esse é o trade-off oposto ao de um parser em árvore. Um parser em árvore é conveniente (você pode navegar pelo documento inteiro com $xml->note->message), mas mantém tudo na memória. O Expat mantém a memória plana ao custo de forçar você a rastrear o estado conforme os eventos chegam.

Quando usar o Expat?

  • Documentos grandes — feeds ou exportações com vários megabytes, nos quais construir uma árvore DOM completa seria custoso.
  • Fontes em streaming — dados chegando por um socket ou em partes, onde não é possível aguardar o documento inteiro.
  • Extrair e descartar — você precisa apenas de alguns valores e não quer o overhead de uma árvore.

Para documentos pequenos que você simplesmente quer ler, o SimpleXML requer muito menos código. Consulte Tipos de parsers XML do PHP para uma comparação lado a lado.

Configurando a extensão

A extensão xml é fornecida com o PHP e habilitada por padrão na maioria das compilações. Se as funções do parser estiverem ausentes, habilite-a no php.ini:

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

Você pode confirmar se está disponível em tempo de execução:

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

Um exemplo completo com Expat

O exemplo abaixo faz o parse de uma string XML e imprime cada evento. Ele registra três handlers — para tags de início, tags de fim e texto — e finaliza com verificação de erros adequada. A leitura a partir de uma string o torna autocontido; a variante baseada em arquivo vem a seguir.

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

Saída:

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

Como funciona. xml_parser_create() cria o parser. xml_set_element_handler() (referência) registra os callbacks de tag de início e fim, e xml_set_character_data_handler() (referência) registra o callback de texto. xml_parse() então conduz todo o processo, chamando essas funções na ordem do documento. O handler de início recebe um array $attrs, portanto ler um atributo como importance é simplesmente $attrs['importance'].

Dois detalhes que costumam confundir as pessoas:

  • Espaços em branco são dados de caractere. As quebras de linha e a indentação entre tags também acionam o handler de dados de caractere — por isso o exemplo chama trim() e pula strings vazias.
  • Case folding. Por padrão, o Expat converte os nomes dos elementos para maiúsculas. Definir XML_OPTION_CASE_FOLDING como false mantém a capitalização original, que é quase sempre o que você deseja.

Fazendo o parse de um arquivo em partes

A verdadeira força do Expat é o streaming. Leia o arquivo um bloco de cada vez e alimente cada bloco em xml_parse(), passando true apenas no último bloco (detectado com 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";
}

Como o documento nunca é totalmente mantido na memória, esse padrão lida com arquivos muito maiores do que o limite de memória. Os handlers podem ser nomes de funções simples (como aqui), closures ou callables [$object, 'method'] via xml_set_object().

Tratamento de erros

Sempre verifique o valor de retorno de xml_parse() — ele retorna false em um documento malformado. O código de erro de xml_get_error_code() pode ser convertido em uma mensagem legível com xml_error_string(), e você pode identificar a localização com xml_get_current_line_number() e xml_get_current_column_number(). Ignorar essa verificação significa que um feed corrompido falha silenciosamente.

Vantagens do Expat

  • Baixo uso de memória — faz streaming do documento em vez de construir uma árvore, mantendo a memória plana independentemente do tamanho do arquivo.
  • Rápido — o parser C subjacente é altamente otimizado.
  • Multiplataforma — incluído com o PHP em todos os sistemas operacionais suportados.
  • Controle granular — você decide exatamente o que fazer em cada evento, ignorando tudo que não precisa.

O trade-off: como não há árvore, você deve rastrear o contexto (em qual elemento está) por conta própria. Se esse controle de estado se tornar pesado, um parser em árvore como SimpleXML ou DOM é a escolha mais adequada.

Conclusão

O parser XML baseado em Expat oferece ao PHP uma forma rápida, eficiente em memória e orientada a eventos para ler XML. Registre handlers para os eventos de seu interesse, alimente o documento com xml_parse(), verifique erros e libere o parser ao concluir. Use-o para documentos grandes ou em streaming; para documentos pequenos, o SimpleXML costuma ser a escolha mais simples.

Prática

Prática
Quais são as características do parser Expat no PHP?
Quais são as características do parser Expat no PHP?
Was this page helpful?