Skip to content

Commit d58d028

Browse files
authored
refactor(view): use PHP 8.4 DOM (#787)
1 parent 3f0d17b commit d58d028

File tree

8 files changed

+49
-41
lines changed

8 files changed

+49
-41
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"giggsey/libphonenumber-for-php": "^8.13.40",
1717
"guzzlehttp/guzzle": "^7.8",
1818
"laminas/laminas-diactoros": "^3.3",
19-
"masterminds/html5": "^2.9",
2019
"monolog/monolog": "^3.7.0",
2120
"nette/php-generator": "^4.1.6",
2221
"nikic/php-parser": "^5.3",
@@ -144,7 +143,7 @@
144143
"scripts": {
145144
"phpunit": "vendor/bin/phpunit --display-warnings --display-skipped --display-deprecations --display-errors --display-notices",
146145
"coverage": "vendor/bin/phpunit --coverage-html build/reports/html --coverage-clover build/reports/clover.xml",
147-
"csfixer": "vendor/bin/php-cs-fixer fix --allow-risky=yes",
146+
"csfixer": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --allow-risky=yes",
148147
"phpstan": "vendor/bin/phpstan analyse src tests --memory-limit=1G",
149148
"rector": "vendor/bin/rector process --no-ansi",
150149
"merge": "vendor/bin/monorepo-builder merge",

phpstan.neon.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ parameters:
2929
message: '#.*uninitialized readonly property \$composer*#'
3030
-
3131
message: '#.*uninitialized readonly property \$stubFileGenerator*#'
32+
-
33+
message: '#.*undefined property Dom*#'
34+
-
35+
message: '#.*undefined method Dom*#'
36+
-
37+
message: '#.* undefined static method Dom*#'
3238

3339
disallowedFunctionCalls:
3440
-

src/Tempest/View/composer.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
"minimum-stability": "dev",
66
"require": {
77
"php": "^8.4",
8-
"masterminds/html5": "^2.9",
98
"tempest/core": "dev-main",
109
"tempest/container": "dev-main",
1110
"tempest/validation": "dev-main",

src/Tempest/View/src/Elements/ElementFactory.php

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
namespace Tempest\View\Elements;
66

7-
use DOMAttr;
8-
use DOMElement;
9-
use DOMNode;
10-
use DOMText;
7+
use Dom\Element as DomElement;
8+
use Dom\Node;
9+
use Dom\Text;
1110
use Tempest\Container\Container;
1211
use function Tempest\Support\str;
1312
use Tempest\View\Element;
@@ -32,17 +31,17 @@ public function setViewCompiler(TempestViewCompiler $compiler): self
3231
return $this;
3332
}
3433

35-
public function make(DOMNode $node): ?Element
34+
public function make(Node $node): ?Element
3635
{
3736
return $this->makeElement(
3837
node: $node,
3938
parent: null,
4039
);
4140
}
4241

43-
private function makeElement(DOMNode $node, ?Element $parent): ?Element
42+
private function makeElement(Node $node, ?Element $parent): ?Element
4443
{
45-
if ($node instanceof DOMText) {
44+
if ($node instanceof Text) {
4645
if (trim($node->textContent) === '') {
4746
return null;
4847
}
@@ -52,31 +51,34 @@ private function makeElement(DOMNode $node, ?Element $parent): ?Element
5251
);
5352
}
5453

54+
$tagName = $node->tagName ? strtolower($node->tagName) : null;
55+
5556
$attributes = [];
5657

57-
/** @var DOMAttr $attribute */
58+
/** @var \Dom\Attr $attribute */
5859
foreach ($node->attributes ?? [] as $attribute) {
5960
$name = str($attribute->name)->camel()->toString();
6061

6162
$attributes[$name] = $attribute->value;
6263
}
6364

64-
if (! $node instanceof DOMElement
65-
|| $node->tagName === 'pre'
66-
|| $node->tagName === 'code') {
65+
if (! $node instanceof DomElement
66+
|| $tagName === 'pre'
67+
|| $tagName === 'code') {
6768
$content = '';
69+
6870
foreach ($node->childNodes as $child) {
6971
$content .= $node->ownerDocument->saveHTML($child);
7072
}
7173

7274
return new RawElement(
73-
tag: $node->tagName ?? null,
75+
tag: $tagName,
7476
content: $content,
7577
attributes: $attributes,
7678
);
7779
}
7880

79-
if ($viewComponentClass = $this->viewConfig->viewComponents[$node->tagName] ?? null) {
81+
if ($viewComponentClass = $this->viewConfig->viewComponents[$tagName] ?? null) {
8082
if (! $viewComponentClass instanceof ViewComponent) {
8183
$viewComponentClass = $this->container->get($viewComponentClass);
8284
}
@@ -86,13 +88,13 @@ private function makeElement(DOMNode $node, ?Element $parent): ?Element
8688
$viewComponentClass,
8789
$attributes,
8890
);
89-
} elseif ($node->tagName === 'x-slot') {
91+
} elseif ($tagName === 'x-slot') {
9092
$element = new SlotElement(
9193
name: $node->getAttribute('name') ?: 'slot',
9294
);
9395
} else {
9496
$element = new GenericElement(
95-
tag: $node->tagName,
97+
tag: $tagName,
9698
attributes: $attributes,
9799
);
98100
}

src/Tempest/View/src/Renderers/TempestViewCompiler.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace Tempest\View\Renderers;
66

7-
use DOMNodeList;
7+
use const Dom\HTML_NO_DEFAULT_NS;
8+
use Dom\HTMLDocument;
9+
use Dom\NodeList;
810
use Exception;
9-
use Masterminds\HTML5;
1011
use Tempest\Core\Kernel;
1112
use function Tempest\path;
1213
use Tempest\View\Attributes\AttributeFactory;
@@ -78,29 +79,27 @@ private function retrieveTemplate(string $path): string
7879
return file_get_contents($searchPath);
7980
}
8081

81-
private function parseDom(string $template): DOMNodeList
82+
private function parseDom(string $template): NodeList
8283
{
8384
$template = str_replace(
8485
search: array_keys(self::TOKEN_MAPPING),
8586
replace: array_values(self::TOKEN_MAPPING),
8687
subject: $template,
8788
);
8889

89-
$html5 = new HTML5();
90-
91-
$dom = $html5->loadHTML("<div id='tempest_render'>{$template}</div>");
90+
$dom = HTMLDocument::createFromString("<div id='tempest_render'>{$template}</div>", LIBXML_NOERROR | HTML_NO_DEFAULT_NS);
9291

9392
return $dom->getElementById('tempest_render')->childNodes;
9493
}
9594

9695
/**
9796
* @return Element[]
9897
*/
99-
private function mapToElements(DOMNodeList $domNodeList): array
98+
private function mapToElements(NodeList $nodeList): array
10099
{
101100
$elements = [];
102101

103-
foreach ($domNodeList as $node) {
102+
foreach ($nodeList as $node) {
104103
$element = $this->elementFactory->make($node);
105104

106105
if ($element === null) {
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
<x-view-component-with-camelcase-attribute-a meta-type="test" />
2-
<x-view-component-with-camelcase-attribute-a meta_type="test" />
1+
<x-view-component-with-camelcase-attribute-a meta-type="test"></x-view-component-with-camelcase-attribute-a>
2+
<x-view-component-with-camelcase-attribute-a meta_type="test"></x-view-component-with-camelcase-attribute-a>

tests/Integration/View/ElementFactoryTest.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
namespace Tests\Tempest\Integration\View;
66

7-
use Masterminds\HTML5;
7+
use const Dom\HTML_NO_DEFAULT_NS;
8+
use Dom\HTMLDocument;
89
use Tempest\View\Elements\ElementFactory;
910
use Tempest\View\Elements\GenericElement;
1011
use Tempest\View\Elements\TextElement;
@@ -17,20 +18,20 @@ final class ElementFactoryTest extends FrameworkIntegrationTestCase
1718
{
1819
public function test_parental_relations(): void
1920
{
21+
// See https://github.com/php/php-src/issues/16960
2022
$contents = <<<'HTML'
2123
<a>
2224
<b>
2325
<c>
2426
Hello
2527
</c>
26-
<d />
27-
<e />
28+
<d></d>
29+
<e></e>
2830
</b>
2931
</a>
3032
HTML;
3133

32-
$html5 = new HTML5();
33-
$dom = $html5->loadHTML("<div id='tempest_render'>{$contents}</div>");
34+
$dom = HTMLDocument::createFromString("<div id='tempest_render'>{$contents}</div>", LIBXML_NOERROR | HTML_NO_DEFAULT_NS);
3435

3536
$elementFactory = $this->container->get(ElementFactory::class);
3637

tests/Integration/View/TempestViewRendererTest.php

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,14 @@ public function test_foreach_consumes_attribute(): void
135135

136136
$this->assertStringContainsStringIgnoringLineEndings(
137137
<<<'HTML'
138-
<html lang="en"><head><title>Home</title></head><body><table><tr><td>a</td></tr>
139-
<tr><td>b</td></tr>
140-
</table></body></html>
141-
HTML,
138+
<title>Home</title>
139+
140+
141+
142+
143+
144+
<table><tbody><tr><td>b</td></tr></tbody></table>
145+
HTML,
142146
$html
143147
);
144148
}
@@ -249,16 +253,14 @@ public function test_pre(): void
249253
{
250254
$this->assertStringEqualsStringIgnoringLineEndings(
251255
<<<'HTML'
252-
<pre>
253-
a
256+
<pre>a
254257
<span class="hl-prop">b</span>
255258
<span class="hl-type">c</span>
256259
</pre>
257260
HTML,
258261
$this->render(
259262
<<<'HTML'
260-
<pre>
261-
a
263+
<pre>a
262264
<span class="hl-prop">b</span>
263265
<span class="hl-type">c</span>
264266
</pre>

0 commit comments

Comments
 (0)