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

tempestphp / tempest-framework / 14280422660

05 Apr 2025 09:06AM UTC coverage: 81.139% (+0.2%) from 80.906%
14280422660

Pull #1115

github

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

379 of 402 new or added lines in 16 files covered. (94.28%)

6 existing lines in 2 files now uncovered.

11366 of 14008 relevant lines covered (81.14%)

104.77 hits per line

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

98.55
/src/Tempest/View/src/Parser/TempestViewCompiler.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\View\Parser;
6

7
use Tempest\Core\Kernel;
8
use Tempest\Discovery\DiscoveryLocation;
9
use Tempest\Mapper\Exceptions\ViewNotFound;
10
use Tempest\View\Attribute;
11
use Tempest\View\Attributes\AttributeFactory;
12
use Tempest\View\Element;
13
use Tempest\View\Elements\ElementFactory;
14
use Tempest\View\View;
15
use function Tempest\Support\arr;
16
use function Tempest\Support\path;
17

18
final readonly class TempestViewCompiler
19
{
20
    public const array PHP_TOKENS = [
21
        '<?php',
22
        '<?=',
23
        '?>',
24
    ];
25

26
    public function __construct(
127✔
27
        private ElementFactory $elementFactory,
28
        private AttributeFactory $attributeFactory,
29
        private Kernel $kernel,
30
    ) {}
127✔
31

32
    public function compile(string|View $view): string
121✔
33
    {
34
        $this->elementFactory->setViewCompiler($this);
121✔
35

36
        // 1. Retrieve template
37
        $template = $this->retrieveTemplate($view);
121✔
38

39
        // 2. Parse AST
40
        $ast = $this->parseAst($template);
121✔
41

42
        // 3. Map to elements
43
        $elements = $this->mapToElements($ast);
121✔
44

45
        // 4. Apply attributes
46
        $elements = $this->applyAttributes($elements);
121✔
47

48
        // 5. Compile to PHP
49
        $compiled = $this->compileElements($elements);
115✔
50

51
        return $compiled;
115✔
52
    }
53

54
    private function retrieveTemplate(string|View $view): string
121✔
55
    {
56
        $path = ($view instanceof View) ? $view->path : $view;
121✔
57

58
        if (! str_ends_with($path, '.php')) {
121✔
59
            return $path;
111✔
60
        }
61

62
        $searchPathOptions = [
18✔
63
            $path,
18✔
64
        ];
18✔
65

66
        if ($view instanceof View && $view->relativeRootPath !== null) {
18✔
67
            $searchPathOptions[] = path($view->relativeRootPath, $path)->toString();
15✔
68
        }
69

70
        $searchPathOptions = [
18✔
71
            ...$searchPathOptions,
18✔
72
            ...arr($this->kernel->discoveryLocations)
18✔
73
                ->map(fn (DiscoveryLocation $discoveryLocation) => path($discoveryLocation->path, $path)->toString())
18✔
74
                ->toArray(),
18✔
75
        ];
18✔
76

77
        foreach ($searchPathOptions as $searchPath) {
18✔
78
            if (file_exists($searchPath)) {
18✔
79
                break;
18✔
80
            }
81
        }
82

83
        if (! file_exists($searchPath)) {
18✔
NEW
84
            throw new ViewNotFound($path);
×
85
        }
86

87
        return file_get_contents($searchPath);
18✔
88
    }
89

90
    private function parseAst(string $template): TempestViewAst
121✔
91
    {
92
        $tokens = new TempestViewLexer($template)->lex();
121✔
93

94
        return new TempestViewParser($tokens)->parse();
121✔
95
    }
96

97
    /**
98
     * @return Element[]
99
     */
100
    private function mapToElements(TempestViewAst $ast): array
121✔
101
    {
102
        $elements = [];
121✔
103

104
        foreach ($ast as $token) {
121✔
105
            $element = $this->elementFactory->make($token);
121✔
106

107
            if ($element === null) {
121✔
108
                continue;
46✔
109
            }
110

111
            $elements[] = $element;
121✔
112
        }
113

114
        return $elements;
121✔
115
    }
116

117
    /**
118
     * @param Element[] $elements
119
     * @return Element[]
120
     */
121
    private function applyAttributes(array $elements): array
121✔
122
    {
123
        $appliedElements = [];
121✔
124

125
        $previous = null;
121✔
126

127
        foreach ($elements as $element) {
121✔
128
            $children = $this->applyAttributes($element->getChildren());
121✔
129

130
            $element
121✔
131
                ->setPrevious($previous)
121✔
132
                ->setChildren($children);
121✔
133

134
            foreach ($element->getAttributes() as $name => $value) {
121✔
135
                // TODO: possibly refactor attribute construction to ElementFactory?
136
                if ($value instanceof Attribute) {
98✔
137
                    $attribute = $value;
1✔
138
                } else {
139
                    $attribute = $this->attributeFactory->make($name);
98✔
140
                }
141

142
                $element = $attribute->apply($element);
98✔
143

144
                if ($element === null) {
93✔
145
                    break;
13✔
146
                }
147
            }
148

149
            if ($element === null) {
121✔
150
                continue;
13✔
151
            }
152

153
            $appliedElements[] = $element;
121✔
154

155
            $previous = $element;
121✔
156
        }
157

158
        return $appliedElements;
121✔
159
    }
160

161
    /** @param \Tempest\View\Element[] $elements */
162
    private function compileElements(array $elements): string
115✔
163
    {
164
        $compiled = arr();
115✔
165

166
        foreach ($elements as $element) {
115✔
167
            $compiled[] = $element->compile();
115✔
168
        }
169

170
        return $compiled
115✔
171
            ->implode(PHP_EOL)
115✔
172
            ->toString();
115✔
173
    }
174
}
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