XPath behaviour with conflicting XML namespace prefixes

Viewed 103

Consider the following XML:

<?xml version="1.0" encoding="utf-8"?>
<movies xmlns:en="http://english-language.com/">
    <en:movie>
        <title>The Godfather</title>
    </en:movie>
    <en:movie>
        <title>Saving Private Ryan</title>
    </en:movie>
    <something-something xmlns:en="http://english.com/">
        <en:movie>
            <title>The Fellowship of the Ring</title>
        </en:movie>
    </something-something>
</movies>

This code below will match The Godfather and Saving Private Ryan. But why? I expected it to fail. It seems that the query ignores the namespace name (URI) in favor of the namespace prefix

$xpath      = new DOMXpath($dom);
$xpath->registerNamespace('en', 'http://complete-nonsense');
$elements   = $xpath->query("//en:movie");

I expected the code below to match The Fellowship of the Ring, but again it matches The Godfather and Saving Private Ryan.

$xpath      = new DOMXpath($dom);
$xpath->registerNamespace('en', 'http://english.com/');
$elements   = $xpath->query("//en:movie");

Only now the does the URI seem to make any difference, now it matches the The Fellowship of the Ring.

$xpath      = new DOMXpath($dom);
$xpath->registerNamespace('english', 'http://english.com/');
$elements   = $xpath->query("//english:movie");

Why is that? Is it something wrong with PHP's implementation or what?

1 Answers

DOMXpath::evaluate()/DOMXpath::query() register the namespace definitions of the current context node on top of the manually registered ones. Basically the document will override the namespace for a prefix. The third argument (since PHP >= 5.3.3) can disable the automatic registration:

$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('en', 'http://english.com/');

// the xmlns:en from the document element overrides the registration
var_dump($xpath->evaluate('normalize-space(//en:movie)'));
// automatic registration disabled - works correctly
var_dump($xpath->evaluate('normalize-space(//en:movie)', NULL, FALSE));

Output:

string(13) "The Godfather"
string(26) "The Fellowship of the Ring"
Related