Optimize nodes
This commit is contained in:
parent
b0c06323f9
commit
3a431fe134
10 changed files with 237 additions and 182 deletions
12
lib/DOM/AbstractDocument.php
Normal file
12
lib/DOM/AbstractDocument.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 , Dustin Wilson, J. King et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace MensBeam\HTML;
|
||||
|
||||
// Exists so Document can extend a method gained from the ContainerNode trait.
|
||||
abstract class AbstractDocument extends \DOMDocument {
|
||||
use ContainerNode, EscapeString, Serialize, Walk;
|
||||
}
|
|
@ -7,7 +7,7 @@ declare(strict_types=1);
|
|||
namespace MensBeam\HTML;
|
||||
|
||||
class Comment extends \DOMComment {
|
||||
use Moonwalk, Node;
|
||||
use LeafNode, Moonwalk;
|
||||
|
||||
public function __toString(): string {
|
||||
# Append the literal string "<!--" (U+003C LESS-THAN SIGN, U+0021 EXCLAMATION
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
declare(strict_types=1);
|
||||
namespace MensBeam\HTML;
|
||||
|
||||
class Document extends \DOMDocument {
|
||||
use EscapeString, Node, Serialize, Walk;
|
||||
|
||||
class Document extends AbstractDocument {
|
||||
// Quirks mode constants
|
||||
public const NO_QUIRKS_MODE = 0;
|
||||
public const QUIRKS_MODE = 1;
|
||||
|
@ -234,4 +232,90 @@ class Document extends \DOMDocument {
|
|||
public function __toString() {
|
||||
return $this->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 "<?" (U+003C LESS-THAN SIGN, U+003F QUESTION MARK),
|
||||
|
|
|
@ -7,7 +7,7 @@ declare(strict_types=1);
|
|||
namespace MensBeam\HTML;
|
||||
|
||||
class Text extends \DOMText {
|
||||
use EscapeString, Moonwalk, Node;
|
||||
use EscapeString, LeafNode, Moonwalk;
|
||||
|
||||
function __toString(): string {
|
||||
# If the parent of current node is a style, script, xmp, iframe, noembed,
|
||||
|
|
103
lib/DOM/traits/ContainerNode.php
Normal file
103
lib/DOM/traits/ContainerNode.php
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 , Dustin Wilson, J. King et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace MensBeam\HTML;
|
||||
|
||||
// Node in the DOM spec is dirty. Many nodes which inherit from it inherit
|
||||
// methods it cannot use which all check for this and throw exceptions. This is
|
||||
// for nodes which do have node insertion methods.
|
||||
trait ContainerNode {
|
||||
use Node;
|
||||
|
||||
public function appendChild($node) {
|
||||
$this->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.
|
||||
}
|
||||
}
|
30
lib/DOM/traits/LeafNode.php
Normal file
30
lib/DOM/traits/LeafNode.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/** @license MIT
|
||||
* Copyright 2017 , Dustin Wilson, J. King et al.
|
||||
* See LICENSE and AUTHORS files for details */
|
||||
|
||||
declare(strict_types=1);
|
||||
namespace MensBeam\HTML;
|
||||
|
||||
// Node in the DOM spec is dirty. Many nodes which inherit from it inherit
|
||||
// methods it cannot use which all check for this and throw exceptions. This is
|
||||
// for nodes which do NOT have node insertion methods.
|
||||
trait LeafNode {
|
||||
use Node;
|
||||
|
||||
public function appendChild($node) {
|
||||
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
|
||||
}
|
||||
|
||||
public function insertBefore($node, $child = null) {
|
||||
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
|
||||
}
|
||||
|
||||
public function removeChild($child) {
|
||||
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
|
||||
}
|
||||
|
||||
public function replaceChild($node, $child) {
|
||||
throw new DOMException(DOMException::HIERARCHY_REQUEST_ERROR);
|
||||
}
|
||||
}
|
|
@ -9,18 +9,6 @@ namespace MensBeam\HTML;
|
|||
// Extensions to PHP's DOM cannot inherit from an extended Node parent, so a
|
||||
// trait is the next best thing...
|
||||
trait Node {
|
||||
public function appendChild($node) {
|
||||
$this->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue