Skip to content

Dom\XMLDocument::C14N() drops namespace declarations on DOM-built documents #21544

@veewee

Description

@veewee

Description

I ran into a C14N issue with Dom\XMLDocument while upgrading php-soap/xml to veewee/xml v4.

When a document is built via the DOM API (createElementNS + appendChild), non-exclusive C14N() strips all namespace declarations while keeping the prefixed element names, producing invalid XML.

Reproducer: https://3v4l.org/PGDSl#v8.5.3

<?php
echo "PHP " . PHP_VERSION . PHP_EOL . PHP_EOL;

// 1. DOM-built document: C14N drops namespace declarations
$doc = Dom\XMLDocument::createEmpty();
$root = $doc->createElementNS("urn:envelope", "env:Root");
$doc->appendChild($root);
$child = $doc->createElementNS("urn:child", "x:Child");
$root->appendChild($child);

echo "DOM-built Dom\XMLDocument" . PHP_EOL;
echo "  saveXML: " . trim($doc->saveXML()) . PHP_EOL;
echo "  C14N:    " . $doc->C14N() . PHP_EOL;
// Expected: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>
// Actual:   <env:Root><x:Child></x:Child></env:Root>
//           ^ namespace declarations are missing, this is not valid XML

echo PHP_EOL;

// 2. Same structure parsed from string: C14N works correctly
$doc2 = Dom\XMLDocument::createFromString(
  '<env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"/></env:Root>'
);
echo "Parsed Dom\XMLDocument" . PHP_EOL;
echo "  C14N:    " . $doc2->C14N() . PHP_EOL;
// Correct: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

echo PHP_EOL;

// 3. Legacy DOMDocument DOM-built: C14N works correctly
$doc3 = new DOMDocument();
$root3 = $doc3->createElementNS("urn:envelope", "env:Root");
$doc3->appendChild($root3);
$child3 = $doc3->createElementNS("urn:child", "x:Child");
$root3->appendChild($child3);
echo "DOM-built DOMDocument (legacy)" . PHP_EOL;
echo "  C14N:    " . $doc3->C14N() . PHP_EOL;
// Correct: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

echo PHP_EOL;

// 4. Exclusive C14N on DOM-built document: works correctly
echo "DOM-built Dom\XMLDocument (exclusive C14N)" . PHP_EOL;
echo "  C14N:    " . $doc->C14N(exclusive: true) . PHP_EOL;
// Correct: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

Output:

DOM-built Dom\XMLDocument
  saveXML: <?xml version="1.0" encoding="UTF-8"?>
<env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"/></env:Root>
  C14N:    <env:Root><x:Child></x:Child></env:Root>

Parsed Dom\XMLDocument
  C14N:    <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

DOM-built DOMDocument (legacy)
  C14N:    <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

DOM-built Dom\XMLDocument (exclusive C14N)
  C14N:    <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>

As you can see, the first C14N output is invalid XML.

The key observations:

  • DOM-built Dom\XMLDocument: C14N() outputs env:Root<x:Child></x:Child></env:Root> (missing xmlns:env and xmlns:x)
  • Same structure parsed via createFromString: works correctly
  • Legacy DOMDocument DOM-built: works correctly
  • Exclusive C14N(exclusive: true) on DOM-built: also works correctly

So the issue is specific to non-exclusive C14N on programmatically built Dom\XMLDocument documents.

Related:

The issue persists on 8.4.19 and 8.5.3.

Cheers,
Toon

/cc @ndossche

PHP Version

Any 8.4 and 8.5

Operating System

any

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions