Browse Source

Upgraded dependencies, added lots of tests for Node

wrapper-classes
Dustin Wilson 3 years ago
parent
commit
41129ddc07
  1. 54
      composer.lock
  2. 4
      lib/Comment.php
  3. 2
      lib/DOMException.php
  4. 5
      lib/DOMImplementation.php
  5. 172
      lib/Document.php
  6. 5
      lib/DocumentFragment.php
  7. 71
      lib/DocumentOrElement.php
  8. 16
      lib/DocumentType.php
  9. 13
      lib/Element.php
  10. 7
      lib/InnerNode/Document.php
  11. 11
      lib/InnerNode/NodeMap.php
  12. 29
      lib/Node.php
  13. 9
      lib/ProcessingInstruction.php
  14. 5
      lib/Text.php
  15. 368
      tests/cases/TestNode.php
  16. 1
      vendor-bin/phpunit/composer.lock

54
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",

4
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);
}
}

2
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',

5
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 {

172
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 {

5
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);
}
}

71
lib/DocumentOrElement.php

@ -0,0 +1,71 @@
<?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\DOM;
use MensBeam\HTML\DOM\InnerNode\Document as InnerDocument,
MensBeam\HTML\Parser;
/**
* Not in standard. Exists so Document and Element can share some properties and
* methods. For instance, getElementsByClassName is mentioned in the standard in
* both the Document and Element interfaces.
*/
trait DocumentOrElement {
protected function validateAndExtract(string $qualifiedName, ?string $namespace = null): array {
# To validate and extract a namespace and qualifiedName, run these steps:
# 1. If namespace is the empty string, set it to null.
if ($namespace === '') {
$namespace = null;
}
# 2. Validate qualifiedName.
# To validate a qualifiedName, throw an "InvalidCharacterError" DOMException if
# qualifiedName does not match the QName production.
if (preg_match(InnerDocument::QNAME_PRODUCTION_REGEX, $qualifiedName) !== 1) {
throw new DOMException(DOMException::INVALID_CHARACTER);
}
# 3. Let prefix be null.
$prefix = null;
# 4. Let localName be qualifiedName.
$localName = $qualifiedName;
# 5. If qualifiedName contains a ":" (U+003E), then split the string on it and
# set prefix to the part before and localName to the part after.
if (strpos($qualifiedName, ':') !== false) {
$temp = explode(':', $qualifiedName, 2);
$prefix = $temp[0];
$prefix = ($prefix !== '') ? $prefix : null;
$localName = $temp[1];
}
# 6. If prefix is non-null and namespace is null, then throw a "NamespaceError" DOMException.
# 7. If prefix is "xml" and namespace is not the XML namespace, then throw a "NamespaceError" DOMException.
# 8. If either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS
# namespace, then throw a "NamespaceError" DOMException.
# 9. If namespace is the XMLNS namespace and neither qualifiedName nor prefix is
# "xmlns", then throw a "NamespaceError" DOMException.
if (
($prefix !== null && $namespace === null) ||
($prefix === 'xml' && $namespace !== Parser::XML_NAMESPACE) ||
(($qualifiedName === 'xmlns' || $prefix === 'xmlns') && $namespace !== Parser::XMLNS_NAMESPACE) ||
($namespace === Parser::XMLNS_NAMESPACE && $qualifiedName !== 'xmlns' && $prefix !== 'xmlns')
) {
throw new DOMException(DOMException::NAMESPACE_ERROR);
}
# 10. Return namespace, prefix, and localName.
return [
'namespace' => $namespace,
'prefix' => $prefix,
'localName' => $localName
];
}
}

16
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;
}

13
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);
}
}

7
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;
}

11
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;
}

29
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));

9
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);
}
}

5
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);
}
}

368
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);
}
}

1
vendor-bin/phpunit/composer.lock

@ -1801,7 +1801,6 @@
"type": "github"
}
],
"abandoned": true,
"time": "2020-09-28T06:45:17+00:00"
},
{

Loading…
Cancel
Save