From 3a431fe13426a9be1a2ec996804d3205e78c2191 Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Wed, 14 Apr 2021 12:30:29 -0500 Subject: [PATCH] Optimize nodes --- lib/DOM/AbstractDocument.php | 12 +++ lib/DOM/Comment.php | 2 +- lib/DOM/Document.php | 90 +++++++++++++++- lib/DOM/DocumentFragment.php | 2 +- lib/DOM/Element.php | 2 +- lib/DOM/ProcessingInstruction.php | 2 +- lib/DOM/Text.php | 2 +- lib/DOM/traits/ContainerNode.php | 103 ++++++++++++++++++ lib/DOM/traits/LeafNode.php | 30 ++++++ lib/DOM/traits/Node.php | 174 ------------------------------ 10 files changed, 237 insertions(+), 182 deletions(-) create mode 100644 lib/DOM/AbstractDocument.php create mode 100644 lib/DOM/traits/ContainerNode.php create mode 100644 lib/DOM/traits/LeafNode.php diff --git a/lib/DOM/AbstractDocument.php b/lib/DOM/AbstractDocument.php new file mode 100644 index 0000000..4c0e658 --- /dev/null +++ b/lib/DOM/AbstractDocument.php @@ -0,0 +1,12 @@ +serialize(); } + + protected function preInsertionValidity(\DOMNode $node, ?\DOMNode $child = null) { + parent::preInsertionValidity($node, $child); + + # 6. If parent is a document, and any of the statements below, switched on node, + # are true, then throw a "HierarchyRequestError" DOMException. + # + # DocumentFragment node + # If node has more than one element child or has a Text node child. + # Otherwise, if node has one element child and either parent has an element + # child, child is a doctype, or child is non-null and a doctype is following + # child. + if ($node instanceof DocumentFragment) { + if ($node->childNodes->length > 1 || $node->firstChild instanceof Text) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } else { + if ($node->firstChild instanceof \DOMDocumentType) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + + foreach ($this->childNodes as $c) { + if ($c instanceof Element) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + + if ($child !== null) { + $n = $child; + while ($n = $n->nextSibling) { + if ($n instanceof \DOMDocumentType) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + } + } + } + # element + # parent has an element child, child is a doctype, or child is non-null and a + # doctype is following child. + elseif ($node instanceof Element) { + if ($child instanceof \DOMDocumentType) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + + if ($child !== null) { + $n = $child; + while ($n = $n->nextSibling) { + if ($n instanceof \DOMDocumentType) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + } + + foreach ($this->childNodes as $c) { + if ($c instanceof Element) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + } + + # doctype + # parent has a doctype child, child is non-null and an element is preceding + # child, or child is null and parent has an element child. + elseif ($node instanceof \DOMDocumentType) { + foreach ($this->childNodes as $c) { + if ($c instanceof \DOMDocumentType) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + + if ($child !== null) { + $n = $child; + while ($n = $n->prevSibling) { + if ($n instanceof Element) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + } else { + foreach ($this->childNodes as $c) { + if ($c instanceof Element) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + } + } + } + } } diff --git a/lib/DOM/DocumentFragment.php b/lib/DOM/DocumentFragment.php index f6d6219..18fb3b4 100644 --- a/lib/DOM/DocumentFragment.php +++ b/lib/DOM/DocumentFragment.php @@ -7,7 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML; class DocumentFragment extends \DOMDocumentFragment { - use Moonwalk, Node, Serialize, Walk; + use ContainerNode, Moonwalk, Serialize, Walk; public function __toString() { return $this->serialize(); diff --git a/lib/DOM/Element.php b/lib/DOM/Element.php index 684c7ee..f746961 100644 --- a/lib/DOM/Element.php +++ b/lib/DOM/Element.php @@ -7,7 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML; class Element extends \DOMElement { - use EscapeString, Moonwalk, Node, Serialize, Walk; + use ContainerNode, EscapeString, Moonwalk, Serialize, Walk; protected $_classList; diff --git a/lib/DOM/ProcessingInstruction.php b/lib/DOM/ProcessingInstruction.php index a7a9a9b..5764599 100644 --- a/lib/DOM/ProcessingInstruction.php +++ b/lib/DOM/ProcessingInstruction.php @@ -7,7 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML; class ProcessingInstruction extends \DOMProcessingInstruction { - use Moonwalk, Node; + use LeafNode, Moonwalk; public function __toString(): string { # Append the literal string "preInsertionValidity($node); + + $result = parent::appendChild($node); + if ($result !== false && $result instanceof TemplateElement) { + if ($result instanceof TemplateElement) { + ElementMap::set($result); + } + } + return $result; + } + + public function insertBefore($node, $child = null) { + $this->preInsertionValidity($node, $child); + + $result = parent::insertBefore($node, $child); + if ($result !== false) { + if ($result instanceof TemplateElement) { + ElementMap::set($result); + } + if ($child instanceof TemplateElement) { + ElementMap::delete($child); + } + } + return $result; + } + + public function removeChild($child) { + $result = parent::removeChild($child); + if ($result !== false && $result instanceof TemplateElement) { + ElementMap::delete($child); + } + return $result; + } + + public function replaceChild($node, $child) { + $result = parent::replaceChild($node, $child); + if ($result !== false) { + if ($result instanceof TemplateElement) { + ElementMap::set($child); + } + if ($child instanceof TemplateElement) { + ElementMap::delete($child); + } + } + return $result; + } + + protected function preInsertionValidity(\DOMNode $node, ?\DOMNode $child = null) { + // "parent" in the spec comments below is $this + + # 1. If parent is not a Document, DocumentFragment, or Element node, then throw + # a "HierarchyRequestError" DOMException. + // Not necessary because they've been disabled and return hierarchy request + // errors in "leaf nodes". + + # 2. If node is a host-including inclusive ancestor of parent, then throw a + # "HierarchyRequestError" DOMException. + # + # An object A is a host-including inclusive ancestor of an object B, if either + # A is an inclusive ancestor of B, or if B’s root has a non-null host and A is a + # host-including inclusive ancestor of B’s root’s host. + // DEVIATION: The baseline for this library is PHP 7.1, and without + // WeakReferences we cannot add a host property to DocumentFragment to check + // against. + // This is handled just fine by PHP's DOM. + + # 3. If child is non-null and its parent is not parent, then throw a + # "NotFoundError" DOMException. + // This is handled just fine by PHP's DOM. + + # 4. If node is not a DocumentFragment, DocumentType, Element, Text, + # ProcessingInstruction, or Comment node, then throw a "HierarchyRequestError" + # DOMException. + if (!$node instanceof DocumentFragment && !$node instanceof \DOMDocumentType && !$node instanceof Element && !$node instanceof Text && !$node instanceof ProcessingInstruction && !$node instanceof Comment) { + throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); + } + + # 5. If either node is a Text node and parent is a document, or node is a + # doctype and parent is not a document, then throw a "HierarchyRequestError" + # DOMException. + // Not necessary because they've been disabled and return hierarchy request + // errors in "leaf nodes". + + # 6. If parent is a document, and any of the statements below, switched on node, + # are true, then throw a "HierarchyRequestError" DOMException. + // Handled by the Document class. + } +} diff --git a/lib/DOM/traits/LeafNode.php b/lib/DOM/traits/LeafNode.php new file mode 100644 index 0000000..03ad37e --- /dev/null +++ b/lib/DOM/traits/LeafNode.php @@ -0,0 +1,30 @@ +preInsertionValidity($node); - - $result = parent::appendChild($node); - if ($result !== false && $result instanceof TemplateElement) { - if ($result instanceof TemplateElement) { - ElementMap::set($result); - } - } - return $result; - } - // Disable C14N public function C14N($exclusive = null, $with_comments = null, ?array $xpath = null, ?array $ns_prefixes = null): bool { return false; @@ -30,166 +18,4 @@ trait Node { public function C14NFile($uri, $exclusive = null, $with_comments = null, ?array $xpath = null, ?array $ns_prefixes = null): bool { return false; } - - public function insertBefore($node, $child = null) { - $this->preInsertionValidity($node, $child); - - $result = parent::insertBefore($node, $child); - if ($result !== false) { - if ($result instanceof TemplateElement) { - ElementMap::set($result); - } - if ($child instanceof TemplateElement) { - ElementMap::delete($child); - } - } - return $result; - } - - public function removeChild($child) { - $result = parent::removeChild($child); - if ($result !== false && $result instanceof TemplateElement) { - ElementMap::delete($child); - } - return $result; - } - - public function replaceChild($node, $child) { - $result = parent::replaceChild($node, $child); - if ($result !== false) { - if ($result instanceof TemplateElement) { - ElementMap::set($child); - } - if ($child instanceof TemplateElement) { - ElementMap::delete($child); - } - } - return $result; - } - - protected function preInsertionValidity(\DOMNode $node, ?\DOMNode $child = null) { - // "parent" in the spec comments below is $this - - # 1. If parent is not a Document, DocumentFragment, or Element node, then throw - # a "HierarchyRequestError" DOMException. - if (!$this instanceof Document && !$this instanceof DocumentFragment && !$this instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - # 2. If node is a host-including inclusive ancestor of parent, then throw a - # "HierarchyRequestError" DOMException. - # - # An object A is a host-including inclusive ancestor of an object B, if either - # A is an inclusive ancestor of B, or if B’s root has a non-null host and A is a - # host-including inclusive ancestor of B’s root’s host. - // DEVIATION: The baseline for this library is PHP 7.1, and without - // WeakReferences we cannot add a host property to DocumentFragment to check - // against. - if ($node instanceof Element && $node->isAncestorOf($this)) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - # 3. If child is non-null and its parent is not parent, then throw a - # "NotFoundError" DOMException. - if ($child !== null && !$child->parentNode->isSameNode($this)) { - throw new DOMException(DOMException::NOT_FOUND); - } - - # 4. If node is not a DocumentFragment, DocumentType, Element, Text, - # ProcessingInstruction, or Comment node, then throw a "HierarchyRequestError" - # DOMException. - if (!$node instanceof DocumentFragment && !$node instanceof \DOMDocumentType && !$node instanceof Element && !$node instanceof Text && !$node instanceof ProcessingInstruction && !$node instanceof Comment) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - # 5. If either node is a Text node and parent is a document, or node is a - # doctype and parent is not a document, then throw a "HierarchyRequestError" - # DOMException. - if (($node instanceof Text && $this instanceof Document) || ($node instanceof \DOMDocumentType && !$this instanceof Document)) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - # 6. If parent is a document, and any of the statements below, switched on node, - # are true, then throw a "HierarchyRequestError" DOMException. - if ($this instanceof Document) { - # DocumentFragment node - # If node has more than one element child or has a Text node child. - # Otherwise, if node has one element child and either parent has an element - # child, child is a doctype, or child is non-null and a doctype is following - # child. - if ($node instanceof DocumentFragment) { - if ($node->childNodes->length > 1 || $node->firstChild instanceof Text) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } else { - if ($node->firstChild instanceof \DOMDocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - foreach ($this->childNodes as $c) { - if ($c instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - - if ($child !== null) { - $n = $child; - while ($n = $n->nextSibling) { - if ($n instanceof \DOMDocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - } - } - } - # element - # parent has an element child, child is a doctype, or child is non-null and a - # doctype is following child. - elseif ($node instanceof Element) { - if ($child instanceof \DOMDocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - - if ($child !== null) { - $n = $child; - while ($n = $n->nextSibling) { - if ($n instanceof \DOMDocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - } - - foreach ($this->childNodes as $c) { - if ($c instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - } - - # doctype - # parent has a doctype child, child is non-null and an element is preceding - # child, or child is null and parent has an element child. - elseif ($node instanceof \DOMDocumentType) { - foreach ($this->childNodes as $c) { - if ($c instanceof \DOMDocumentType) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - - if ($child !== null) { - $n = $child; - while ($n = $n->prevSibling) { - if ($n instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - } else { - foreach ($this->childNodes as $c) { - if ($c instanceof Element) { - throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR); - } - } - } - } - } - } }