← Back to PHP Course | Chapter 14: Advanced PHP | Lesson 23 of 24

PHP XML Expat Parser

SimpleXML loads an entire XML document into memory as one object tree before you can read anything from it, which becomes a problem for extremely large XML files. The Expat parser takes a completely different approach: it reads through an XML document once, from start to finish, firing off callback functions as it encounters each opening tag, closing tag, and piece of text.

Why Use a Streaming Parser

SimpleXML and DOM both load an entire XML document into memory as a tree before you can read anything -- fine for small files, but potentially a serious memory problem for a multi-gigabyte XML export. Expat instead streams through the document once, processing it piece by piece without ever holding the whole thing in memory.

Note: Reach for Expat specifically when working with XML files large enough that loading the whole thing into memory at once would be a real concern.

Warning: Expat's event-driven style is more complex to work with than SimpleXML for straightforward "just give me the values" tasks -- only reach for it when its memory efficiency genuinely matters.

Example: Why Use a Streaming Parser

php
<?php
echo "Expat streams through XML piece by piece instead of loading a full tree into memory";
?>

Creating a Parser and Registering Handlers

xml_parser_create() creates a new parser instance, and xml_set_element_handler($parser, $startCallback, $endCallback) registers two functions: one called every time an opening tag is encountered, and another every time a closing tag is encountered.

Note: Name your start and end handler functions clearly (like startElement and endElement) so their purpose is obvious when reading the registration call.

Warning: Handler functions must match Expat's expected parameter signature exactly (parser, tag name, and attributes for the start handler) or the callback will not receive the data correctly.

Example: Creating a Parser and Registering Handlers

php
<?php
$parser = xml_parser_create();
xml_set_element_handler($parser, function ($parser, $name) {
    echo "Start: $name\n";
}, function ($parser, $name) {
    echo "End: $name\n";
});
echo "Handlers registered";
xml_parser_free($parser);
?>

Parsing XML Data

xml_parse($parser, $data, $isFinal) feeds a chunk of XML text to the parser, triggering the registered callbacks as it processes that chunk -- passing true as the final $isFinal argument tells the parser this is the last piece of data, letting it detect an incomplete document.

Note: For data already fully available as one string, pass true for $isFinal on the single xml_parse() call, marking the entire document as complete.

Warning: xml_parse() returns 0 on a parsing error -- check its return value, especially when feeding data in multiple chunks, to catch malformed XML early.

Example: Parsing XML Data

php
<?php
$parser = xml_parser_create();
xml_set_element_handler($parser, function ($p, $name) { echo "<$name>\n"; }, function ($p, $name) { echo "</$name>\n"; });
xml_parse($parser, "<book><title>PHP</title></book>", true);
xml_parser_free($parser);
?>

Building Data with Handler State

Since each Expat callback only sees the single tag or text chunk it was just given, tracking meaningful state across multiple calls (like which element is currently open, or accumulating a list of parsed records) requires using variables outside the handler functions, often global or captured by reference.

Note: Use a class with the parser state as object properties, rather than global variables, for anything beyond the simplest Expat-based parsing script.

Warning: Relying on global variables inside handler functions works but can become hard to manage and error-prone once the parsing logic grows more complex than a few tags.

Example: Building Data with Handler State

php
<?php
$titles = [];
$parser = xml_parser_create();
xml_set_element_handler($parser, function () {}, function () {});
xml_set_character_data_handler($parser, function ($p, $data) use (&$titles) {
    if (trim($data) !== '') $titles[] = trim($data);
});
xml_parse($parser, "<book>PHP Basics</book>", true);
print_r($titles);
xml_parser_free($parser);
?>

Freeing Parser Resources

xml_parser_free($parser) releases the parser instance's resources once parsing is complete -- important to call explicitly in scripts that create many parsers (like processing many files in a loop), though PHP also cleans it up automatically when the script ends.

Note: Call xml_parser_free() as soon as you are done with a parser, especially in any loop that creates a new one for each of several files.

Warning: Attempting to use xml_parse() on a parser instance after it has already been freed will fail, so make sure all parsing work finishes before freeing it.

Example: Freeing Parser Resources

php
<?php
$parser = xml_parser_create();
xml_parse($parser, "<book></book>", true);
xml_parser_free($parser);
echo "Parser resources released";
?>
Common Mistakes
  1. Choosing Expat for small, everyday XML parsing tasks where SimpleXML would be far simpler and perfectly adequate -- Expat's complexity is only worth it for very large files.
  2. Forgetting that Expat callbacks fire in document order as parsing proceeds -- you cannot "look ahead" to data that has not been reached yet, unlike with a fully-loaded SimpleXML tree.
  3. Not tracking parsing state (like which element is currently open) across callback calls, since each callback only knows about the single tag or text chunk it was just given.
Chapter Summary
  • xml_parser_create() creates a new Expat parser instance, and xml_set_element_handler() registers callback functions for opening and closing tags.
  • xml_parse($parser, $data) feeds XML data to the parser, which processes it and triggers the registered callbacks along the way.
  • Expat is a streaming, event-based parser that never holds the entire document in memory at once, making it well-suited to very large XML files.
Browser Support

The Expat XML parser extension has been bundled with PHP since PHP 4 and is enabled by default in most standard installations.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.