Nested templates don't work :/

This commit is contained in:
Dustin Wilson 2021-09-28 23:19:10 -05:00
parent eaf51dc738
commit d6f75116da
25 changed files with 230 additions and 114 deletions

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;
@ -10,6 +12,9 @@ use MensBeam\HTML\Parser,
class Document extends AbstractDocument {
public $mangledAttributes = false;
public $mangledElements = false;
protected $_body = null;
/** Nonstandard */
protected $_documentEncoding;
@ -185,9 +190,6 @@ class Document extends AbstractDocument {
$e = parent::createElementNS($namespaceURI, $qualifiedName, $value);
} else {
$e = new HTMLTemplateElement($this, $qualifiedName, $value);
// Template elements need to have a reference kept in userland
ElementMap::set($e);
$e->content = $this->createDocumentFragment();
}
return $e;
@ -212,9 +214,9 @@ class Document extends AbstractDocument {
public function importNode(\DOMNode $node, bool $deep = false) {
$node = parent::importNode($node, $deep);
if ($node instanceof \DOMElement) {
$node = $this->convertElementToSubClass($node);
}
/*if ($node instanceof \DOMElement) {
$node = $this->convertTemplate($node);
}*/
return $node;
}
@ -251,16 +253,7 @@ class Document extends AbstractDocument {
}
}
$templates = $this->walk(function($n) {
if ($n instanceof Element && $n->namespaceURI === null && $n->nodeName === 'template') {
return true;
}
});
foreach ($templates as $template) {
$template->replaceWith($this->convertElementToSubClass($template));
}
$this->replaceTemplates();
return true;
}
@ -785,7 +778,7 @@ class Document extends AbstractDocument {
}
private function convertElementToSubClass(\DOMElement $element): \DOMElement {
private function convertTemplate(\DOMElement $element): \DOMElement {
if ($element->namespaceURI === null && $element->nodeName === 'template') {
$template = $this->createElement('template');
@ -793,7 +786,16 @@ class Document extends AbstractDocument {
$template->setAttributeNode($element->attributes->item(0));
}
while ($element->hasChildNodes()) {
$template->content->appendChild($element->firstChild);
$child = $element->firstChild;
if ($child instanceof Element && $child->namespaceURI === null && $child->nodeName === 'template') {
$newChild = $this->convertTemplate($child);
$child->parentNode->removeChild($child);
$child = $newChild;
}
$this->replaceTemplates($child);
$template->content->appendChild($child);
}
$element = $template;
@ -802,6 +804,34 @@ class Document extends AbstractDocument {
return $element;
}
private function replaceTemplates(?\DOMNode $node = null) {
if ($node === null) {
$node = $this;
} elseif ($node instanceof HTMLTemplateElement) {
$node = $node->content;
}
$templates = $node->walk(function($n) {
if ($n instanceof Element && $n->namespaceURI === null && $n->nodeName === 'template') {
return true;
}
});
// FIXME: This is a really ugly way to do it, but I -HAVE- to replace these templates in reverse.
$temp = [];
foreach ($templates as $template) {
$temp[] = $template;
}
$templates = $temp;
// Iterate through the templates in reverse so nested templates can be handled
// properly.
for ($templatesLength = count($templates), $i = $templatesLength - 1; $i >= 0; $i--) {
$template = $templates[$i];
$template->parentNode->replaceChild($this->convertTemplate($template), $template);
}
}
public function __toString() {
return $this->saveHTML();

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,10 +1,14 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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\Parser;
class Element extends \DOMElement {
use ContainerNode, DocumentOrElement, EscapeString, MagicProperties, Moonwalk, ParentNode, ToString, Walk;
@ -34,7 +38,7 @@ class Element extends \DOMElement {
# might throw an exception instead of returning a string).
// DEVIATION: Parsing of XML documents will not be handled by this
// implementation, so there's no need for the well-formed flag.
return $this->ownerDocument->serialize($this);
return $this->ownerDocument->saveHTML($this);
}
protected function __set_innerHTML(string $value) {

View file

@ -1,5 +1,6 @@
<?php
/** @license MIT
/**
* @license MIT
* Copyright 2017 Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details
*/

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;
@ -21,9 +23,25 @@ class HTMLTemplateElement extends Element {
$frag->appendChild($this);
$frag->removeChild($this);
unset($frag);
$this->content = $this->ownerDocument->createDocumentFragment();
// Template elements need to have a reference kept in userland
ElementMap::set($this);
}
public function __destruct() {
ElementMap::delete($this);
public function cloneNode(bool $deep = false) {
$copy = $this->ownerDocument->createElement('template');
foreach ($this->attributes as $attr) {
$copy->setAttributeNS($attr->namespaceURI, $attr->name, $attr->value);
}
if ($deep) {
foreach ($this->content->childNodes as $child) {
$copy->content->appendChild($child->cloneNode(true));
}
}
return $copy;
}
}

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,6 +1,7 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
/**
* @license MIT
* Copyright 2017, Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details
*/
@ -21,7 +22,7 @@ trait MagicProperties {
// exist fatal error.
$methodName = $this->getMagicPropertyMethodName($name);
if (!method_exists($this, $methodName)) {
trigger_error("Property \"$name\" does not exist", \E_USER_ERROR);
throw new \Exception("Property \"$name\" does not exist");
}
return call_user_func([ $this, $methodName ]);
}
@ -42,16 +43,16 @@ trait MagicProperties {
// Finally, if a getter doesn't exist trigger a property does not exist fatal
// error.
if (method_exists($this, $this->getMagicPropertyMethodName($name))) {
trigger_error("Cannot write readonly property \"$name\"", \E_USER_ERROR);
throw new \Exception("Cannot write readonly property \"$name\"");
} else {
trigger_error("Property \"$name\" does not exist", \E_USER_ERROR);
throw new \Exception("Property \"$name\" does not exist");
}
}
public function __unset(string $name) {
$methodName = $this->getMagicPropertyMethodName($name, false);
if (!method_exists($this, $methodName)) {
trigger_error("Cannot write readonly property \"$name\"", \E_USER_ERROR);
throw new \Exception("Cannot write readonly property \"$name\"");
}
call_user_func([ $this, $methodName ], null);

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
/**
* @license MIT
* Copyright 2017, Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\HTML\DOM;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
* Copyright 2017 , Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
/**
* @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;

View file

@ -1,7 +1,9 @@
<?php
/** @license MIT
/**
* @license MIT
* Copyright 2017, Dustin Wilson, J. King et al.
* See LICENSE and AUTHORS files for details */
* See LICENSE and AUTHORS files for details
*/
declare(strict_types=1);
namespace MensBeam\HTML\DOM;
@ -23,12 +25,12 @@ trait Walk {
$prev = $node->previousSibling;
if ($filter === null || $filter($node) === true) {
yield $node;
}
// If the node was replaced mid-loop then make node be the element that it was
// replaced with by determining the previous node's position.
if ($node->parentNode === null) {
$node = $prev->nextSibling ?? $parent->firstChild;
}
// If the node was replaced mid-loop then make node be the element that it was
// replaced with by determining the previous node's position.
if ($node->parentNode === null) {
$node = $prev->nextSibling ?? $parent->firstChild;
}
if ($node instanceof HTMLTemplateElement) {

View file

@ -6,9 +6,12 @@
declare(strict_types=1);
namespace MensBeam\HTML\DOM\TestCase;
use MensBeam\HTML\DOM\Document;
use MensBeam\HTML\DOM\Parser;
use MensBeam\HTML\DOM\TemplateElement;
use MensBeam\HTML\DOM\{
Document,
HTMLTemplateElement
};
use MensBeam\HTML\Parser;
class TestDOM extends \PHPUnit\Framework\TestCase {
/**
@ -63,25 +66,25 @@ class TestDOM extends \PHPUnit\Framework\TestCase {
}
/** @covers \MensBeam\HTML\DOM\Document::createElementNS */
public function testCreateTemplateElements(): void {
public function testCreateHTMLTemplateElements(): void {
$d = new Document;
$t = $d->createElement("template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElement("TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS(null, "template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS(null, "TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS("http://www.w3.org/1999/xhtml", "template");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
$t = $d->createElementNS("http://www.w3.org/1999/xhtml", "TEMPLATE");
$this->assertInstanceOf(TemplateElement::class, $t);
$this->assertInstanceOf(HTMLTemplateElement::class, $t);
$this->assertNotNull($t->ownerDocument);
}

View file

@ -1,26 +1,25 @@
<?xml version="1.0"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
colors="true"
bootstrap="bootstrap.php"
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
beStrictAboutTestsThatDoNotTestAnything="true"
forceCoversAnnotation="true"
>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../lib</directory>
</whitelist>
</filter>
<testsuites>
forceCoversAnnotation="true">
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">../lib</directory>
</include>
</coverage>
<testsuites>
<testsuite name="DOM">
<file>cases/TestDOM.php</file>
<file>cases/TestDOM.php</file>
</testsuite>
<testsuite name="Serializer">
<file>cases/TestSerializer.php</file>
<file>cases/TestSerializer.php</file>
</testsuite>
</testsuites>
</testsuites>
</phpunit>

View file

@ -0,0 +1,26 @@
<?xml version="1.0"?>
<phpunit
colors="true"
bootstrap="bootstrap.php"
convertErrorsToExceptions="false"
convertNoticesToExceptions="false"
convertWarningsToExceptions="false"
beStrictAboutTestsThatDoNotTestAnything="true"
forceCoversAnnotation="true"
>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../lib</directory>
</whitelist>
</filter>
<testsuites>
<testsuite name="DOM">
<file>cases/TestDOM.php</file>
</testsuite>
<testsuite name="Serializer">
<file>cases/TestSerializer.php</file>
</testsuite>
</testsuites>
</phpunit>