diff options
author | Asmir Mustafic <[email protected]> | 2014-07-08 10:40:06 +0200 |
---|---|---|
committer | Asmir Mustafic <[email protected]> | 2014-07-08 10:40:06 +0200 |
commit | 072da11763d0188e0dad12335c85f43fe6fcf9b2 (patch) | |
tree | 3fd78a6a15d775099e5561a169273c1a5953aa44 /src | |
parent | 7c176539423d7e1acd0e5191855bdee8f0ed1a37 (diff) |
Removed xmlns workaround, using a different method, that supports XMLNS
declarations
Diffstat (limited to 'src')
-rw-r--r-- | src/HTML5/Parser/DOMTreeBuilder.php | 34 | ||||
-rw-r--r-- | src/HTML5/Serializer/OutputRules.php | 68 |
2 files changed, 91 insertions, 11 deletions
diff --git a/src/HTML5/Parser/DOMTreeBuilder.php b/src/HTML5/Parser/DOMTreeBuilder.php index d2c8020..59504f5 100644 --- a/src/HTML5/Parser/DOMTreeBuilder.php +++ b/src/HTML5/Parser/DOMTreeBuilder.php @@ -301,10 +301,12 @@ class DOMTreeBuilder implements EventHandler ) + $this->nsStack[0]); $pushes ++; } + $needsWorkaround = false; if (isset($this->options["xmlNamespaces"]) && $this->options["xmlNamespaces"]) { // when xmlNamespaces is true a and we found a 'xmlns' or 'xmlns:*' attribute, we should add a new item to the $nsStack foreach ($attributes as $aName => $aVal) { if ($aName === 'xmlns') { + $needsWorkaround = $aVal; array_unshift($this->nsStack, array( '' => $aVal ) + $this->nsStack[0]); @@ -321,17 +323,29 @@ class DOMTreeBuilder implements EventHandler try { $prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : ''; - if (isset($this->nsStack[0][$prefix])) { - $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname); + + if ($needsWorkaround!==false) { + + $xml = "<$lname xmlns=\"$needsWorkaround\" ".(strlen($prefix) && isset($this->nsStack[0][$prefix])?("xmlns:$prefix=\"".$this->nsStack[0][$prefix]."\""):"")."/>"; + + $frag = new \DOMDocument('1.0', 'UTF-8'); + $frag->loadXML($xml); + + $ele = $this->doc->importNode($frag->documentElement, true); + } else { - $ele = $this->doc->createElement($lname); + if (isset($this->nsStack[0][$prefix])) { + $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname); + } else { + $ele = $this->doc->createElement($lname); + } } + } catch (\DOMException $e) { $this->parseError("Illegal tag name: <$lname>. Replaced with <invalid>."); $ele = $this->doc->createElement('invalid'); } - // when we add some namespacess, we have to track them. Later, when "endElement" is invoked, we have to remove them if ($pushes > 0) { // PHP tends to free the memory used by DOM, @@ -346,6 +360,10 @@ class DOMTreeBuilder implements EventHandler } foreach ($attributes as $aName => $aVal) { + // xmlns attributes can't be set + if ($aName === 'xmlns') { + continue; + } if ($this->insertMode == static::IM_IN_SVG) { $aName = Elements::normalizeSvgAttribute($aName); @@ -355,11 +373,11 @@ class DOMTreeBuilder implements EventHandler try { $prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false; - if ($prefix!==false && $prefix !== 'xmlns' && isset($this->nsStack[0][$prefix])) { + + if ($prefix==='xmlns') { + $ele->setAttributeNs(self::NAMESPACE_XMLNS, $aName, $aVal); + } elseif ($prefix!==false && isset($this->nsStack[0][$prefix])) { $ele->setAttributeNs($this->nsStack[0][$prefix], $aName, $aVal); - } elseif ($aName === 'xmlns') { - // setAttribute('xmlns', '..') is not possible, so we have to add a fake attribute - $ele->setAttribute("xmlns:x___xmlns__x", $aVal); } else { $ele->setAttribute($aName, $aVal); } diff --git a/src/HTML5/Serializer/OutputRules.php b/src/HTML5/Serializer/OutputRules.php index ff8341e..1ab5c76 100644 --- a/src/HTML5/Serializer/OutputRules.php +++ b/src/HTML5/Serializer/OutputRules.php @@ -15,6 +15,32 @@ use Masterminds\HTML5\Elements; */ class OutputRules implements \Masterminds\HTML5\Serializer\RulesInterface { + /** + * Defined in http://www.w3.org/TR/html51/infrastructure.html#html-namespace-0 + */ + const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml'; + + const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML'; + + const NAMESPACE_SVG = 'http://www.w3.org/2000/svg'; + + const NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink'; + + const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace'; + + const NAMESPACE_XMLNS = 'http://www.w3.org/2000/xmlns/'; + + /** + * Holds the HTML5 element names that causes a namespace switch + * + * @var array + */ + protected $nsRoots = array( + 'html' => self::NAMESPACE_HTML, + 'svg' => self::NAMESPACE_SVG, + 'math' => self::NAMESPACE_MATHML + ); + const IM_IN_HTML = 1; @@ -28,6 +54,8 @@ class OutputRules implements \Masterminds\HTML5\Serializer\RulesInterface protected $encode = false; + protected $xpath; + protected $out; protected $outputMode; @@ -148,6 +176,39 @@ class OutputRules implements \Masterminds\HTML5\Serializer\RulesInterface ->wr($ele->data) ->wr('?>'); } + /** + * Write the namespace attributes + * + * + * @param \DOMNode $ele + * The element being written. + */ + protected function namespaceAttrs($ele) + { + $this->xpath = new \DOMXPath($ele->ownerDocument); + $declared = array(); + + $declared["xmlns:xml"] = "http://www.w3.org/XML/1998/namespace"; + + if ($ele->parentNode) { + foreach( $this->xpath->query('namespace::*', $ele->parentNode ) as $nsNode ) { + $declared[$nsNode->nodeName] = $nsNode->nodeValue; + } + } + foreach( $this->xpath->query('namespace::*', $ele ) as $nsNode ) { + if (isset($declared[$nsNode->nodeName]) && $declared[$nsNode->nodeName] === $nsNode->nodeValue) { + unset($declared[$nsNode->nodeName]); + } else { + $declared[$nsNode->nodeName] = $nsNode->nodeValue; + } + } + + foreach( $declared as $aName => $aValue ) { + if (!in_array($aValue, $this->nsRoots)) { + $this->wr(' ')->wr($aName)->wr('="')->wr($aValue)->wr('"'); + } + } + } /** * Write the opening tag. @@ -161,7 +222,11 @@ class OutputRules implements \Masterminds\HTML5\Serializer\RulesInterface protected function openTag($ele) { $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName); + + $this->attrs($ele); + $this->namespaceAttrs($ele); + if ($this->outputMode == static::IM_IN_HTML) { $this->wr('>'); @@ -196,9 +261,6 @@ class OutputRules implements \Masterminds\HTML5\Serializer\RulesInterface // prefix. It seems that DOM does this for us already, but there // may be exceptions. $name = $node->name; - if ($name == "xmlns:x___xmlns__x") { - $name = "xmlns"; - } // Special handling for attributes in SVG and MathML. // Using if/elseif instead of switch because it's faster in PHP. |