@vituum/vite-plugin-latte 1.0.0-beta.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +2 -2
- package/package.json +13 -11
- package/types/index.d.ts +2 -0
- package/vendor/autoload.php +19 -1
- package/vendor/bin/latte-lint +16 -4
- package/vendor/composer/ClassLoader.php +72 -65
- package/vendor/composer/InstalledVersions.php +21 -12
- package/vendor/composer/autoload_classmap.php +12 -8
- package/vendor/composer/autoload_namespaces.php +1 -1
- package/vendor/composer/autoload_psr4.php +1 -1
- package/vendor/composer/autoload_real.php +7 -26
- package/vendor/composer/autoload_static.php +13 -9
- package/vendor/composer/installed.json +8 -8
- package/vendor/composer/installed.php +10 -10
- package/vendor/latte/latte/bin/latte-lint +8 -2
- package/vendor/latte/latte/composer.json +1 -1
- package/vendor/latte/latte/readme.md +6 -6
- package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +1 -0
- package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +3 -2
- package/vendor/latte/latte/src/Latte/Compiler/Block.php +0 -3
- package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +103 -88
- package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +2 -1
- package/vendor/latte/latte/src/Latte/Compiler/Node.php +0 -4
- package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +0 -4
- package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +0 -4
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +11 -3
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +10 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +22 -4
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +35 -12
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +6 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArrayItemNode.php +12 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +4 -31
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +15 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +11 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AuxiliaryNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +2 -2
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +8 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +15 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +11 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +11 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/{StaticCallNode.php → StaticMethodCallNode.php} +11 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/{StaticCallableNode.php → StaticMethodCallableNode.php} +11 -1
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TemporaryNode.php +38 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +24 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListItemNode.php +48 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ListNode.php +56 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +3 -3
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/InterpolatedStringNode.php +1 -8
- package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +30 -0
- package/vendor/latte/latte/src/Latte/Compiler/Position.php +4 -8
- package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +14 -7
- package/vendor/latte/latte/src/Latte/Compiler/Tag.php +13 -14
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +3 -7
- package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +52 -3
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +354 -322
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +6 -5
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +95 -176
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +40 -33
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +182 -126
- package/vendor/latte/latte/src/Latte/Compiler/Token.php +5 -9
- package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +6 -22
- package/vendor/latte/latte/src/Latte/Engine.php +37 -8
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +0 -2
- package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +0 -4
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +14 -3
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +8 -6
- package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +1 -2
- package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +2 -13
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +1 -2
- package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +0 -3
- package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +4 -13
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +4 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +5 -3
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +5 -2
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +6 -4
- package/vendor/latte/latte/src/Latte/Essential/Nodes/JumpNode.php +26 -23
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NElseNode.php +88 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +20 -28
- package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +7 -12
- package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +3 -4
- package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +9 -11
- package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +0 -2
- package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +6 -1
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +0 -2
- package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +0 -2
- package/vendor/latte/latte/src/Latte/PositionAwareException.php +1 -1
- package/vendor/latte/latte/src/Latte/Runtime/Block.php +0 -4
- package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +43 -51
- package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +0 -2
- package/vendor/latte/latte/src/Latte/Runtime/Filters.php +64 -30
- package/vendor/latte/latte/src/Latte/Runtime/Html.php +0 -4
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +2 -2
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +2 -1
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +3 -3
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/{StaticCallNode.php → StaticMethodCallNode.php} +3 -3
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/{StaticCallableNode.php → StaticMethodCallableNode.php} +2 -2
- package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +0 -2
- package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +11 -9
- package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +0 -2
- package/vendor/latte/latte/src/Latte/exceptions.php +2 -11
- package/vendor/latte/latte/src/Tools/Linter.php +13 -37
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +0 -53
- package/vendor/latte/latte/src/Latte/Strict.php +0 -101
|
@@ -9,7 +9,6 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Compiler;
|
|
11
11
|
|
|
12
|
-
use Latte;
|
|
13
12
|
use Latte\Compiler\Nodes\Html\ElementNode;
|
|
14
13
|
use Latte\ContentType;
|
|
15
14
|
use Latte\Runtime\Filters;
|
|
@@ -20,8 +19,6 @@ use Latte\Runtime\Filters;
|
|
|
20
19
|
*/
|
|
21
20
|
final class Escaper
|
|
22
21
|
{
|
|
23
|
-
use Latte\Strict;
|
|
24
|
-
|
|
25
22
|
public const
|
|
26
23
|
Text = 'text',
|
|
27
24
|
JavaScript = 'js',
|
|
@@ -33,14 +30,54 @@ final class Escaper
|
|
|
33
30
|
HtmlText = 'html',
|
|
34
31
|
HtmlComment = 'html/comment',
|
|
35
32
|
HtmlBogusTag = 'html/bogus',
|
|
36
|
-
|
|
37
|
-
HtmlJavaScript = 'html/js',
|
|
33
|
+
HtmlRawText = 'html/raw',
|
|
38
34
|
HtmlTag = 'html/tag',
|
|
39
35
|
HtmlAttribute = 'html/attr';
|
|
40
36
|
|
|
37
|
+
private const Convertors = [
|
|
38
|
+
self::Text => [
|
|
39
|
+
self::HtmlText => 'escapeHtmlText',
|
|
40
|
+
self::HtmlAttribute => 'escapeHtmlAttr',
|
|
41
|
+
self::HtmlAttribute . '/' . self::JavaScript => 'escapeHtmlAttr',
|
|
42
|
+
self::HtmlAttribute . '/' . self::Css => 'escapeHtmlAttr',
|
|
43
|
+
self::HtmlAttribute . '/' . self::Url => 'escapeHtmlAttr',
|
|
44
|
+
self::HtmlComment => 'escapeHtmlComment',
|
|
45
|
+
'xml' => 'escapeXml',
|
|
46
|
+
'xml/attr' => 'escapeXml',
|
|
47
|
+
],
|
|
48
|
+
self::JavaScript => [
|
|
49
|
+
self::HtmlText => 'escapeHtmlText',
|
|
50
|
+
self::HtmlAttribute => 'escapeHtmlAttr',
|
|
51
|
+
self::HtmlAttribute . '/' . self::JavaScript => 'escapeHtmlAttr',
|
|
52
|
+
self::HtmlRawText . '/' . self::JavaScript => 'convertJSToHtmlRawText',
|
|
53
|
+
self::HtmlComment => 'escapeHtmlComment',
|
|
54
|
+
],
|
|
55
|
+
self::Css => [
|
|
56
|
+
self::HtmlText => 'escapeHtmlText',
|
|
57
|
+
self::HtmlAttribute => 'escapeHtmlAttr',
|
|
58
|
+
self::HtmlAttribute . '/' . self::Css => 'escapeHtmlAttr',
|
|
59
|
+
self::HtmlRawText . '/' . self::Css => 'convertJSToHtmlRawText',
|
|
60
|
+
self::HtmlComment => 'escapeHtmlComment',
|
|
61
|
+
],
|
|
62
|
+
self::HtmlText => [
|
|
63
|
+
self::HtmlAttribute => 'convertHtmlToHtmlAttr',
|
|
64
|
+
self::HtmlAttribute . '/' . self::JavaScript => 'convertHtmlToHtmlAttr',
|
|
65
|
+
self::HtmlAttribute . '/' . self::Css => 'convertHtmlToHtmlAttr',
|
|
66
|
+
self::HtmlAttribute . '/' . self::Url => 'convertHtmlToHtmlAttr',
|
|
67
|
+
self::HtmlComment => 'escapeHtmlComment',
|
|
68
|
+
self::HtmlRawText . '/' . self::HtmlText => 'convertHtmlToHtmlRawText',
|
|
69
|
+
],
|
|
70
|
+
self::HtmlAttribute => [
|
|
71
|
+
self::HtmlText => 'convertHtmlToHtmlAttr',
|
|
72
|
+
],
|
|
73
|
+
self::HtmlAttribute . '/' . self::Url => [
|
|
74
|
+
self::HtmlText => 'convertHtmlToHtmlAttr',
|
|
75
|
+
self::HtmlAttribute => 'nop',
|
|
76
|
+
],
|
|
77
|
+
];
|
|
78
|
+
|
|
41
79
|
private string $state = '';
|
|
42
80
|
private string $tag = '';
|
|
43
|
-
private string $quote = '';
|
|
44
81
|
private string $subType = '';
|
|
45
82
|
|
|
46
83
|
|
|
@@ -65,8 +102,7 @@ final class Escaper
|
|
|
65
102
|
|
|
66
103
|
public function export(): string
|
|
67
104
|
{
|
|
68
|
-
return
|
|
69
|
-
. ($this->subType ? '/' . $this->subType : '');
|
|
105
|
+
return $this->state . ($this->subType ? '/' . $this->subType : '');
|
|
70
106
|
}
|
|
71
107
|
|
|
72
108
|
|
|
@@ -76,20 +112,29 @@ final class Escaper
|
|
|
76
112
|
}
|
|
77
113
|
|
|
78
114
|
|
|
79
|
-
public function enterHtmlText(
|
|
115
|
+
public function enterHtmlText(ElementNode $el): void
|
|
80
116
|
{
|
|
81
|
-
$
|
|
82
|
-
|
|
83
|
-
$
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
117
|
+
if ($el->isRawText()) {
|
|
118
|
+
$this->state = self::HtmlRawText;
|
|
119
|
+
$this->subType = self::Text;
|
|
120
|
+
if ($el->is('script')) {
|
|
121
|
+
$type = $el->getAttribute('type');
|
|
122
|
+
if ($type === true || $type === null
|
|
123
|
+
|| is_string($type) && preg_match('#((application|text)/(((x-)?java|ecma|j|live)script|json)|text/plain|module|importmap|)$#Ai', $type)
|
|
124
|
+
) {
|
|
125
|
+
$this->subType = self::JavaScript;
|
|
126
|
+
|
|
127
|
+
} elseif (is_string($type) && preg_match('#text/((x-)?template|html)$#Ai', $type)) {
|
|
128
|
+
$this->subType = self::HtmlText;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
} elseif ($el->is('style')) {
|
|
132
|
+
$this->subType = self::Css;
|
|
92
133
|
}
|
|
134
|
+
|
|
135
|
+
} else {
|
|
136
|
+
$this->state = self::HtmlText;
|
|
137
|
+
$this->subType = '';
|
|
93
138
|
}
|
|
94
139
|
}
|
|
95
140
|
|
|
@@ -101,10 +146,9 @@ final class Escaper
|
|
|
101
146
|
}
|
|
102
147
|
|
|
103
148
|
|
|
104
|
-
public function enterHtmlAttribute(?string $name = null
|
|
149
|
+
public function enterHtmlAttribute(?string $name = null): void
|
|
105
150
|
{
|
|
106
151
|
$this->state = self::HtmlAttribute;
|
|
107
|
-
$this->quote = $quote;
|
|
108
152
|
$this->subType = '';
|
|
109
153
|
|
|
110
154
|
if ($this->contentType === ContentType::Html && is_string($name)) {
|
|
@@ -122,12 +166,6 @@ final class Escaper
|
|
|
122
166
|
}
|
|
123
167
|
|
|
124
168
|
|
|
125
|
-
public function enterHtmlAttributeQuote(string $quote = '"'): void
|
|
126
|
-
{
|
|
127
|
-
$this->quote = $quote;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
169
|
public function enterHtmlBogusTag(): void
|
|
132
170
|
{
|
|
133
171
|
$this->state = self::HtmlBogusTag;
|
|
@@ -142,27 +180,30 @@ final class Escaper
|
|
|
142
180
|
|
|
143
181
|
public function escape(string $str): string
|
|
144
182
|
{
|
|
145
|
-
[$lq, $rq] = $this->state === self::HtmlAttribute && !$this->quote ? ["'\"' . ", " . '\"'"] : ['', ''];
|
|
146
183
|
return match ($this->contentType) {
|
|
147
184
|
ContentType::Html => match ($this->state) {
|
|
148
185
|
self::HtmlText => 'LR\Filters::escapeHtmlText(' . $str . ')',
|
|
149
186
|
self::HtmlTag => 'LR\Filters::escapeHtmlTag(' . $str . ')',
|
|
150
187
|
self::HtmlAttribute => match ($this->subType) {
|
|
151
188
|
'',
|
|
152
|
-
self::Url =>
|
|
153
|
-
self::JavaScript =>
|
|
154
|
-
self::Css =>
|
|
189
|
+
self::Url => 'LR\Filters::escapeHtmlAttr(' . $str . ')',
|
|
190
|
+
self::JavaScript => 'LR\Filters::escapeHtmlAttr(LR\Filters::escapeJs(' . $str . '))',
|
|
191
|
+
self::Css => 'LR\Filters::escapeHtmlAttr(LR\Filters::escapeCss(' . $str . '))',
|
|
155
192
|
},
|
|
156
193
|
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
157
194
|
self::HtmlBogusTag => 'LR\Filters::escapeHtml(' . $str . ')',
|
|
158
|
-
self::
|
|
159
|
-
|
|
195
|
+
self::HtmlRawText => match ($this->subType) {
|
|
196
|
+
self::Text => 'LR\Filters::convertJSToHtmlRawText(' . $str . ')', // sanitization, escaping is not possible
|
|
197
|
+
self::HtmlText => 'LR\Filters::escapeHtmlRawTextHtml(' . $str . ')',
|
|
198
|
+
self::JavaScript => 'LR\Filters::escapeJs(' . $str . ')',
|
|
199
|
+
self::Css => 'LR\Filters::escapeCss(' . $str . ')',
|
|
200
|
+
},
|
|
160
201
|
default => throw new \LogicException("Unknown context $this->contentType, $this->state."),
|
|
161
202
|
},
|
|
162
203
|
ContentType::Xml => match ($this->state) {
|
|
163
204
|
self::HtmlText,
|
|
164
205
|
self::HtmlBogusTag => 'LR\Filters::escapeXml(' . $str . ')',
|
|
165
|
-
self::HtmlAttribute =>
|
|
206
|
+
self::HtmlAttribute => 'LR\Filters::escapeXml(' . $str . ')',
|
|
166
207
|
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
167
208
|
self::HtmlTag => 'LR\Filters::escapeXmlTag(' . $str . ')',
|
|
168
209
|
default => throw new \LogicException("Unknown context $this->contentType, $this->state."),
|
|
@@ -176,6 +217,28 @@ final class Escaper
|
|
|
176
217
|
}
|
|
177
218
|
|
|
178
219
|
|
|
220
|
+
public function escapeMandatory(string $str): string
|
|
221
|
+
{
|
|
222
|
+
return match ($this->contentType) {
|
|
223
|
+
ContentType::Html => match ($this->state) {
|
|
224
|
+
self::HtmlAttribute => "LR\\Filters::escapeHtmlQuotes($str)",
|
|
225
|
+
self::HtmlRawText => match ($this->subType) {
|
|
226
|
+
self::HtmlText => 'LR\Filters::convertHtmlToHtmlRawText(' . $str . ')',
|
|
227
|
+
default => "LR\\Filters::convertJSToHtmlRawText($str)",
|
|
228
|
+
},
|
|
229
|
+
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
230
|
+
default => $str,
|
|
231
|
+
},
|
|
232
|
+
ContentType::Xml => match ($this->state) {
|
|
233
|
+
self::HtmlAttribute => "LR\\Filters::escapeHtmlQuotes($str)",
|
|
234
|
+
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
235
|
+
default => $str,
|
|
236
|
+
},
|
|
237
|
+
default => $str,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
|
|
179
242
|
public function check(string $str): string
|
|
180
243
|
{
|
|
181
244
|
if ($this->state === self::HtmlAttribute && $this->subType === self::Url) {
|
|
@@ -187,58 +250,10 @@ final class Escaper
|
|
|
187
250
|
|
|
188
251
|
public static function getConvertor(string $source, string $dest): ?callable
|
|
189
252
|
{
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
'html/attr/css' => 'escapeHtmlAttr',
|
|
196
|
-
'html/attr/url' => 'escapeHtmlAttr',
|
|
197
|
-
'html/comment' => 'escapeHtmlComment',
|
|
198
|
-
'xml' => 'escapeXml',
|
|
199
|
-
'xml/attr' => 'escapeXml',
|
|
200
|
-
],
|
|
201
|
-
self::JavaScript => [
|
|
202
|
-
'html' => 'escapeHtmlText',
|
|
203
|
-
'html/attr' => 'escapeHtmlAttr',
|
|
204
|
-
'html/attr/js' => 'escapeHtmlAttr',
|
|
205
|
-
'html/js' => 'convertJSToHtmlRawText',
|
|
206
|
-
'html/comment' => 'escapeHtmlComment',
|
|
207
|
-
],
|
|
208
|
-
self::Css => [
|
|
209
|
-
'html' => 'escapeHtmlText',
|
|
210
|
-
'html/attr' => 'escapeHtmlAttr',
|
|
211
|
-
'html/attr/css' => 'escapeHtmlAttr',
|
|
212
|
-
'html/css' => 'convertJSToHtmlRawText',
|
|
213
|
-
'html/comment' => 'escapeHtmlComment',
|
|
214
|
-
],
|
|
215
|
-
'html' => [
|
|
216
|
-
'html/attr' => 'convertHtmlToHtmlAttr',
|
|
217
|
-
'html/attr/js' => 'convertHtmlToHtmlAttr',
|
|
218
|
-
'html/attr/css' => 'convertHtmlToHtmlAttr',
|
|
219
|
-
'html/attr/url' => 'convertHtmlToHtmlAttr',
|
|
220
|
-
'html/comment' => 'escapeHtmlComment',
|
|
221
|
-
'html/unquoted-attr' => 'convertHtmlToUnquotedAttr',
|
|
222
|
-
],
|
|
223
|
-
'html/attr' => [
|
|
224
|
-
'html' => 'convertHtmlToHtmlAttr',
|
|
225
|
-
'html/unquoted-attr' => 'convertHtmlAttrToUnquotedAttr',
|
|
226
|
-
],
|
|
227
|
-
'html/attr/url' => [
|
|
228
|
-
'html' => 'convertHtmlToHtmlAttr',
|
|
229
|
-
'html/attr' => 'nop',
|
|
230
|
-
],
|
|
231
|
-
'html/unquoted-attr' => [
|
|
232
|
-
'html' => 'convertHtmlToHtmlAttr',
|
|
233
|
-
],
|
|
234
|
-
];
|
|
235
|
-
|
|
236
|
-
if ($source === $dest) {
|
|
237
|
-
return [Filters::class, 'nop'];
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
return isset($table[$source][$dest])
|
|
241
|
-
? [Filters::class, $table[$source][$dest]]
|
|
242
|
-
: null;
|
|
253
|
+
return match (true) {
|
|
254
|
+
$source === $dest => [Filters::class, 'nop'],
|
|
255
|
+
isset(self::Convertors[$source][$dest]) => [Filters::class, self::Convertors[$source][$dest]],
|
|
256
|
+
default => null,
|
|
257
|
+
};
|
|
243
258
|
}
|
|
244
259
|
}
|
|
@@ -17,6 +17,7 @@ use Latte\Compiler\Nodes\Php\NameNode;
|
|
|
17
17
|
use Latte\Compiler\Nodes\Php\Scalar;
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
/** @deprecated */
|
|
20
21
|
final class ExpressionBuilder
|
|
21
22
|
{
|
|
22
23
|
public function __construct(
|
|
@@ -69,7 +70,7 @@ final class ExpressionBuilder
|
|
|
69
70
|
$name = is_string($name)
|
|
70
71
|
? new IdentifierNode($name)
|
|
71
72
|
: ($name instanceof self ? $name->expr : $name);
|
|
72
|
-
return new self(new Expression\
|
|
73
|
+
return new self(new Expression\StaticMethodCallNode($this->expr, $name, self::arrayToArgs($args)));
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
|
|
@@ -4,16 +4,12 @@ declare(strict_types=1);
|
|
|
4
4
|
|
|
5
5
|
namespace Latte\Compiler;
|
|
6
6
|
|
|
7
|
-
use Latte;
|
|
8
|
-
|
|
9
7
|
|
|
10
8
|
/**
|
|
11
9
|
* @implements \IteratorAggregate<Node>
|
|
12
10
|
*/
|
|
13
11
|
abstract class Node implements \IteratorAggregate
|
|
14
12
|
{
|
|
15
|
-
use Latte\Strict;
|
|
16
|
-
|
|
17
13
|
public ?Position $position = null;
|
|
18
14
|
|
|
19
15
|
|
|
@@ -9,7 +9,6 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Compiler;
|
|
11
11
|
|
|
12
|
-
use Latte;
|
|
13
12
|
use Latte\Compiler\Nodes\Php;
|
|
14
13
|
use Latte\Compiler\Nodes\Php\Expression;
|
|
15
14
|
use Latte\Compiler\Nodes\Php\ExpressionNode;
|
|
@@ -18,8 +17,6 @@ use Latte\Compiler\Nodes\Php\Scalar;
|
|
|
18
17
|
|
|
19
18
|
final class NodeHelpers
|
|
20
19
|
{
|
|
21
|
-
use Latte\Strict;
|
|
22
|
-
|
|
23
20
|
/** @return Node[] */
|
|
24
21
|
public static function find(Node $node, callable $filter): array
|
|
25
22
|
{
|
|
@@ -124,7 +121,6 @@ final class NodeHelpers
|
|
|
124
121
|
|
|
125
122
|
return match (true) {
|
|
126
123
|
$node instanceof Nodes\TextNode => $node->content,
|
|
127
|
-
$node instanceof Nodes\Html\QuotedValue => self::toText($node->value),
|
|
128
124
|
$node instanceof Nodes\NopNode => '',
|
|
129
125
|
default => null,
|
|
130
126
|
};
|
|
@@ -9,25 +9,33 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Compiler\Nodes;
|
|
11
11
|
|
|
12
|
+
use Latte\Compiler\Node;
|
|
12
13
|
use Latte\Compiler\PrintContext;
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class AuxiliaryNode extends AreaNode
|
|
16
17
|
{
|
|
17
18
|
public function __construct(
|
|
18
|
-
public /*readonly*/ \Closure $
|
|
19
|
+
public /*readonly*/ \Closure $print,
|
|
20
|
+
/** @var (?Node)[] */
|
|
21
|
+
public array $nodes = [],
|
|
19
22
|
) {
|
|
23
|
+
(function (?Node ...$nodes) {})(...$nodes);
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
public function print(PrintContext $context): string
|
|
24
28
|
{
|
|
25
|
-
return ($this->
|
|
29
|
+
return ($this->print)($context, ...$this->nodes);
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
|
|
29
33
|
public function &getIterator(): \Generator
|
|
30
34
|
{
|
|
31
|
-
|
|
35
|
+
foreach ($this->nodes as &$node) {
|
|
36
|
+
if ($node) {
|
|
37
|
+
yield $node;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
32
40
|
}
|
|
33
41
|
}
|
|
@@ -39,6 +39,16 @@ final class FragmentNode extends AreaNode
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
public function simplify(bool $allowsNull = true): ?AreaNode
|
|
43
|
+
{
|
|
44
|
+
return match (true) {
|
|
45
|
+
!$this->children => $allowsNull ? null : $this,
|
|
46
|
+
count($this->children) === 1 => $this->children[0],
|
|
47
|
+
default => $this,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
42
52
|
public function print(PrintContext $context): string
|
|
43
53
|
{
|
|
44
54
|
$res = '';
|
|
@@ -11,6 +11,8 @@ namespace Latte\Compiler\Nodes\Html;
|
|
|
11
11
|
|
|
12
12
|
use Latte\Compiler\NodeHelpers;
|
|
13
13
|
use Latte\Compiler\Nodes\AreaNode;
|
|
14
|
+
use Latte\Compiler\Nodes\FragmentNode;
|
|
15
|
+
use Latte\Compiler\Nodes\TextNode;
|
|
14
16
|
use Latte\Compiler\Position;
|
|
15
17
|
use Latte\Compiler\PrintContext;
|
|
16
18
|
|
|
@@ -20,6 +22,7 @@ class AttributeNode extends AreaNode
|
|
|
20
22
|
public function __construct(
|
|
21
23
|
public AreaNode $name,
|
|
22
24
|
public ?AreaNode $value = null,
|
|
25
|
+
public ?string $quote = null,
|
|
23
26
|
public ?Position $position = null,
|
|
24
27
|
) {
|
|
25
28
|
}
|
|
@@ -28,12 +31,27 @@ class AttributeNode extends AreaNode
|
|
|
28
31
|
public function print(PrintContext $context): string
|
|
29
32
|
{
|
|
30
33
|
$res = $this->name->print($context);
|
|
31
|
-
if (
|
|
32
|
-
$
|
|
33
|
-
|
|
34
|
+
if (!$this->value) {
|
|
35
|
+
return $res;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
$res .= "echo '=';";
|
|
39
|
+
$quote = $this->quote ?? ($this->value instanceof TextNode ? null : '"');
|
|
40
|
+
$res .= $quote ? 'echo ' . var_export($quote, true) . ';' : '';
|
|
41
|
+
|
|
42
|
+
$escaper = $context->beginEscape();
|
|
43
|
+
$escaper->enterHtmlAttribute(NodeHelpers::toText($this->name));
|
|
44
|
+
if ($this->value instanceof FragmentNode && $escaper->export() === 'html/attr/url') {
|
|
45
|
+
foreach ($this->value->children as $child) {
|
|
46
|
+
$res .= $child->print($context);
|
|
47
|
+
$escaper->enterHtmlAttribute(null);
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
34
50
|
$res .= $this->value->print($context);
|
|
35
|
-
$context->restoreEscape();
|
|
36
51
|
}
|
|
52
|
+
|
|
53
|
+
$context->restoreEscape();
|
|
54
|
+
$res .= $quote ? 'echo ' . var_export($quote, true) . ';' : '';
|
|
37
55
|
return $res;
|
|
38
56
|
}
|
|
39
57
|
|
|
@@ -18,6 +18,7 @@ use Latte\Compiler\Nodes\FragmentNode;
|
|
|
18
18
|
use Latte\Compiler\Position;
|
|
19
19
|
use Latte\Compiler\PrintContext;
|
|
20
20
|
use Latte\Compiler\Tag;
|
|
21
|
+
use Latte\ContentType;
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -25,7 +26,7 @@ use Latte\Compiler\Tag;
|
|
|
25
26
|
*/
|
|
26
27
|
class ElementNode extends AreaNode
|
|
27
28
|
{
|
|
28
|
-
public ?
|
|
29
|
+
public ?Nodes\Php\ExpressionNode $variableName = null;
|
|
29
30
|
public ?FragmentNode $attributes = null;
|
|
30
31
|
public bool $selfClosing = false;
|
|
31
32
|
public ?AreaNode $content = null;
|
|
@@ -36,6 +37,7 @@ class ElementNode extends AreaNode
|
|
|
36
37
|
/** n:tag & n:tag- support */
|
|
37
38
|
public AreaNode $tagNode;
|
|
38
39
|
public bool $captureTagName = false;
|
|
40
|
+
public bool $breakable = false;
|
|
39
41
|
private ?string $endTagVar;
|
|
40
42
|
|
|
41
43
|
|
|
@@ -44,6 +46,7 @@ class ElementNode extends AreaNode
|
|
|
44
46
|
public ?Position $position = null,
|
|
45
47
|
public /*readonly*/ ?self $parent = null,
|
|
46
48
|
public ?\stdClass $data = null,
|
|
49
|
+
public string $contentType = ContentType::Html,
|
|
47
50
|
) {
|
|
48
51
|
$this->data ??= new \stdClass;
|
|
49
52
|
$this->tagNode = new AuxiliaryNode(\Closure::fromCallable([$this, 'printStartTag']));
|
|
@@ -65,10 +68,25 @@ class ElementNode extends AreaNode
|
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
|
|
71
|
+
public function is(string $name): bool
|
|
72
|
+
{
|
|
73
|
+
return $this->contentType === ContentType::Html
|
|
74
|
+
? strcasecmp($this->name, $name) === 0
|
|
75
|
+
: $this->name === $name;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
public function isRawText(): bool
|
|
80
|
+
{
|
|
81
|
+
return $this->contentType === ContentType::Html
|
|
82
|
+
&& ($this->is('script') || $this->is('style'));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
68
86
|
public function print(PrintContext $context): string
|
|
69
87
|
{
|
|
70
88
|
$res = $this->endTagVar = null;
|
|
71
|
-
if ($this->captureTagName || $this->
|
|
89
|
+
if ($this->captureTagName || $this->variableName) {
|
|
72
90
|
$endTag = $this->endTagVar = '$ʟ_tag[' . $context->generateId() . ']';
|
|
73
91
|
$res = "$this->endTagVar = '';";
|
|
74
92
|
} else {
|
|
@@ -79,9 +97,11 @@ class ElementNode extends AreaNode
|
|
|
79
97
|
|
|
80
98
|
if ($this->content) {
|
|
81
99
|
$context->beginEscape()->enterHtmlText($this);
|
|
82
|
-
$
|
|
100
|
+
$content = $this->content->print($context);
|
|
83
101
|
$context->restoreEscape();
|
|
84
|
-
$res .=
|
|
102
|
+
$res .= $this->breakable
|
|
103
|
+
? 'try { ' . $content . ' } finally { echo ' . $endTag . '; } '
|
|
104
|
+
: $content . 'echo ' . $endTag . ';';
|
|
85
105
|
}
|
|
86
106
|
|
|
87
107
|
return $res;
|
|
@@ -93,14 +113,17 @@ class ElementNode extends AreaNode
|
|
|
93
113
|
$context->beginEscape()->enterHtmlTag($this->name);
|
|
94
114
|
$res = "echo '<';";
|
|
95
115
|
|
|
96
|
-
$namePhp = var_export($this->name, true);
|
|
97
116
|
if ($this->endTagVar) {
|
|
98
|
-
$
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
117
|
+
$expr = $this->variableName
|
|
118
|
+
? 'LR\Filters::safeTag('
|
|
119
|
+
. $this->variableName->print($context)
|
|
120
|
+
. ($this->contentType === ContentType::Xml ? ', true' : '')
|
|
121
|
+
. ')'
|
|
122
|
+
: var_export($this->name, true);
|
|
123
|
+
$res .= "echo \$ʟ_tmp = $expr /* line {$this->position->line} */;"
|
|
124
|
+
. "{$this->endTagVar} = '</' . \$ʟ_tmp . '>' . {$this->endTagVar};";
|
|
102
125
|
} else {
|
|
103
|
-
$res .= 'echo ' . $
|
|
126
|
+
$res .= 'echo ' . var_export($this->name, true) . ';';
|
|
104
127
|
}
|
|
105
128
|
|
|
106
129
|
foreach ($this->attributes?->children ?? [] as $attr) {
|
|
@@ -116,8 +139,8 @@ class ElementNode extends AreaNode
|
|
|
116
139
|
public function &getIterator(): \Generator
|
|
117
140
|
{
|
|
118
141
|
yield $this->tagNode;
|
|
119
|
-
if ($this->
|
|
120
|
-
yield $this->
|
|
142
|
+
if ($this->variableName) {
|
|
143
|
+
yield $this->variableName;
|
|
121
144
|
}
|
|
122
145
|
if ($this->attributes) {
|
|
123
146
|
yield $this->attributes;
|
|
@@ -35,6 +35,12 @@ class ArgumentNode extends Node
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
public function toArrayItem(): ArrayItemNode
|
|
39
|
+
{
|
|
40
|
+
return new ArrayItemNode($this->value, $this->name, $this->byRef, $this->unpack, $this->position);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
38
44
|
public function &getIterator(): \Generator
|
|
39
45
|
{
|
|
40
46
|
if ($this->name) {
|
|
@@ -40,6 +40,18 @@ class ArrayItemNode extends Node
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
|
|
43
|
+
public function toArgument(): ArgumentNode
|
|
44
|
+
{
|
|
45
|
+
$key = match (true) {
|
|
46
|
+
$this->key instanceof Scalar\StringNode => new IdentifierNode($this->key->value),
|
|
47
|
+
$this->key instanceof IdentifierNode => $this->key,
|
|
48
|
+
$this->key === null => null,
|
|
49
|
+
default => throw new \InvalidArgumentException('The expression used in the key cannot be converted to an argument.'),
|
|
50
|
+
};
|
|
51
|
+
return new ArgumentNode($this->value, $this->byRef, $this->unpack, $key, $this->position);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
43
55
|
public function &getIterator(): \Generator
|
|
44
56
|
{
|
|
45
57
|
if ($this->key) {
|
|
@@ -12,8 +12,6 @@ namespace Latte\Compiler\Nodes\Php\Expression;
|
|
|
12
12
|
use Latte\Compiler\Nodes\Php\ArgumentNode;
|
|
13
13
|
use Latte\Compiler\Nodes\Php\ArrayItemNode;
|
|
14
14
|
use Latte\Compiler\Nodes\Php\ExpressionNode;
|
|
15
|
-
use Latte\Compiler\Nodes\Php\IdentifierNode;
|
|
16
|
-
use Latte\Compiler\Nodes\Php\Scalar;
|
|
17
15
|
use Latte\Compiler\Position;
|
|
18
16
|
use Latte\Compiler\PrintContext;
|
|
19
17
|
|
|
@@ -21,41 +19,18 @@ use Latte\Compiler\PrintContext;
|
|
|
21
19
|
class ArrayNode extends ExpressionNode
|
|
22
20
|
{
|
|
23
21
|
public function __construct(
|
|
24
|
-
/** @var array<ArrayItemNode
|
|
22
|
+
/** @var array<ArrayItemNode> */
|
|
25
23
|
public array $items = [],
|
|
26
24
|
public ?Position $position = null,
|
|
27
25
|
) {
|
|
28
|
-
(function (
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/** @param ArgumentNode[] $args */
|
|
33
|
-
public static function fromArguments(array $args): self
|
|
34
|
-
{
|
|
35
|
-
$node = new self;
|
|
36
|
-
foreach ($args as $arg) {
|
|
37
|
-
$node->items[] = new ArrayItemNode($arg->value, $arg->name, $arg->byRef, $arg->unpack, $arg->position);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return $node;
|
|
26
|
+
(function (ArrayItemNode ...$args) {})(...$items);
|
|
41
27
|
}
|
|
42
28
|
|
|
43
29
|
|
|
44
30
|
/** @return ArgumentNode[] */
|
|
45
31
|
public function toArguments(): array
|
|
46
32
|
{
|
|
47
|
-
$
|
|
48
|
-
foreach ($this->items as $item) {
|
|
49
|
-
$key = match (true) {
|
|
50
|
-
$item->key instanceof Scalar\StringNode => new IdentifierNode($item->key->value),
|
|
51
|
-
$item->key instanceof IdentifierNode => $item->key,
|
|
52
|
-
$item->key === null => null,
|
|
53
|
-
default => throw new \InvalidArgumentException('The expression used in the key cannot be converted to an argument.'),
|
|
54
|
-
};
|
|
55
|
-
$args[] = new ArgumentNode($item->value, $item->byRef, $item->unpack, $key, $item->position);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return $args;
|
|
33
|
+
return array_map(fn(ArrayItemNode $item) => $item->toArgument(), $this->items);
|
|
59
34
|
}
|
|
60
35
|
|
|
61
36
|
|
|
@@ -87,9 +62,7 @@ class ArrayNode extends ExpressionNode
|
|
|
87
62
|
public function &getIterator(): \Generator
|
|
88
63
|
{
|
|
89
64
|
foreach ($this->items as &$item) {
|
|
90
|
-
|
|
91
|
-
yield $item;
|
|
92
|
-
}
|
|
65
|
+
yield $item;
|
|
93
66
|
}
|
|
94
67
|
}
|
|
95
68
|
}
|