W3docs

xml_set_character_data_handler()

The xml_set_character_data_handler() function is a PHP built-in function that sets a user-defined function as the handler for character data in an XML parser.

The xml_set_character_data_handler() function registers a user-defined callback that the XML parser calls every time it encounters character data — the text content that sits between XML tags. It belongs to the legacy xml extension, which wraps the event-driven Expat parser, so it is part of PHP's stream-based ("SAX-style") parsing API rather than the tree-based DOMDocument or SimpleXML approaches.

You reach for this function whenever you want to capture or transform the text inside elements as the document streams past — for example, collecting the title of each <item> in an RSS feed, or running a transformation on extracted text without ever building the whole document in memory. It is almost always used together with xml_set_element_handler(), which tells you which element the text belongs to.

Heads up: The xml extension is legacy. For brand-new code, prefer SimpleXML or DOMDocument. Use this handler when you are maintaining existing Expat-based parsers or genuinely need event-driven streaming for very large documents.

Syntax

xml_set_character_data_handler(XMLParser $parser, callable $handler): bool
  • $parser — the parser created by xml_parser_create().
  • $handler — the callback to invoke for character data. Pass a function name as a string, a closure, or an array for a method callback. Passing an empty string "" unsets a previously registered handler.

The function returns true on success.

The handler signature

Your handler receives two arguments — the parser and the text chunk:

function handler(XMLParser $parser, string $data): void

Older documentation sometimes shows a third $length parameter, but the Expat-based xml extension does not pass one. On PHP 8+, declaring a third required parameter throws an ArgumentCountError, so keep your handler to two parameters (or make extra ones optional).

Usage Examples

Example 1: A minimal character data handler

Create a parser, attach a handler, then feed it an XML string. The handler simply echoes the trimmed text it receives:

function handle_character_data($parser, $data) {
    echo trim($data);
}

$xml_parser = xml_parser_create();
xml_set_character_data_handler($xml_parser, "handle_character_data");

$xml_data = '<root><item>Hello World</item></root>';
xml_parse($xml_parser, $xml_data, true);

xml_parser_free($xml_parser);
// Output: Hello World

xml_parser_create() builds the parser, xml_set_character_data_handler() wires up the callback, xml_parse() streams the data (the final true marks it as the last chunk), and xml_parser_free() releases the parser.

The chunking gotcha

The parser can call your handler multiple times for a single element, and it also fires for the whitespace between elements. It does not hand you one tidy string per element. Watch what happens with two items and some indentation:

function show($parser, $data) {
    echo '[' . $data . "]\n";
}

$p = xml_parser_create();
xml_set_character_data_handler($p, "show");

$xml = '<root><item>Hello World</item>  <item>Goodbye</item></root>';
xml_parse($p, $xml, true);
xml_parser_free($p);

// Output:
// [Hello World]
// [  ]
// [Goodbye]

The two-space gap between the </item> and the next <item> triggers its own call. Long text can also arrive split across several calls. Because of this, you should buffer the data and only act on it once you know the element has ended.

Example 2: Buffering text with an element handler

The reliable pattern is: reset a buffer when an element starts, append every character-data chunk to it, and read the buffer when the element ends. That is why xml_set_element_handler() is almost always paired with this function.

$buffer = '';

function start_element($parser, $name, $attrs) {
    global $buffer;
    $buffer = '';            // start collecting fresh text
}

function character_data($parser, $data) {
    global $buffer;
    $buffer .= $data;        // chunks may arrive in pieces — append
}

function end_element($parser, $name) {
    global $buffer;
    $text = trim($buffer);
    if ($text !== '') {
        echo "$name: $text\n";
    }
}

$parser = xml_parser_create();
xml_set_element_handler($parser, "start_element", "end_element");
xml_set_character_data_handler($parser, "character_data");

$xml = '<books><title>PHP Basics</title><title>Advanced XML</title></books>';
xml_parse($parser, $xml, true);
xml_parser_free($parser);

// Output:
// TITLE: PHP Basics
// TITLE: Advanced XML

Element names arrive upper-cased by default because the parser folds case. To keep the original casing, disable case folding with xml_parser_set_option():

xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);

Common pitfalls

  • Assuming one call per element. As shown above, character data can be delivered in several chunks; always accumulate into a buffer.
  • Whitespace-only calls. Indentation and newlines between tags trigger the handler with whitespace. Filter these out with trim() before acting.
  • A third handler parameter. The Expat handler passes only $parser and $data; a required $length parameter causes an ArgumentCountError.
  • Registering the handler after parsing. Attach all handlers before you call xml_parse(), or events will already have passed.

Conclusion

xml_set_character_data_handler() registers a callback for the text between XML tags in PHP's legacy, Expat-based xml extension. Its handler takes two arguments — the parser and the data chunk — and may fire many times per element, including for whitespace, so the dependable approach is to buffer text and process it when the element ends via xml_set_element_handler(). For new projects, prefer the modern SimpleXML or DOMDocument APIs; reach for this function when maintaining existing parsers or streaming very large documents.

Practice

Practice
What does the 'xml_set_character_data_handler()' function do in PHP?
What does the 'xml_set_character_data_handler()' function do in PHP?
Was this page helpful?