Skip to content

Commit e3f5ef5

Browse files
committed
Rewrite normalizeDocument
1 parent 51602ab commit e3f5ef5

4 files changed

Lines changed: 32 additions & 53 deletions

File tree

src/XML/DOMDocumentFactory.php

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
use function restore_error_handler;
1717
use function set_error_handler;
1818
use function sprintf;
19-
use function strpos;
2019

2120
/**
2221
* @package simplesamlphp/xml-common
@@ -168,53 +167,47 @@ public static function create(string $encoding = 'UTF-8'): Dom\XMLDocument
168167
* @return \Dom\XMLDocument The same document instance, potentially modified. If the document has no root element
169168
* or no namespace declarations to normalize, it is returned unchanged.
170169
*/
171-
public static function normalizeDocument(Dom\XMLDocument $doc): Dom\XMLDocument
170+
public static function normalizeDocument(Dom\Document $doc): Dom\Document
172171
{
173-
// Get the root element
174172
$root = $doc->documentElement;
175173
if ($root === null) {
176174
return $doc;
177175
}
178176

179177
$xpath = XPath::getXPath($doc);
178+
180179
$xmlnsAttributes = [];
181180

182-
// Collect namespace declarations needed for prefixed elements in the document
183-
foreach ($xpath->query('//*[namespace::*]') as $node) {
184-
if ($node instanceof Dom\Element) {
185-
$name = 'xmlns:' . $node->prefix;
186-
// Both prefix and namespaceURI NULL equals the default xmlns:xml namespace
187-
if ($node->prefix !== null && $node->namespaceURI !== null) {
188-
$xmlnsAttributes[$name] = $node->namespaceURI;
181+
// More reliable collection: look for attributes starting with xmlns on every element
182+
foreach ($xpath->query('//*') as $element) {
183+
if ($element instanceof Dom\Element) {
184+
foreach ($element->attributes as $attr) {
185+
if (str_starts_with($attr->nodeName, 'xmlns')) {
186+
$name = $attr->nodeName;
187+
$value = $attr->nodeValue;
188+
$xmlnsAttributes[$name] = $value;
189+
}
189190
}
190191
}
191192
}
192193

193-
// If no xmlns attributes found, return early with debug info
194-
if (empty($xmlnsAttributes)) {
195-
return $doc;
196-
}
197-
198-
// Remove xmlns attributes from all elements (proper XMLNS namespace removal)
199-
foreach ($xpath->query('//*[namespace::*]') as $node) {
200-
if (!$node instanceof Dom\Element) {
201-
continue;
202-
}
203-
204-
foreach ($node->attributes as $attr) {
205-
if ($attr->namespaceURI === C::NS_XMLNS) {
206-
$node->removeAttributeNS(C::NS_XMLNS, $attr->localName);
207-
continue;
194+
// Remove from all elements
195+
foreach ($xpath->query('//*') as $element) {
196+
if ($element instanceof Dom\Element) {
197+
$toRemove = [];
198+
foreach ($element->attributes as $attr) {
199+
if (str_starts_with($attr->nodeName, 'xmlns')) {
200+
$toRemove[] = $attr->nodeName;
201+
}
208202
}
209203

210-
if (strpos($attr->nodeName, 'xmlns') === 0 || $attr->nodeName === 'xmlns') {
211-
// Fallback for implementations that still expose xmlns attrs without namespaceURI
212-
$node->removeAttribute($attr->nodeName);
204+
foreach ($toRemove as $name) {
205+
$element->removeAttribute($name);
213206
}
214207
}
215208
}
216209

217-
// Add all collected xmlns attributes to the root element
210+
// Re-attach to root
218211
foreach ($xmlnsAttributes as $name => $value) {
219212
$root->setAttributeNS(C::NS_XMLNS, $name, $value);
220213
}

src/XML/SerializableElementTrait.php

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,18 @@ trait SerializableElementTrait
3131
public function __toString(): string
3232
{
3333
$xml = $this->toXML();
34-
35-
/** @var \Dom\XMLDocument $ownerDocument */
36-
$ownerDocument = $xml->ownerDocument;
37-
$xmlString = $ownerDocument->saveXml($ownerDocument->documentElement);
38-
39-
$doc = DOMDocumentFactory::fromString($xmlString);
40-
$doc->formatOutput = $this->formatOutput;
34+
$doc = $xml->ownerDocument;
4135

4236
if (static::getNormalization() === true) {
43-
/** @var \Dom\XMLDocument $normalized */
4437
$normalized = DOMDocumentFactory::normalizeDocument($doc);
45-
$normalized->normalize();
46-
return $normalized->saveXml($normalized->firstChild);
38+
$normalized->formatOutput = $this->formatOutput;
39+
return $normalized->saveXml($normalized->documentElement);
4740
}
4841

49-
$doc->normalize();
50-
return $doc->saveXml($doc->firstChild);
42+
// Non-normalized path
43+
$doc->formatOutput = $this->formatOutput;
44+
$doc->normalizeDocument();
45+
return $doc->saveXml($doc->documentElement);
5146
}
5247

5348

tests/resources/schemas/simplesamlphp.xsd

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<!DOCTYPE schema
3-
PUBLIC "-//W3C//DTD XMLSchema 200102//EN" "http://www.w3.org/2001/XMLSchema.dtd"
4-
[
5-
<!ATTLIST schema
6-
xmlns:ds CDATA #FIXED "urn:x-simplesamlphp:namespace">
7-
<!ENTITY ssp 'urn:x-simplesamlphp:namespace'>
8-
<!ENTITY % p ''>
9-
<!ENTITY % s ''>
10-
]>
112

123
<!-- Schema for SimpleSAMLphp dummy classes -->
134

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<ssp:ExtendableElement xmlns:ssp="urn:x-simplesamlphp:namespace" xmlns:dummy="urn:custom:dummy">
2-
<ssp:Chunk>some</ssp:Chunk>
3-
<dummy:Chunk>some</dummy:Chunk>
1+
<ssp:ExtendableElement xmlns:ssp="urn:x-simplesamlphp:namespace">
2+
<ssp:Chunk xmlns:ssp="urn:x-simplesamlphp:namespace">some</ssp:Chunk>
3+
<dummy:Chunk xmlns:dummy="urn:custom:dummy">some</dummy:Chunk>
44
</ssp:ExtendableElement>

0 commit comments

Comments
 (0)