@vituum/vite-plugin-latte 1.0.0 → 1.2.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/latte/PlaceholderFunction.php +2 -2
- package/package.json +10 -11
- package/vendor/autoload.php +18 -0
- 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 +14 -8
- package/vendor/composer/autoload_namespaces.php +1 -1
- package/vendor/composer/autoload_psr4.php +1 -1
- package/vendor/composer/autoload_real.php +3 -22
- package/vendor/composer/autoload_static.php +13 -7
- 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 +113 -89
- 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/FilterNode.php +1 -1
- 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 +5 -5
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +11 -21
- 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 +15 -8
- package/vendor/latte/latte/src/Latte/Compiler/Tag.php +13 -14
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +5 -9
- package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +52 -3
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +353 -326
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +6 -5
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +105 -178
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +40 -33
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +186 -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 +136 -95
- package/vendor/latte/latte/src/Latte/Essential/AuxiliaryIterator.php +46 -0
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +42 -27
- package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +0 -4
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +81 -66
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +103 -43
- 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 +8 -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 +40 -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/TemplatePrintNode.php +25 -3
- 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/VarPrintNode.php +9 -2
- package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +1 -1
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +16 -58
- 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/Helpers.php +3 -1
- package/vendor/latte/latte/src/Latte/Loader.php +1 -0
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +1 -4
- 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 -33
- package/vendor/latte/latte/src/Latte/Runtime/FunctionExecutor.php +68 -0
- package/vendor/latte/latte/src/Latte/Runtime/Html.php +0 -4
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +3 -5
- 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,21 +30,63 @@ 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
|
|
|
47
84
|
public function __construct(
|
|
48
85
|
private string $contentType,
|
|
49
86
|
) {
|
|
50
|
-
$this->state = $
|
|
87
|
+
$this->state = in_array($contentType, [ContentType::Html, ContentType::Xml], true)
|
|
88
|
+
? self::HtmlText
|
|
89
|
+
: $contentType;
|
|
51
90
|
}
|
|
52
91
|
|
|
53
92
|
|
|
@@ -65,8 +104,7 @@ final class Escaper
|
|
|
65
104
|
|
|
66
105
|
public function export(): string
|
|
67
106
|
{
|
|
68
|
-
return
|
|
69
|
-
. ($this->subType ? '/' . $this->subType : '');
|
|
107
|
+
return $this->state . ($this->subType ? '/' . $this->subType : '');
|
|
70
108
|
}
|
|
71
109
|
|
|
72
110
|
|
|
@@ -76,20 +114,29 @@ final class Escaper
|
|
|
76
114
|
}
|
|
77
115
|
|
|
78
116
|
|
|
79
|
-
public function enterHtmlText(
|
|
117
|
+
public function enterHtmlText(ElementNode $el): void
|
|
80
118
|
{
|
|
81
|
-
$
|
|
82
|
-
|
|
83
|
-
$
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
if ($el->isRawText()) {
|
|
120
|
+
$this->state = self::HtmlRawText;
|
|
121
|
+
$this->subType = self::Text;
|
|
122
|
+
if ($el->is('script')) {
|
|
123
|
+
$type = $el->getAttribute('type');
|
|
124
|
+
if ($type === true || $type === null
|
|
125
|
+
|| is_string($type) && preg_match('#((application|text)/(((x-)?java|ecma|j|live)script|json)|application/.+\+json|text/plain|module|importmap|)$#Ai', $type)
|
|
126
|
+
) {
|
|
127
|
+
$this->subType = self::JavaScript;
|
|
128
|
+
|
|
129
|
+
} elseif (is_string($type) && preg_match('#text/((x-)?template|html)$#Ai', $type)) {
|
|
130
|
+
$this->subType = self::HtmlText;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
} elseif ($el->is('style')) {
|
|
134
|
+
$this->subType = self::Css;
|
|
92
135
|
}
|
|
136
|
+
|
|
137
|
+
} else {
|
|
138
|
+
$this->state = self::HtmlText;
|
|
139
|
+
$this->subType = '';
|
|
93
140
|
}
|
|
94
141
|
}
|
|
95
142
|
|
|
@@ -101,10 +148,16 @@ final class Escaper
|
|
|
101
148
|
}
|
|
102
149
|
|
|
103
150
|
|
|
104
|
-
public function
|
|
151
|
+
public function enterHtmlRaw(string $subType): void
|
|
152
|
+
{
|
|
153
|
+
$this->state = self::HtmlRawText;
|
|
154
|
+
$this->subType = $subType;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
public function enterHtmlAttribute(?string $name = null): void
|
|
105
159
|
{
|
|
106
160
|
$this->state = self::HtmlAttribute;
|
|
107
|
-
$this->quote = $quote;
|
|
108
161
|
$this->subType = '';
|
|
109
162
|
|
|
110
163
|
if ($this->contentType === ContentType::Html && is_string($name)) {
|
|
@@ -122,12 +175,6 @@ final class Escaper
|
|
|
122
175
|
}
|
|
123
176
|
|
|
124
177
|
|
|
125
|
-
public function enterHtmlAttributeQuote(string $quote = '"'): void
|
|
126
|
-
{
|
|
127
|
-
$this->quote = $quote;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
|
|
131
178
|
public function enterHtmlBogusTag(): void
|
|
132
179
|
{
|
|
133
180
|
$this->state = self::HtmlBogusTag;
|
|
@@ -142,27 +189,30 @@ final class Escaper
|
|
|
142
189
|
|
|
143
190
|
public function escape(string $str): string
|
|
144
191
|
{
|
|
145
|
-
[$lq, $rq] = $this->state === self::HtmlAttribute && !$this->quote ? ["'\"' . ", " . '\"'"] : ['', ''];
|
|
146
192
|
return match ($this->contentType) {
|
|
147
193
|
ContentType::Html => match ($this->state) {
|
|
148
194
|
self::HtmlText => 'LR\Filters::escapeHtmlText(' . $str . ')',
|
|
149
195
|
self::HtmlTag => 'LR\Filters::escapeHtmlTag(' . $str . ')',
|
|
150
196
|
self::HtmlAttribute => match ($this->subType) {
|
|
151
197
|
'',
|
|
152
|
-
self::Url =>
|
|
153
|
-
self::JavaScript =>
|
|
154
|
-
self::Css =>
|
|
198
|
+
self::Url => 'LR\Filters::escapeHtmlAttr(' . $str . ')',
|
|
199
|
+
self::JavaScript => 'LR\Filters::escapeHtmlAttr(LR\Filters::escapeJs(' . $str . '))',
|
|
200
|
+
self::Css => 'LR\Filters::escapeHtmlAttr(LR\Filters::escapeCss(' . $str . '))',
|
|
155
201
|
},
|
|
156
202
|
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
157
203
|
self::HtmlBogusTag => 'LR\Filters::escapeHtml(' . $str . ')',
|
|
158
|
-
self::
|
|
159
|
-
|
|
204
|
+
self::HtmlRawText => match ($this->subType) {
|
|
205
|
+
self::Text => 'LR\Filters::convertJSToHtmlRawText(' . $str . ')', // sanitization, escaping is not possible
|
|
206
|
+
self::HtmlText => 'LR\Filters::escapeHtmlRawTextHtml(' . $str . ')',
|
|
207
|
+
self::JavaScript => 'LR\Filters::escapeJs(' . $str . ')',
|
|
208
|
+
self::Css => 'LR\Filters::escapeCss(' . $str . ')',
|
|
209
|
+
},
|
|
160
210
|
default => throw new \LogicException("Unknown context $this->contentType, $this->state."),
|
|
161
211
|
},
|
|
162
212
|
ContentType::Xml => match ($this->state) {
|
|
163
213
|
self::HtmlText,
|
|
164
214
|
self::HtmlBogusTag => 'LR\Filters::escapeXml(' . $str . ')',
|
|
165
|
-
self::HtmlAttribute =>
|
|
215
|
+
self::HtmlAttribute => 'LR\Filters::escapeXml(' . $str . ')',
|
|
166
216
|
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
167
217
|
self::HtmlTag => 'LR\Filters::escapeXmlTag(' . $str . ')',
|
|
168
218
|
default => throw new \LogicException("Unknown context $this->contentType, $this->state."),
|
|
@@ -176,6 +226,28 @@ final class Escaper
|
|
|
176
226
|
}
|
|
177
227
|
|
|
178
228
|
|
|
229
|
+
public function escapeMandatory(string $str): string
|
|
230
|
+
{
|
|
231
|
+
return match ($this->contentType) {
|
|
232
|
+
ContentType::Html => match ($this->state) {
|
|
233
|
+
self::HtmlAttribute => "LR\\Filters::escapeHtmlQuotes($str)",
|
|
234
|
+
self::HtmlRawText => match ($this->subType) {
|
|
235
|
+
self::HtmlText => 'LR\Filters::convertHtmlToHtmlRawText(' . $str . ')',
|
|
236
|
+
default => "LR\\Filters::convertJSToHtmlRawText($str)",
|
|
237
|
+
},
|
|
238
|
+
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
239
|
+
default => $str,
|
|
240
|
+
},
|
|
241
|
+
ContentType::Xml => match ($this->state) {
|
|
242
|
+
self::HtmlAttribute => "LR\\Filters::escapeHtmlQuotes($str)",
|
|
243
|
+
self::HtmlComment => 'LR\Filters::escapeHtmlComment(' . $str . ')',
|
|
244
|
+
default => $str,
|
|
245
|
+
},
|
|
246
|
+
default => $str,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
179
251
|
public function check(string $str): string
|
|
180
252
|
{
|
|
181
253
|
if ($this->state === self::HtmlAttribute && $this->subType === self::Url) {
|
|
@@ -187,58 +259,10 @@ final class Escaper
|
|
|
187
259
|
|
|
188
260
|
public static function getConvertor(string $source, string $dest): ?callable
|
|
189
261
|
{
|
|
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;
|
|
262
|
+
return match (true) {
|
|
263
|
+
$source === $dest => [Filters::class, 'nop'],
|
|
264
|
+
isset(self::Convertors[$source][$dest]) => [Filters::class, self::Convertors[$source][$dest]],
|
|
265
|
+
default => null,
|
|
266
|
+
};
|
|
243
267
|
}
|
|
244
268
|
}
|
|
@@ -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
|
}
|