• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tempestphp / tempest-framework / 14279037618

05 Apr 2025 05:58AM UTC coverage: 81.145% (+0.2%) from 80.906%
14279037618

Pull #1115

github

web-flow
Merge 64b2d83c6 into 90e820853
Pull Request #1115: refactor(view): implement custom html parser

275 of 292 new or added lines in 13 files covered. (94.18%)

10 existing lines in 5 files now uncovered.

11349 of 13986 relevant lines covered (81.15%)

104.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.72
/src/Tempest/View/src/Elements/ElementFactory.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\View\Elements;
6

7
use Dom\Comment;
8
use Dom\DocumentType;
9
use Dom\Element as DomElement;
10
use Dom\Text;
11
use Tempest\Container\Container;
12
use Tempest\Core\AppConfig;
13
use Tempest\View\Attributes\PhpAttribute;
14
use Tempest\View\Element;
15
use Tempest\View\Parser\Token;
16
use Tempest\View\Parser\TokenType;
17
use Tempest\View\Renderers\TempestViewCompiler;
18
use Tempest\View\ViewComponent;
19
use Tempest\View\ViewConfig;
20

21
use function Tempest\Support\str;
22

23
final class ElementFactory
24
{
25
    private TempestViewCompiler $compiler;
26

27
    public function __construct(
128✔
28
        private readonly AppConfig $appConfig,
29
        private readonly ViewConfig $viewConfig,
30
        private readonly Container $container,
31
    ) {}
128✔
32

33
    public function setViewCompiler(TempestViewCompiler $compiler): self
121✔
34
    {
35
        $this->compiler = $compiler;
121✔
36

37
        return $this;
121✔
38
    }
39

40
    public function make(Token $token): ?Element
122✔
41
    {
42
        return $this->makeElement(
122✔
43
            token: $token,
122✔
44
            parent: null,
122✔
45
        );
122✔
46
    }
47

48
    private function makeElement(Token $token, ?Element $parent): ?Element
122✔
49
    {
50
        if (
51
            $token->type === TokenType::OPEN_TAG_END ||
122✔
52
                $token->type === TokenType::ATTRIBUTE_NAME ||
122✔
53
                $token->type === TokenType::ATTRIBUTE_VALUE ||
122✔
54
                $token->type === TokenType::SELF_CLOSING_TAG_END
122✔
55
        ) {
NEW
56
            return null;
×
57
        }
58

59
        if ($token->type === TokenType::CONTENT) {
122✔
60
            $text = $token->compile();
97✔
61

62
            if (trim($text) === '') {
97✔
63
                return null;
59✔
64
            }
65

66
            return new TextElement(text: $text);
74✔
67
        }
68

69
        if (! $token->tag || $token->type === TokenType::COMMENT || $token->type === TokenType::PHP) {
114✔
70
            return new RawElement(tag: null, content: $token->compile());
74✔
71
        }
72

73
        $attributes = [];
113✔
74

75
        foreach ($token->htmlAttributes as $name => $value) {
113✔
76
            $name = str($name)
98✔
77
                ->trim()
98✔
78
                ->before('=')
98✔
79
                ->camel()
98✔
80
                ->toString();
98✔
81

82
            $value = str($value)
98✔
83
                ->afterFirst('"')
98✔
84
                ->beforeLast('"')
98✔
85
                ->toString();
98✔
86

87
            $attributes[$name] = $value;
98✔
88
        }
89

90
        foreach ($token->phpAttributes as $index => $content) {
113✔
91
            $attributes[] = new PhpAttribute((string) $index, $content);
1✔
92
        }
93

94
        if ($token->tag === 'code' || $token->tag === 'pre') {
113✔
95
            return new RawElement(
2✔
96
                tag: $token->tag,
2✔
97
                content: $token->compileChildren(),
2✔
98
                attributes: $attributes,
2✔
99
            );
2✔
100
        }
101

102
        if ($viewComponentClass = $this->viewConfig->viewComponents[$token->tag] ?? null) {
112✔
103
            if (! ($viewComponentClass instanceof ViewComponent)) {
71✔
104
                $viewComponentClass = $this->container->get($viewComponentClass);
28✔
105
            }
106

107
            $element = new ViewComponentElement(
71✔
108
                environment: $this->appConfig->environment,
71✔
109
                compiler: $this->compiler,
71✔
110
                viewComponent: $viewComponentClass,
71✔
111
                attributes: $attributes,
71✔
112
            );
71✔
113
        } elseif ($token->tag === 'x-template') {
103✔
114
            $element = new TemplateElement(
1✔
115
                attributes: $attributes,
1✔
116
            );
1✔
117
        } elseif ($token->tag === 'x-slot') {
103✔
118
            $element = new SlotElement(
6✔
119
                name: $token->getAttribute('name') ?? 'slot',
6✔
120
                attributes: $attributes,
6✔
121
            );
6✔
122
        } else {
123
            $element = new GenericElement(
102✔
124
                tag: $token->tag,
102✔
125
                attributes: $attributes,
102✔
126
            );
102✔
127
        }
128

129
        $children = [];
112✔
130

131
        foreach ($token->children as $child) {
112✔
132
            $childElement = $this->clone()->makeElement(
78✔
133
                token: $child,
78✔
134
                parent: $parent,
78✔
135
            );
78✔
136

137
            if ($childElement === null) {
78✔
138
                continue;
38✔
139
            }
140

141
            $children[] = $childElement;
78✔
142
        }
143

144
        $element->setChildren($children);
112✔
145

146
        return $element;
112✔
147
    }
148

149
    private function clone(): self
78✔
150
    {
151
        return clone $this;
78✔
152
    }
153
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc