xml_set_end_namespace_decl_handler()
The xml_set_end_namespace_decl_handler() function is a PHP built-in function that sets a user-defined function as the handler for end namespace declarations in
The xml_set_end_namespace_decl_handler() function is a PHP built-in function that registers a user-defined callback to run whenever the SAX XML parser reaches the end of an element that declared one or more XML namespaces. It is the closing counterpart of xml_set_start_namespace_decl_handler(): the start handler fires when a namespace prefix comes into scope, and this end handler fires when that prefix goes back out of scope.
This function belongs to PHP's event-driven (SAX) xml extension, which reads a document top to bottom and calls your handlers as it encounters each construct, rather than building a tree in memory like SimpleXML. It is most useful when you want to track which namespaces are currently active while parsing — for example, to maintain a stack of in-scope prefixes or to clean up state once a namespaced section ends.
When to use it
A namespace declaration is an attribute such as xmlns:ns="http://example.com". Its scope is the element it appears on and all of that element's descendants. Use the end-namespace handler when you need to know the exact moment that scope closes — typically to pop a prefix off a stack you built in the start handler, mirroring how the parser itself manages scope.
Syntax
xml_set_end_namespace_decl_handler(XMLParser $parser, callable|false $handler): bool| Parameter | Description |
|---|---|
$parser | The XML parser, created by xml_parser_create_ns() (recommended) or xml_parser_create(). |
$handler | The callback to run on each end-of-namespace event, or false to remove a previously set handler. |
Return value: returns true on success and false on failure (for example, if $parser is not a valid parser).
Your handler receives two arguments:
function handler(XMLParser $parser, string $prefix): void$prefix is the namespace prefix whose scope is ending (an empty string "" for a default xmlns="..." declaration). Note that, unlike the start handler, the end handler does not receive the namespace URI — only the prefix.
Usage Examples
Example: Setting an end namespace declaration handler
This example sets up the handler and triggers it by parsing a small XML string. The handler is invoked during xml_parse(), as the parser closes the element that declared the namespace.
Parsing XML with an end namespace declaration handler
function handle_end_namespace_decl($parser, $prefix) {
echo "End of namespace prefix: $prefix\n";
}
$xml_parser = xml_parser_create_ns();
xml_set_end_namespace_decl_handler($xml_parser, "handle_end_namespace_decl");
$xml_data = '<?xml version="1.0"?><root xmlns:ns="http://example.com"><ns:child/></root>';
xml_parse($xml_parser, $xml_data, true);
xml_parser_free($xml_parser);The ns prefix is declared on <root>, so its scope ends when </root> is reached. When the handler fires, it prints the prefix going out of scope:
End of namespace prefix: nsHeads up: firing of the end namespace handler is sensitive to the underlying
libexpat/PHP build — on some PHP versions the start handler runs while the end handler does not. Always test against your target runtime, and never rely on the end handler alone to detect a namespace; pair it with the start handler (below) so your state stays consistent.
Example: Pairing start and end handlers to track scope
In real parsers the end handler is rarely used alone. Pairing it with the start handler lets you maintain a stack of the prefixes currently in scope. The start handler pushes each prefix as it comes into scope; the end handler pops it when the declaring element closes:
Tracking in-scope namespace prefixes with a stack
$activePrefixes = [];
function on_start_ns($parser, $prefix, $uri) {
global $activePrefixes;
$activePrefixes[] = $prefix;
echo "Enter '$prefix' -> $uri | active: " . implode(', ', $activePrefixes) . "\n";
}
function on_end_ns($parser, $prefix) {
global $activePrefixes;
array_pop($activePrefixes);
echo "Leave '$prefix' | active: " . implode(', ', $activePrefixes) . "\n";
}
$parser = xml_parser_create_ns();
xml_set_start_namespace_decl_handler($parser, "on_start_ns");
xml_set_end_namespace_decl_handler($parser, "on_end_ns");
$xml = '<?xml version="1.0"?>'
. '<a xmlns:x="urn:x"><b xmlns:y="urn:y"><c/></b></a>';
xml_parse($parser, $xml, true);
xml_parser_free($parser);The x prefix is declared on <a> and y on the inner <b>. As scopes open and close, the stack tracks which prefixes are currently active. The end events fire in last-in, first-out order — y (the inner one) leaves scope before x:
Enter 'x' -> urn:x | active: x
Enter 'y' -> urn:y | active: x, y
Leave 'y' | active: x
Leave 'x' | active: This start/pop pattern is the canonical use of the end handler: it keeps your view of in-scope namespaces aligned with the parser's own scope management.
Common gotchas
- Use
xml_parser_create_ns(). Namespace handlers only receive events when the parser is namespace-aware. A parser made with the plainxml_parser_create()will not split prefixes from URIs. - No URI in the end callback. If you need the URI when scope closes, capture it in the start handler (keyed by prefix) and look it up here.
- Set handlers before parsing. Register the handler before the first call to
xml_parse(); handlers set mid-parse will miss earlier events. - Free the parser. Call
xml_parser_free()when done to release resources, especially in long-running scripts.
Conclusion
xml_set_end_namespace_decl_handler() lets a SAX parser tell your code exactly when an XML namespace prefix goes out of scope. Combined with xml_set_start_namespace_decl_handler() and a namespace-aware parser from xml_parser_create_ns(), it gives you precise, low-memory control over namespace scope while streaming through large XML documents.