From d53b9237c4649d178b6c9a8b56df31710002ddc7 Mon Sep 17 00:00:00 2001 From: "J. King" Date: Tue, 30 Mar 2021 12:01:26 -0400 Subject: [PATCH] Fix xmlns attributes properly --- lib/DOM/Document.php | 8 ++------ lib/DOM/Element.php | 20 +++++++++++++++----- lib/TreeBuilder.php | 10 ---------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/DOM/Document.php b/lib/DOM/Document.php index 40fcc04..18c000f 100644 --- a/lib/DOM/Document.php +++ b/lib/DOM/Document.php @@ -50,19 +50,15 @@ class Document extends \DOMDocument { public function createAttributeNS($namespaceURI, $qualifiedName) { try { - $out = @parent::createAttributeNS($namespaceURI, $qualifiedName); + return parent::createAttributeNS($namespaceURI, $qualifiedName); } catch (\DOMException $e) { // The element name is invalid for XML // Replace any offending characters with "UHHHHHH" where H are the // uppercase hexadecimal digits of the character's code point $this->mangledAttributes = true; $qualifiedName = $this->coerceName($qualifiedName); - $out = parent::createAttributeNS($namespaceURI, $qualifiedName); + return parent::createAttributeNS($namespaceURI, $qualifiedName); } - if ($out === false) { - throw new \DOMException("Document element must be inserted first"); - } - return $out; } public function createElement($name, $value = "") { diff --git a/lib/DOM/Element.php b/lib/DOM/Element.php index 679982d..8df8133 100644 --- a/lib/DOM/Element.php +++ b/lib/DOM/Element.php @@ -76,20 +76,30 @@ class Element extends \DOMElement { // set it. if ($qualifiedName === 'class' && $namespaceURI === null && $this->_classList !== null) { $this->_classList->value = $value; + } elseif ($namespaceURI === Parser::XMLNS_NAMESPACE) { + // NOTE: We create attribute nodes so that xmlns attributes + // don't get lost; otherwise they cannot be serialized + $a = @$this->ownerDocument->createAttributeNS($namespaceURI, $qualifiedName); + if ($a === false) { + // The document element does not exist yet, so we need + // to insert this element into the document + $this->ownerDocument->appendChild($this); + $a = $this->ownerDocument->createAttributeNS($namespaceURI, $qualifiedName); + $this->ownerDocument->removeChild($this); + } + $a->value = $this->escapeString($value, true); + $this->appendChild($a); } else { try { - // NOTE: We create attribute nodes so that xmlns attributes don't get lost - $a = $this->ownerDocument->createAttributeNS($namespaceURI, $qualifiedName); + parent::setAttributeNS($namespaceURI, $qualifiedName, $value); } catch (\DOMException $e) { // The attribute name is invalid for XML // Replace any offending characters with "UHHHHHH" where H are the // uppercase hexadecimal digits of the character's code point $this->ownerDocument->mangledAttributes = true; $qualifiedName = $this->coerceName($qualifiedName); - $a = $this->ownerDocument->createAttributeNS($namespaceURI, $qualifiedName); + parent::setAttributeNS($namespaceURI, $qualifiedName, $value); } - $a->value = $this->escapeString($value, true); - $this->appendChild($a); if ($qualifiedName === "id" && $namespaceURI === null) { $this->setIdAttribute($qualifiedName, true); } diff --git a/lib/TreeBuilder.php b/lib/TreeBuilder.php index 7060d79..ace1ae4 100644 --- a/lib/TreeBuilder.php +++ b/lib/TreeBuilder.php @@ -4150,12 +4150,6 @@ class TreeBuilder { # Let element be the result of creating an element given document, # localName, given namespace, null, and is. $element = $this->DOM->createElementNS($namespace, $token->name); - // DEVIATION: If there is no document (root) element yet, temporarily - // insert this element so that creating attributes for it does not - // fail due to a PHP DOM limitation - if (!$this->DOM->documentElement) { - $this->DOM->appendChild($element); - } # Append each attribute in the given token to element. foreach ($token->attributes as $attr) { # If element has an xmlns attribute in the XMLNS namespace whose value @@ -4174,10 +4168,6 @@ class TreeBuilder { $element->setAttributeNS($attr->namespace, $attr->name, $attr->value); } } - if ($this->DOM->documentElement && $this->DOM->documentElement->isSameNode($element)) { - // Pop off the document element if it was inserted above - $this->DOM->removeChild($element); - } # Return element. return $element; }