From 41129ddc07b7173ace0023576fb623fa823fea78 Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Mon, 25 Oct 2021 15:48:32 -0500 Subject: [PATCH] Upgraded dependencies, added lots of tests for Node --- composer.lock | 54 ++--- lib/Comment.php | 4 +- lib/DOMException.php | 2 +- lib/DOMImplementation.php | 5 +- lib/Document.php | 172 ++++++++++++++- lib/DocumentFragment.php | 5 + lib/DocumentOrElement.php | 71 ++++++ lib/DocumentType.php | 16 +- lib/Element.php | 13 +- lib/InnerNode/Document.php | 7 + lib/InnerNode/NodeMap.php | 11 +- lib/Node.php | 29 ++- lib/ProcessingInstruction.php | 9 +- lib/Text.php | 5 + tests/cases/TestNode.php | 368 ++++++++++++++++++++++++++++++- vendor-bin/phpunit/composer.lock | 1 - 16 files changed, 710 insertions(+), 62 deletions(-) create mode 100644 lib/DocumentOrElement.php diff --git a/composer.lock b/composer.lock index f3577d4..5f44bc0 100644 --- a/composer.lock +++ b/composer.lock @@ -12,7 +12,7 @@ "source": { "type": "git", "url": "mensbeam-gitea:MensBeam/Framework.git", - "reference": "07af6f6a0c8a911d9e08870bdd54946757c0e958" + "reference": "bf7515f4c5d02ce9919a92a27812af7587a4700e" }, "require": { "php": ">=7.4" @@ -55,20 +55,20 @@ } ], "description": "Common classes and traits used in many Mensbeam projects", - "time": "2021-10-21T04:01:22+00:00" + "time": "2021-10-21T14:20:02+00:00" }, { "name": "mensbeam/html-parser", - "version": "1.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://code.mensbeam.com/MensBeam/HTML-Parser", - "reference": "c06caaabab7350823b10246e49594a41cf50401d" + "reference": "3fc966226b7d45ab51a6c20cb8cafeedcf54ccec" }, "require": { "ext-dom": "*", - "mensbeam/intl": ">=0.9.0", - "mensbeam/mimesniff": "^0.2.0", + "mensbeam/intl": ">=0.9.1", + "mensbeam/mimesniff": ">=0.2.0", "php": ">=7.1" }, "require-dev": { @@ -107,7 +107,7 @@ "homepage": "https://jkingweb.ca/" } ], - "description": "Parses modern HTML text into a PHP DOMDocument", + "description": "Parser and serializer for modern HTML documents", "keywords": [ "HTML5", "WHATWG", @@ -116,20 +116,20 @@ "parser", "parsing" ], - "time": "2021-10-12T00:23:16+00:00" + "time": "2021-10-24T17:24:48+00:00" }, { "name": "mensbeam/intl", - "version": "0.9.0", + "version": "0.9.1", "source": { "type": "git", "url": "https://github.com/mensbeam/intl.git", - "reference": "de037b182ce99aaa90ebc09b0ee0457ddf1d07bc" + "reference": "07d26e3f45c3a3167eb6389572419d3bda7ff5e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mensbeam/intl/zipball/de037b182ce99aaa90ebc09b0ee0457ddf1d07bc", - "reference": "de037b182ce99aaa90ebc09b0ee0457ddf1d07bc", + "url": "https://api.github.com/repos/mensbeam/intl/zipball/07d26e3f45c3a3167eb6389572419d3bda7ff5e1", + "reference": "07d26e3f45c3a3167eb6389572419d3bda7ff5e1", "shasum": "" }, "require": { @@ -169,9 +169,9 @@ ], "support": { "issues": "https://github.com/mensbeam/intl/issues", - "source": "https://github.com/mensbeam/intl/tree/0.9.0" + "source": "https://github.com/mensbeam/intl/tree/0.9.1" }, - "time": "2021-03-25T19:08:04+00:00" + "time": "2021-10-24T14:37:46+00:00" }, { "name": "mensbeam/mimesniff", @@ -678,16 +678,16 @@ }, { "name": "guzzlehttp/promises", - "version": "1.5.0", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "136a635e2b4a49b9d79e9c8fee267ffb257fdba0" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/136a635e2b4a49b9d79e9c8fee267ffb257fdba0", - "reference": "136a635e2b4a49b9d79e9c8fee267ffb257fdba0", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { @@ -742,7 +742,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.5.0" + "source": "https://github.com/guzzle/promises/tree/1.5.1" }, "funding": [ { @@ -758,7 +758,7 @@ "type": "tidelift" } ], - "time": "2021-10-07T13:05:22+00:00" + "time": "2021-10-22T20:56:57+00:00" }, { "name": "guzzlehttp/psr7", @@ -1292,21 +1292,20 @@ }, { "name": "scrivo/highlight.php", - "version": "v9.18.1.7", + "version": "v9.18.1.8", "source": { "type": "git", "url": "https://github.com/scrivo/highlight.php.git", - "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91" + "reference": "6d5049cd2578e19a06adbb6ac77879089be1e3f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/05996fcc61e97978d76ca7d1ac14b65e7cd26f91", - "reference": "05996fcc61e97978d76ca7d1ac14b65e7cd26f91", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/6d5049cd2578e19a06adbb6ac77879089be1e3f9", + "reference": "6d5049cd2578e19a06adbb6ac77879089be1e3f9", "shasum": "" }, "require": { "ext-json": "*", - "ext-mbstring": "*", "php": ">=5.4" }, "require-dev": { @@ -1315,6 +1314,9 @@ "symfony/finder": "^2.8|^3.4", "symfony/var-dumper": "^2.8|^3.4" }, + "suggest": { + "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords" + }, "type": "library", "autoload": { "psr-0": { @@ -1364,7 +1366,7 @@ "type": "github" } ], - "time": "2021-07-09T00:30:39+00:00" + "time": "2021-10-24T00:28:14+00:00" }, { "name": "symfony/console", diff --git a/lib/Comment.php b/lib/Comment.php index f84b7af..98ca67f 100644 --- a/lib/Comment.php +++ b/lib/Comment.php @@ -10,7 +10,7 @@ namespace MensBeam\HTML\DOM; class Comment extends CharacterData { - public function __construct(string $data = '') { - $this->innerNode = new \DOMComment($data); + protected function __construct(\DOMComment $comment) { + parent::__construct($comment); } } \ No newline at end of file diff --git a/lib/DOMException.php b/lib/DOMException.php index 034d070..59b6300 100644 --- a/lib/DOMException.php +++ b/lib/DOMException.php @@ -32,7 +32,7 @@ class DOMException extends Exception { 5 => 'Invalid character', 7 => 'Modification not allowed here', 8 => 'Not found error', - 9 => 'Feature is not supported because %s', + 9 => 'Feature is not supported', 12 => 'Syntax error', 13 => 'Invalid modification error', 14 => 'Namespace error', diff --git a/lib/DOMImplementation.php b/lib/DOMImplementation.php index 32a7470..937f9f3 100644 --- a/lib/DOMImplementation.php +++ b/lib/DOMImplementation.php @@ -91,8 +91,9 @@ class DOMImplementation { # the associated document of this. $innerDocument = Reflection::getProtectedProperty($this->document->get(), 'innerNode'); // PHP's DOM won't accept an empty string as the qualifiedName, so use a space - // instead; this will be worked around in DocumentType. - return $innerDocument->getWrapperNode($innerDocument->implementation->createDocumentType(($qualifiedName !== '') ? $qualifiedName : ' ', $publicId, $systemId)); + // instead which won't be encountered elsewhere because it violates the QName + // production; this will be worked around in DocumentType. + return $innerDocument->getWrapperNode($innerDocument->implementation->createDocumentType($qualifiedName, $publicId, $systemId)); } public function createHTMLDocument(string $title = ''): Document { diff --git a/lib/Document.php b/lib/Document.php index 0e1a920..a28f51e 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -15,10 +15,12 @@ use MensBeam\HTML\Parser; class Document extends Node { - use ParentNode; + use DocumentOrElement, ParentNode; + protected string $_compatMode = 'CSS1Compat'; protected string $_contentType = 'text/html'; protected DOMImplementation $_implementation; + protected string $_URL = ''; protected function __get_body(): Element { if ($this->documentElement === null || !$this->documentElement->hasChildNodes()) { @@ -34,6 +36,10 @@ class Document extends Node { }, true)->current(); } + protected function __get_compatMode(): string { + return $this->_compatMode; + } + protected function __get_contentType(): string { return $this->_contentType; } @@ -42,10 +48,18 @@ class Document extends Node { return $this->innerNode->getWrapperNode($this->innerNode->documentElement); } + protected function __get_documentURI(): string { + return $this->_URL; + } + protected function __get_implementation(): DOMImplementation { return $this->_implementation; } + protected function __get_URL(): string { + return $this->_URL; + } + public function __construct() { parent::__construct(new InnerDocument($this)); @@ -53,11 +67,111 @@ class Document extends Node { } - public function createCDATASection(): CDATASection { + public function createAttribute(string $localName): Attr { + # The createAttribute(localName) method steps are: + # + # 1. If localName does not match the Name production in XML, then throw an + # "InvalidCharacterError" DOMException. + if (preg_match(InnerDocument::NAME_PRODUCTION_REGEX, $localName) !== 1) { + throw new DOMException(DOMException::INVALID_CHARACTER); + } + + # 2. If this is an HTML document, then set localName to localName in ASCII + # lowercase. + if (!$this instanceof XMLDocument) { + $localName = strtolower($localName); + } + + // Before we do the next step we need to work around a PHP DOM bug. PHP DOM + // cannot create attribute nodes if there's no document element. So, create the + // attribute node in a separate document which does have a document element and + // then import + $target = $this->innerNode; + $documentElement = $this->documentElement; + if ($documentElement === null) { + $target = new \DOMDocument(); + $target->appendChild($target->createElement('html')); + } + + # 3. Return a new attribute whose local name is localName and node document is + # this. + // We need to do a couple more things here. PHP's XML-based DOM doesn't allow + // some characters. We have to coerce them sometimes. + try { + $attr = $target->createAttributeNS(null, $localName); + } 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 + $attr = $target->createAttributeNS(null, $this->coerceName($localName)); + } + + if ($documentElement === null) { + return $this->importNode($attr); + } + + return $this->innerNode->getWrapperNode($attr); + } + + public function createAttributeNS(string $namespace, string $qualifiedName): Attr { + # The createAttributeNS(namespace, qualifiedName) method steps are: + # + # 1. Let namespace, prefix, and localName be the result of passing namespace and + # qualifiedName to validate and extract. + [ 'namespace' => $namespace, 'prefix' => $prefix, 'localName' => $localName ] = $this->validateAndExtract($qualifiedName, $namespace); + $qualifiedName = ($prefix) ? "$prefix:$localName" : $localName; + + // Before we do the next step we need to work around a PHP DOM bug. PHP DOM + // cannot create attribute nodes if there's no document element. So, create the + // attribute node in a separate document which does have a document element and + // then import + $target = $this->innerNode; + $documentElement = $this->documentElement; + if ($documentElement === null) { + $target = new \DOMDocument(); + $target->appendChild($target->createElement('html')); + } + + # 2. Return a new attribute whose namespace is namespace, namespace prefix is + # prefix, local name is localName, and node document is this. + // We need to do a couple more things here. PHP's XML-based DOM doesn't allow + // some characters. We have to coerce them sometimes. + try { + $attr = $target->createAttributeNS($namespace, $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 + $attr = $target->createAttributeNS($namespace, $this->coerceName($qualifiedName)); + } + + if ($documentElement === null) { + return $this->importNode($attr); + } + + return $this->innerNode->getWrapperNode($attr); + } + + public function createCDATASection(string $data): CDATASection { + # The createCDATASection(data) method steps are: + # + # 1. If this is an HTML document, then throw a "NotSupportedError" DOMException. + if (!$this instanceof XMLDocument) { + throw new DOMException(DOMException::NOT_SUPPORTED); + } + + # 2. If data contains the string "]]>", then throw an "InvalidCharacterError" + # DOMException. + if (str_contains(needle: ']]>', haystack: $data)) { + throw new DOMException(DOMException::INVALID_CHARACTER_ERROR); + } + + # 3. Return a new CDATASection node with its data set to data and node document + # set to this. return $this->innerNode->getWrapperNode($this->innerNode->createCDATASection($data)); } - public function createComment(): Comment { + public function createComment(string $data): Comment { return $this->innerNode->getWrapperNode($this->innerNode->createComment($data)); } @@ -66,6 +180,10 @@ class Document extends Node { } public function createElement(string $localName): Element { + # The createElement(localName, options) method steps are: + // DEVIATION: The options parameter is at present only used for custom elements. + // There is no scripting in this implementation. + # 1. If localName does not match the Name production, then throw an # "InvalidCharacterError" DOMException. if (!preg_match(InnerDocument::NAME_PRODUCTION_REGEX, $localName)) { @@ -74,7 +192,7 @@ class Document extends Node { # 2. If this is an HTML document, then set localName to localName in ASCII # lowercase. - if ($this instanceof Document && !$this instanceof XMLElement) { + if (!$this instanceof XMLElement) { $localName = strtolower($localName); } @@ -100,8 +218,52 @@ class Document extends Node { return $this->innerNode->getWrapperNode($element); } + public function createElementNS(string $namespace, string $qualifiedName): Element { + # The internal createElementNS steps, given document, namespace, qualifiedName, + # and options, are as follows: + // DEVIATION: The options parameter is at present only used for custom elements. + // There is no scripting in this implementation. + + # 1. Let namespace, prefix, and localName be the result of passing namespace and + # qualifiedName to validate and extract. + [ 'namespace' => $namespace, 'prefix' => $prefix, 'localName' => $localName ] = $this->validateAndExtract($qualifiedName, $namespace); + $qualifiedName = ($prefix) ? "$prefix:$localName" : $localName; + + # 2. Let is be null. + # 3. If options is a dictionary and options["is"] exists, then set is to it. + # 4. Return the result of creating an element given document, localName, namespace, + # prefix, is, and with the synchronous custom elements flag set. + // DEVIATION: There is no scripting in this implementation. + + try { + $element = $this->innerNode->createElementNS($namespace, $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 + if ($namespace !== null) { + $qualifiedName = implode(':', array_map([ $this, 'coerceName' ], explode(':', $qualifiedName, 2))); + } else { + $qualifiedName = $this->coerceName($qualifiedName); + } + + $element = $this->innerNode->createElementNS($namespace, $qualifiedName); + } + + return $this->innerNode->getWrapperNode($element); + } + public function createProcessingInstruction(string $target, string $data): ProcessingInstruction { - return $this->innerNode->getWrapperNode($this->innerNode->createProcessingInstruction($target, $data)); + try { + $instruction = $this->innerNode->createProcessingInstruction($target, $data); + } catch (\DOMException $e) { + // The target is invalid for XML + // Replace any offending characters with "UHHHHHH" where H are the + // uppercase hexadecimal digits of the character's code point + $instruction = $this->innerNode->createProcessingInstruction($this->coerceName($target), $data); + } + + return $this->innerNode->getWrapperNode($instruction); } public function createTextNode(string $data): Text { diff --git a/lib/DocumentFragment.php b/lib/DocumentFragment.php index f4eee2a..5e3cbca 100644 --- a/lib/DocumentFragment.php +++ b/lib/DocumentFragment.php @@ -13,4 +13,9 @@ class DocumentFragment extends Node { use ParentNode; protected ?\WeakReference $host = null; + + + protected function __construct(\DOMDocumentFragment $fragment) { + parent::__construct($fragment); + } } diff --git a/lib/DocumentOrElement.php b/lib/DocumentOrElement.php new file mode 100644 index 0000000..77f0c19 --- /dev/null +++ b/lib/DocumentOrElement.php @@ -0,0 +1,71 @@ + $namespace, + 'prefix' => $prefix, + 'localName' => $localName + ]; + } +} diff --git a/lib/DocumentType.php b/lib/DocumentType.php index 73467d4..3186aa4 100644 --- a/lib/DocumentType.php +++ b/lib/DocumentType.php @@ -12,13 +12,25 @@ namespace MensBeam\HTML\DOM; class DocumentType extends Node { use ChildNode; + // We need to work around a PHP DOM bug where doctype nodes aren't associated + // with a document until they're appended. _ownerDocument is set when the node + // is created but ignored once the doctype is appended to a document + protected ?\WeakReference $_ownerDocument = null; + + protected function __get_name(): string { - // Return an empty string if a space because this implementation gets around a - // PHP DOM limitation by substituting an empty string for a space. + // Return an empty string if a space is the name of the doctype. While the DOM + // itself cannot create a doctype with an empty string as the name, the HTML + // parser can. PHP's DOM cannot handle an empty string as the name, so a single + // space (an invalid value) is used instead and coerced to an empty string. $name = $this->innerNode->name; return ($name !== ' ') ? $this->innerNode->name : ''; } + protected function __get_ownerDocument(): ?Document { + return parent::__get_ownerDocument() ?? $this->_ownerDocument->get(); + } + protected function __get_publicId(): string { return $this->innerNode->publicId; } diff --git a/lib/Element.php b/lib/Element.php index 0b25728..f47e00d 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -11,7 +11,7 @@ use MensBeam\HTML\Parser; class Element extends Node { - use ChildNode, ParentNode; + use ChildNode, DocumentOrElement, ParentNode; protected function __get_namespaceURI(): string { // PHP's DOM uses null incorrectly for the HTML namespace, and if you attempt to @@ -53,4 +53,15 @@ class Element extends Node { return $value; } + + public function setAttributeNode(Attr $attr): ?Attr { + # The setAttributeNode(attr) and setAttributeNodeNS(attr) methods steps are to + # return the result of setting an attribute given attr and this. + $this->innerNode->setAttributeNode($this->getInnerNode($attr)); + return $attr; + } + + public function setAttributeNodeNS(Attr $attr): ?Attr { + return $this->setAttributeNode($attr); + } } \ No newline at end of file diff --git a/lib/InnerNode/Document.php b/lib/InnerNode/Document.php index ca291e0..4c2cc45 100644 --- a/lib/InnerNode/Document.php +++ b/lib/InnerNode/Document.php @@ -98,6 +98,13 @@ class Document extends \DOMDocument { } $wrapperNode = Reflection::createFromProtectedConstructor(self::$parentNamespace . "\\$className", $node); + + // We need to work around a PHP DOM bug where doctype nodes aren't associated + // with a document until they're appended. + if ($className === 'DocumentType') { + Reflection::setProtectedProperty($wrapperNode, '_ownerDocument', $this->_wrapperNode); + } + $this->nodeMap->set($wrapperNode, $node); return $wrapperNode; } diff --git a/lib/InnerNode/NodeMap.php b/lib/InnerNode/NodeMap.php index 7b46061..bf07888 100644 --- a/lib/InnerNode/NodeMap.php +++ b/lib/InnerNode/NodeMap.php @@ -15,18 +15,16 @@ class NodeMap { protected $innerArray = []; - public function delete(\DOMNode|WrapperNode $node): bool { + public function delete(\DOMNode|WrapperNode $node): void { $key = $this->key($node); if ($key === false) { - return false; + return; } unset($this->wrapperArray[$key]); unset($this->innerArray[$key]); $this->wrapperArray = array_values($this->wrapperArray); $this->innerArray = array_values($this->innerArray); - - return true; } public function get(\DOMNode|WrapperNode $node): \DOMNode|WrapperNode|null { @@ -42,14 +40,11 @@ class NodeMap { return ($this->key($node) !== false); } - public function set(WrapperNode $wrapper, \DOMNode $inner): bool { + public function set(WrapperNode $wrapper, \DOMNode $inner): void { if (!$this->has($wrapper)) { $this->wrapperArray[] = $wrapper; $this->innerArray[] = $inner; - return true; } - - return false; } diff --git a/lib/Node.php b/lib/Node.php index fe124be..5bcee68 100644 --- a/lib/Node.php +++ b/lib/Node.php @@ -23,7 +23,7 @@ abstract class Node { public const ENTITY_NODE = 6; // legacy public const PROCESSING_INSTRUCTION_NODE = 7; public const COMMENT_NODE = 8; - public const DOCUMENT_MODE = 9; + public const DOCUMENT_NODE = 9; public const DOCUMENT_TYPE_NODE = 10; public const DOCUMENT_FRAGMENT_NODE = 11; public const NOTATION_NODE = 12; // legacy @@ -148,18 +148,29 @@ abstract class Node { protected function __get_nodeName(): string { # The nodeName getter steps are to return the first matching statement, # switching on the interface this implements: + $nodeName = $this->innerNode->nodeName; # ↪ Element # Its HTML-uppercased qualified name. if ($this instanceof Element) { - $nodeName = $this->innerNode->nodeName; // Uncoerce names if necessary return strtoupper(!str_contains(needle: 'U', haystack: $nodeName) ? $nodeName : $this->uncoerceName($nodeName)); } + // Attribute nodes and processing instructions need the node name uncoerced if + // necessary + elseif ($this instanceof Attr || $this instanceof ProcessingInstruction) { + return (!str_contains(needle: 'U', haystack: $nodeName)) ? $nodeName : $this->uncoerceName($nodeName); + } + // While the DOM itself cannot create a doctype with an empty string as the + // name, the HTML parser can. PHP's DOM cannot handle an empty string as the + // name, so a single space (an invalid value) is used instead and coerced to an + // empty string. + elseif ($this instanceof DocumentType) { + return $this->name; + } - // PHP's DOM mostly does this correctly with the exception of Element, so let's - // fall back to PHP's DOM on everything else. - return $this->innerNode->nodeName; + // PHP's DOM handles everything correctly on everything else. + return $nodeName; } protected function __get_nodeType(): int { @@ -198,15 +209,15 @@ abstract class Node { $this->innerNode->nodeValue = $value; } - protected function __get_ownerDocument(): Document { + protected function __get_ownerDocument(): ?Document { # The ownerDocument getter steps are to return null, if this is a document; # otherwise this’s node document. // PHP's DOM does this correctly already. - if ($this instanceof Document) { + if ($this instanceof Document || !$ownerDocument = $this->innerNode->ownerDocument) { return null; } - return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->ownerDocument); + return $this->innerNode->ownerDocument->getWrapperNode($ownerDocument); } protected function __get_parentElement(): ?Element { @@ -247,8 +258,6 @@ abstract class Node { public function appendChild(Node $node): Node { - # The appendChild(node) method steps are to return the result of appending node to - # this. // Aside from pre-insertion validity PHP's DOM does this correctly already. $this->preInsertionValidity($node); $this->innerNode->appendChild($this->getInnerNode($node)); diff --git a/lib/ProcessingInstruction.php b/lib/ProcessingInstruction.php index bcfb3c4..49a9175 100644 --- a/lib/ProcessingInstruction.php +++ b/lib/ProcessingInstruction.php @@ -11,6 +11,13 @@ namespace MensBeam\HTML\DOM; class ProcessingInstruction extends CharacterData { protected function __get_target(): string { - return $this->innerNode->target; + // Need to uncoerce string if necessary. + $target = $this->innerNode->target; + return (!str_contains(needle: 'U', haystack: $target)) ? $target : $this->uncoerceName($target); + } + + + protected function __construct(\DOMProcessingInstruction $pi) { + parent::__construct($pi); } } \ No newline at end of file diff --git a/lib/Text.php b/lib/Text.php index 689d3a8..7c74d1c 100644 --- a/lib/Text.php +++ b/lib/Text.php @@ -25,4 +25,9 @@ class Text extends CharacterData { return $this->innerNode->ownerDocument->getWrapperNode($this->innerNode->splitText($offset)); } + + + protected function __construct(\DOMText $text) { + parent::__construct($text); + } } \ No newline at end of file diff --git a/tests/cases/TestNode.php b/tests/cases/TestNode.php index 038a067..ea51a53 100644 --- a/tests/cases/TestNode.php +++ b/tests/cases/TestNode.php @@ -10,7 +10,8 @@ namespace MensBeam\HTML\DOM\TestCase; use MensBeam\HTML\DOM\{ Document, - Node + Node, + XMLDocument }; use MensBeam\HTML\Parser; @@ -24,6 +25,8 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\Document::createTextNode * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity * @covers \MensBeam\HTML\DOM\NodeList::__construct @@ -31,6 +34,7 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\NodeList::__get_length * @covers \MensBeam\HTML\DOM\NodeList::item * @covers \MensBeam\HTML\DOM\NodeList::offsetGet + * @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode @@ -68,6 +72,8 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\Document::__construct * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct @@ -97,13 +103,15 @@ class TestNode extends \PHPUnit\Framework\TestCase { /** * @covers \MensBeam\HTML\DOM\Node::__get_isConnected + * @covers \MensBeam\HTML\DOM\Node::getRootNode * * @covers \MensBeam\HTML\DOM\ChildNode::moonwalk * @covers \MensBeam\HTML\DOM\Document::__construct * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild - * @covers \MensBeam\HTML\DOM\Node::getRootNode * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode @@ -132,8 +140,11 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\Document::createTextNode * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode @@ -170,15 +181,20 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\Document::__construct * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentType::__construct * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key @@ -209,15 +225,20 @@ class TestNode extends \PHPUnit\Framework\TestCase { * @covers \MensBeam\HTML\DOM\Document::__construct * @covers \MensBeam\HTML\DOM\Document::createElement * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentType::__construct * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct * @covers \MensBeam\HTML\DOM\Node::appendChild * @covers \MensBeam\HTML\DOM\Node::preInsertionValidity + * @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key @@ -245,16 +266,34 @@ class TestNode extends \PHPUnit\Framework\TestCase { /** * @covers \MensBeam\HTML\DOM\Node::__get_nodeName * + * @covers \MensBeam\HTML\DOM\Attr::__construct + * @covers \MensBeam\HTML\DOM\Comment::__construct + * @covers \MensBeam\HTML\DOM\CDATASection::__construct * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::createComment + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createElementNS + * @covers \MensBeam\HTML\DOM\Document::createProcessingInstruction * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__get_name * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\ProcessingInstruction::__construct + * @covers \MensBeam\HTML\DOM\Text::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key @@ -262,11 +301,334 @@ class TestNode extends \PHPUnit\Framework\TestCase { */ public function testProperty_nodeName() { $d = new Document(); - $dt = $d->appendChild($d->implementation->createDocumentType('html', '', '')); + $d2 = new XMLDocument(); + + // Node::nodeName on attribute node + $this->assertSame('href', $d->createAttribute('href')->nodeName); + // Node::nodeName on attribute node with coerced name + $this->assertSame('poop💩', $d->createAttribute('poop💩')->nodeName); + // Node::nodeName on namespaced attribute node + $this->assertSame('xlink:href', $d->createAttributeNS(Parser::XLINK_NAMESPACE, 'xlink:href')->nodeName); + // Node::nodeName on namespaced attribute node with coerced name + $this->assertSame('poop💩:poop💩', $d->createAttributeNS('https://poop💩.poop', 'poop💩:poop💩')->nodeName); + + // Node::nodeName on CDATA section + $this->assertSame('#cdata-section', $d2->createCDATASection('ook')->nodeName); + + // Node::nodeName on comment + $this->assertSame('#comment', $d->createComment('comment')->nodeName); + + // Node::nodeName on document + $this->assertSame('#document', $d->nodeName); + + // Node::nodeName on doctype + $this->assertSame('html', $d->implementation->createDocumentType('html', '', '')->nodeName); + + // Node::nodeName on document fragment + $this->assertSame('#document-fragment', $d->createDocumentFragment()->nodeName); // Node::nodeName on element $this->assertSame('HTML', $d->createElement('html')->nodeName); // Node::nodeName on element with coerced name $this->assertSame('POOP💩', $d->createElement('poop💩')->nodeName); + // Node::nodeName on namespaced element + $this->assertSame('SVG', $d->createElementNS(Parser::SVG_NAMESPACE, 'svg')->nodeName); + // Node::nodeName on namespaced element with coerced name + $this->assertSame('POOP💩:POOP💩', $d->createElementNS('https://poop💩.poop', 'poop💩:poop💩')->nodeName); + + // Node::nodeName on processing instruction + $this->assertSame('ook', $d->createProcessingInstruction('ook', 'eek')->nodeName); + // Node::nodeName on processing instruction with coerced target + $this->assertSame('poop💩', $d->createProcessingInstruction('poop💩', 'ook')->nodeName); + + // Node::nodeName on text node + $this->assertSame('#text', $d->createTextNode('ook')->nodeName); + } + + + /** + * @covers \MensBeam\HTML\DOM\Node::__get_nodeType + * + * @covers \MensBeam\HTML\DOM\Attr::__construct + * @covers \MensBeam\HTML\DOM\Comment::__construct + * @covers \MensBeam\HTML\DOM\CDATASection::__construct + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::createComment + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createElementNS + * @covers \MensBeam\HTML\DOM\Document::createProcessingInstruction + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\ProcessingInstruction::__construct + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::set + */ + public function testProperty_nodeType() { + $d = new Document(); + $d2 = new XMLDocument(); + + // Node::nodeType on attribute node + $this->assertSame(Node::ATTRIBUTE_NODE, $d->createAttribute('href')->nodeType); + // Node::nodeType on CDATA section + $this->assertSame(Node::CDATA_SECTION_NODE, $d2->createCDATASection('ook')->nodeType); + // Node::nodeType on comment + $this->assertSame(Node::COMMENT_NODE, $d->createComment('comment')->nodeType); + // Node::nodeType on document + $this->assertSame(Node::DOCUMENT_NODE, $d->nodeType); + // Node::nodeType on doctype + $this->assertSame(Node::DOCUMENT_TYPE_NODE, $d->implementation->createDocumentType('html', '', '')->nodeType); + // Node::nodeType on document fragment + $this->assertSame(Node::DOCUMENT_FRAGMENT_NODE, $d->createDocumentFragment()->nodeType); + // Node::nodeType on element + $this->assertSame(Node::ELEMENT_NODE, $d->createElement('html')->nodeType); + // Node::nodeType on processing instruction + $this->assertSame(Node::PROCESSING_INSTRUCTION_NODE, $d->createProcessingInstruction('ook', 'eek')->nodeType); + // Node::nodeType on text node + $this->assertSame(Node::TEXT_NODE, $d->createTextNode('ook')->nodeType); + } + + + /** + * @covers \MensBeam\HTML\DOM\Node::__get_nodeValue + * @covers \MensBeam\HTML\DOM\Node::__set_nodeValue + * + * @covers \MensBeam\HTML\DOM\Attr::__construct + * @covers \MensBeam\HTML\DOM\Comment::__construct + * @covers \MensBeam\HTML\DOM\CDATASection::__construct + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::createComment + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createElementNS + * @covers \MensBeam\HTML\DOM\Document::createProcessingInstruction + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\ProcessingInstruction::__construct + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::set + */ + public function testProperty_nodeValue() { + $d = new Document(); + $d2 = new XMLDocument(); + $attr = $d->createAttribute('href'); + $attr->value = 'https://poop💩.poop'; + $cdata = $d2->createCDATASection('ook'); + $comment = $d->createComment('comment'); + $element = $d->createElement('html'); + $pi = $d->createProcessingInstruction('ook', 'eek'); + $text = $d->createTextNode('ook'); + + // Node::nodeValue on attribute node + $this->assertSame('https://poop💩.poop', $attr->nodeValue); + $attr->nodeValue = 'https://ook.com'; + $this->assertSame('https://ook.com', $attr->nodeValue); + + // Node::nodeValue on CDATA section + $this->assertSame('ook', $cdata->nodeValue); + $cdata->nodeValue = 'eek'; + $this->assertSame('eek', $cdata->nodeValue); + + // Node::nodeValue on comment + $this->assertSame('comment', $comment->nodeValue); + $comment->nodeValue = 'poop💩'; + $this->assertSame('poop💩', $comment->nodeValue); + + // Node::nodeValue on document + $this->assertnull($d->nodeValue); + + // Node::nodeValue on doctype + $this->assertNull($d->implementation->createDocumentType('html', '', '')->nodeValue); + + // Node::nodeValue on document fragment + $this->assertNull($d->createDocumentFragment()->nodeValue); + + // Node::nodeValue on element + $this->assertNull($element->nodeValue); + $element->nodeValue = ''; // This should do nothing + $this->assertNull($element->nodeValue); + + + // Node::nodeValue on processing instruction + $this->assertSame('eek', $pi->nodeValue); + $pi->nodeValue = 'ook'; + $this->assertSame('ook', $pi->nodeValue); + + // Node::nodeValue on text node + $this->assertSame('ook', $text->nodeValue); + $text->nodeValue = 'eek'; + $this->assertSame('eek', $text->nodeValue); + } + + + /** + * @covers \MensBeam\HTML\DOM\Node::__get_ownerDocument + * + * @covers \MensBeam\HTML\DOM\Attr::__construct + * @covers \MensBeam\HTML\DOM\Comment::__construct + * @covers \MensBeam\HTML\DOM\CDATASection::__construct + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::createComment + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createElementNS + * @covers \MensBeam\HTML\DOM\Document::createProcessingInstruction + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\ProcessingInstruction::__construct + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::set + */ + public function testProperty_ownerDocument() { + $d = new Document(); + $d2 = new XMLDocument(); + + // Node::nodeType on attribute node + $this->assertSame($d, $d->createAttribute('href')->ownerDocument); + // Node::nodeType on CDATA section + $this->assertSame($d2, $d2->createCDATASection('ook')->ownerDocument); + // Node::nodeType on comment + $this->assertSame($d, $d->createComment('comment')->ownerDocument); + // Node::nodeType on document + $this->assertNull($d->ownerDocument); + // Node::nodeType on doctype + $this->assertSame($d, $d->implementation->createDocumentType('html', '', '')->ownerDocument); + // Node::nodeType on document fragment + $this->assertSame($d, $d->createDocumentFragment()->ownerDocument); + // Node::nodeType on element + $this->assertSame($d, $d->createElement('html')->ownerDocument); + // Node::nodeType on processing instruction + $this->assertSame($d, $d->createProcessingInstruction('ook', 'eek')->ownerDocument); + // Node::nodeType on text node + $this->assertSame($d, $d->createTextNode('ook')->ownerDocument); + } + + + /** + * @covers \MensBeam\HTML\DOM\Node::__get_parentElement + * @covers \MensBeam\HTML\DOM\Node::__get_parentNode + * + * @covers \MensBeam\HTML\DOM\Attr::__construct + * @covers \MensBeam\HTML\DOM\Comment::__construct + * @covers \MensBeam\HTML\DOM\CDATASection::__construct + * @covers \MensBeam\HTML\DOM\Document::__construct + * @covers \MensBeam\HTML\DOM\Document::createAttribute + * @covers \MensBeam\HTML\DOM\Document::createAttributeNS + * @covers \MensBeam\HTML\DOM\Document::createComment + * @covers \MensBeam\HTML\DOM\Document::createDocumentFragment + * @covers \MensBeam\HTML\DOM\Document::createElement + * @covers \MensBeam\HTML\DOM\Document::createElementNS + * @covers \MensBeam\HTML\DOM\Document::createProcessingInstruction + * @covers \MensBeam\HTML\DOM\Document::createTextNode + * @covers \MensBeam\HTML\DOM\DocumentOrElement::validateAndExtract + * @covers \MensBeam\HTML\DOM\DocumentFragment::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__construct + * @covers \MensBeam\HTML\DOM\DocumentType::__get_ownerDocument + * @covers \MensBeam\HTML\DOM\DOMImplementation::__construct + * @covers \MensBeam\HTML\DOM\DOMImplementation::createDocumentType + * @covers \MensBeam\HTML\DOM\Element::__construct + * @covers \MensBeam\HTML\DOM\Node::__construct + * @covers \MensBeam\HTML\DOM\ProcessingInstruction::__construct + * @covers \MensBeam\HTML\DOM\Text::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__construct + * @covers \MensBeam\HTML\DOM\InnerNode\Document::getWrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Document::__get_wrapperNode + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::createFromProtectedConstructor + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::getProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\Reflection::setProtectedProperty + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::get + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::has + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::key + * @covers \MensBeam\HTML\DOM\InnerNode\NodeMap::set + */ + public function testProperty_parentElement() { + $d = new Document(); + $doctype = $d->appendChild($d->implementation->createDocumentType('html', '', '')); + $documentElement = $d->appendChild($d->createElement('html')); + $documentElement->appendChild($d->createElement('body')); + $body = $d->body; + $attr = $d->createAttribute('href'); + $attr->value = 'https://poop💩.poop'; + $body->setAttributeNode($attr); + $comment = $body->appendChild($d->createComment('ook')); + $pi = $body->appendChild($d->createProcessingInstruction('ook', 'eek')); + $text = $body->appendChild($d->createTextNode('ook')); + + $d2 = new XMLDocument(); + $xmlElement = $d2->appendChild($d2->createElement('ook')); + $cdata = $xmlElement->appendChild($d2->createCDATASection('ook')); + + // Node::parentElement on attribute node + $this->assertSame($body, $attr->parentElement); + // Node::parentElement on CDATA section + $this->assertSame($xmlElement, $cdata->parentElement); + // Node::parentElement on comment + $this->assertSame($body, $comment->parentElement); + // Node::parentElement on document + $this->assertNull($d->parentElement); + // Node::parentElement on doctype + $this->assertNull($doctype->parentElement); + // Node::parentNode on doctype + $this->assertSame($d, $doctype->parentNode); + // Node::parentElement on document fragment + $this->assertNull($d->createDocumentFragment()->parentElement); + // Node::parentElement on element + $this->assertSame($documentElement, $body->parentElement); + // Node::parentElement on processing instruction + $this->assertSame($body, $pi->parentElement); + // Node::parentElement on text node + $this->assertSame($body, $text->parentElement); } } \ No newline at end of file diff --git a/vendor-bin/phpunit/composer.lock b/vendor-bin/phpunit/composer.lock index d221c04..2f525c8 100644 --- a/vendor-bin/phpunit/composer.lock +++ b/vendor-bin/phpunit/composer.lock @@ -1801,7 +1801,6 @@ "type": "github" } ], - "abandoned": true, "time": "2020-09-28T06:45:17+00:00" }, {