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

tempestphp / tempest-framework / 11292226413

11 Oct 2024 12:21PM UTC coverage: 82.134%. First build
11292226413

Pull #560

github

web-flow
Merge 5007f5fd5 into 571879663
Pull Request #560: chore: refactor view engine

429 of 471 new or added lines in 34 files covered. (91.08%)

6758 of 8228 relevant lines covered (82.13%)

38.48 hits per line

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

95.52
/src/Tempest/View/src/Renderers/TempestViewCompiler.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Tempest\View\Renderers;
6

7
use DOMNodeList;
8
use Exception;
9
use Masterminds\HTML5;
10
use Tempest\Core\Kernel;
11
use function Tempest\path;
12
use Tempest\View\Attributes\AttributeFactory;
13
use Tempest\View\Element;
14
use Tempest\View\Elements\ElementFactory;
15

16
final readonly class TempestViewCompiler
17
{
18
    public const string TOKEN_PHP_OPEN = '__TOKEN_PHP_OPEN__';
19

20
    public const string TOKEN_PHP_SHORT_ECHO = '__TOKEN_PHP_SHORT_ECHO__';
21

22
    public const string TOKEN_PHP_CLOSE = '__TOKEN_PHP_CLOSE__';
23

24
    public const array TOKEN_MAPPING = [
25
        '<?php' => self::TOKEN_PHP_OPEN,
26
        '<?=' => self::TOKEN_PHP_SHORT_ECHO,
27
        '?>' => self::TOKEN_PHP_CLOSE,
28
    ];
29

30
    public function __construct(
54✔
31
        private ElementFactory $elementFactory,
32
        private AttributeFactory $attributeFactory,
33
        private Kernel $kernel,
34
    ) {
35
    }
54✔
36

37
    public function compile(string $path): string
48✔
38
    {
39
        // 1. Retrieve template
40
        $template = $this->retrieveTemplate($path);
48✔
41

42
        // 2. Parse as DOM
43
        $dom = $this->parseDom($template);
48✔
44

45
        // 3. Map to elements
46
        $elements = $this->mapToElements($dom);
48✔
47

48
        // 4. Apply attributes
49
        $elements = $this->applyAttributes($elements);
48✔
50

51
        // 5. Compile to PHP
52
        $compiled = $this->compileElements($elements);
47✔
53

54
        return $compiled;
47✔
55
    }
56

57
    private function retrieveTemplate(string $path): string
48✔
58
    {
59
        if (! str_ends_with($path, '.php')) {
48✔
60
            return $path;
45✔
61
        }
62

63
        $discoveryLocations = $this->kernel->discoveryLocations;
12✔
64

65
        $searchPath = $path;
12✔
66

67
        while (! file_exists($searchPath) && $location = current($discoveryLocations)) {
12✔
NEW
68
            $searchPath = path($location->path, $path);
×
NEW
69
            next($discoveryLocations);
×
70
        }
71

72
        if (! file_exists($searchPath)) {
12✔
NEW
73
            throw new Exception("View {$searchPath} not found");
×
74
        }
75

76
        return file_get_contents($searchPath);
12✔
77
    }
78

79
    private function parseDom(string $template): DOMNodeList
48✔
80
    {
81
        $template = str_replace(
48✔
82
            search: array_keys(self::TOKEN_MAPPING),
48✔
83
            replace: array_values(self::TOKEN_MAPPING),
48✔
84
            subject: $template,
48✔
85
        );
48✔
86

87
        $html5 = new HTML5();
48✔
88

89
        $dom = $html5->loadHTML("<div id='tempest_render'>{$template}</div>");
48✔
90

91
        return $dom->getElementById('tempest_render')->childNodes;
48✔
92
    }
93

94
    /**
95
     * @return Element[]
96
     */
97
    private function mapToElements(DOMNodeList $domNodeList): array
48✔
98
    {
99
        $elements = [];
48✔
100

101
        foreach ($domNodeList as $node) {
48✔
102
            $element = $this->elementFactory
48✔
103
                ->setViewCompiler($this)
48✔
104
                ->make($node);
48✔
105

106
            if ($element === null) {
48✔
107
                continue;
18✔
108
            }
109

110
            $elements[] = $element;
48✔
111
        }
112

113
        return $elements;
48✔
114
    }
115

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

124
        $previous = null;
48✔
125

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

129
            $element
48✔
130
                ->setPrevious($previous)
48✔
131
                ->setChildren($children);
48✔
132

133
            foreach ($element->getAttributes() as $name => $value) {
48✔
134
                $attribute = $this->attributeFactory->make($name);
34✔
135

136
                $element = $attribute->apply($element);
34✔
137

138
                if ($element === null) {
34✔
139
                    break;
9✔
140
                }
141
            }
142

143
            if ($element === null) {
48✔
144
                continue;
9✔
145
            }
146

147
            $appliedElements[] = $element;
48✔
148

149
            $previous = $element;
48✔
150
        }
151

152
        return $appliedElements;
48✔
153
    }
154

155
    /** @param \Tempest\View\Element[] $elements */
156
    private function compileElements(array $elements): string
47✔
157
    {
158
        $compiled = [];
47✔
159

160
        foreach ($elements as $element) {
47✔
161
            $compiled[] = $element->compile();
47✔
162
        }
163

164
        $compiled = implode(PHP_EOL, $compiled);
47✔
165

166
        return str_replace(
47✔
167
            search: array_values(self::TOKEN_MAPPING),
47✔
168
            replace: array_keys(self::TOKEN_MAPPING),
47✔
169
            subject: $compiled,
47✔
170
        );
47✔
171
    }
172
}
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

© 2025 Coveralls, Inc