diff --git a/lib/DOMException.php b/lib/DOMException.php index d50ba23..dad52a6 100644 --- a/lib/DOMException.php +++ b/lib/DOMException.php @@ -27,7 +27,7 @@ class DOMException extends \Exception { 7 => 'Modification not allowed here', 8 => 'Not found error', 12 => 'Syntax error', - 100 => 'Argument #%s (\$%s) must be of type %s, %s given', + 100 => 'Argument #%s ($%s) must be of type %s, %s given', 101 => 'Failed to set the "outerHTML" property; the element does not have a parent node' ]; diff --git a/lib/Document.php b/lib/Document.php index 3e96819..f58bb9a 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -124,7 +124,11 @@ class Document extends AbstractDocument { public function __construct($source = null, ?string $encoding = null, int $quirksMode = 0) { // Because we cannot have union types until php 8... :) if ($source !== null && !$source instanceof \DOMDocument && !is_string($source)) { - throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', '\DOMDocument|string|null', gettype($source)); + $type = gettype($source); + if ($type === 'object') { + $type = get_class($source); + } + throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', '\DOMDocument|string|null', $type); } parent::__construct(); @@ -214,9 +218,13 @@ class Document extends AbstractDocument { public function importNode(\DOMNode $node, bool $deep = false) { $node = parent::importNode($node, $deep); - /*if ($node instanceof \DOMElement) { - $node = $this->convertTemplate($node); - }*/ + if ($node instanceof \DOMElement || $node instanceof \DOMDocumentFragment) { + if ($node instanceof \DOMElement && !$node instanceof HTMLTemplateElement && $node->namespaceURI === null && $node->nodeName === 'template') { + $node = $this->convertTemplate($node); + } else { + $this->replaceTemplates($node); + } + } return $node; } @@ -233,13 +241,17 @@ class Document extends AbstractDocument { public function loadDOM(\DOMDocument $source, ?string $encoding = null, int $quirksMode = 0) { if (!$source instanceof \DOMDocument) { - throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', '\DOMDocument', gettype($source)); + $type = gettype($source); + if ($type === 'object') { + $type = get_class($source); + } + throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', '\DOMDocument', $type); } $this->_documentEncoding = $encoding; $this->_quirksMode = $quirksMode; - // If there are already existing child nodes then remove them before loading the + // If there are already-existing child nodes then remove them before loading the // DOM. while ($this->hasChildNodes()) { $this->removeChild($this->firstChild); @@ -253,13 +265,16 @@ class Document extends AbstractDocument { } } - $this->replaceTemplates(); return true; } public function loadHTML($source, $options = null, ?string $encoding = null): bool { if (!is_string($source)) { - throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', 'string', gettype($source)); + $type = gettype($source); + if ($type === 'object') { + $type = get_class($source); + } + throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'source', 'string', $type); } $source = Parser::parse($source, $encoding, null); @@ -788,13 +803,16 @@ class Document extends AbstractDocument { while ($element->hasChildNodes()) { $child = $element->firstChild; - if ($child instanceof Element && !$child instanceof HTMLTemplateElement && $child->namespaceURI === null && $child->nodeName === 'template') { - $newChild = $this->convertTemplate($child); - $child->parentNode->removeChild($child); - $child = $newChild; + if ($child instanceof Element) { + if (!$child instanceof HTMLTemplateElement && $child->namespaceURI === null && $child->nodeName === 'template') { + $newChild = $this->convertTemplate($child); + $child->parentNode->removeChild($child); + $child = $newChild; + } + + $this->replaceTemplates($child); } - $this->replaceTemplates($child); $template->content->appendChild($child); } @@ -807,7 +825,17 @@ class Document extends AbstractDocument { private function replaceTemplates(?\DOMNode $node = null) { if ($node === null) { $node = $this; - } elseif ($node instanceof HTMLTemplateElement) { + } + + if (!$node instanceof \DOMDocument && !$node instanceof \DOMElement && !$node instanceof \DOMDocumentFragment) { + $type = gettype($node); + if ($type === 'object') { + $type = get_class($node); + } + throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'node', '\DOMDocument|\DOMDocumentFragment|\DOMElement|null', $type); + } + + if ($node instanceof HTMLTemplateElement) { $node = $node->content; } diff --git a/lib/Element.php b/lib/Element.php index 829ec33..71c9d34 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -52,7 +52,8 @@ class Element extends \DOMElement { # 2. Let fragment be the result of invoking the fragment parsing algorithm with # the new value as markup, and with context element. - $fragment = Parser::parseFragment($value, $this->ownerDocument, 'UTF-8', $this); + $fragment = Parser::parseFragment($value, null, 'UTF-8', $this); + $fragment = $this->ownerDocument->importNode($fragment); # 3. If the context object is a template element, then let context object be the # template's template contents (a DocumentFragment). @@ -169,7 +170,8 @@ class Element extends \DOMElement { # 5. Let fragment be the result of invoking the fragment parsing algorithm with # the new value as markup, and parent as the context element. - $fragment = Parser::parseFragment($value, $this->ownerDocument, 'UTF-8', $parent); + $fragment = Parser::parseFragment($value, null, 'UTF-8', $parent); + $fragment = $this->ownerDocument->importNode($fragment); # 6. Replace the context object with fragment within the context object's # parent. diff --git a/lib/traits/ParentNode.php b/lib/traits/ParentNode.php index a9419b1..35b3eff 100644 --- a/lib/traits/ParentNode.php +++ b/lib/traits/ParentNode.php @@ -225,7 +225,11 @@ if (version_compare(\PHP_VERSION, '8.0', '>=')) { foreach ($nodes as $n) { // Can't do union types until PHP 8... OTL if (!$n instanceof \DOMNode && !is_string($n)) { - throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'nodes', '\DOMNode|string', gettype($n)); + $type = gettype($n); + if ($type === 'object') { + $type = get_class($n); + } + throw new DOMException(DOMException::ARGUMENT_TYPE_ERROR, 1, 'nodes', '[\DOMNode|string]', $n); } $nn = (!is_string($n)) ? $n : $this->ownerDocument->createTextNode($n); diff --git a/tests/cases/TestSerializer.php b/tests/cases/TestSerializer.php index ae5cc5c..db7339d 100644 --- a/tests/cases/TestSerializer.php +++ b/tests/cases/TestSerializer.php @@ -7,9 +7,9 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM\TestCase; use MensBeam\HTML\DOM\Document; -use MensBeam\HTML\DOM\Parser; +use MensBeam\HTML\Parser; -/** +/** * @covers \MensBeam\HTML\DOM\Document * @covers \MensBeam\HTML\DOM\DocumentFragment * @covers \MensBeam\HTML\DOM\Element