@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
|
@@ -11,10 +11,10 @@ namespace Latte\Essential;
|
|
|
11
11
|
|
|
12
12
|
use Latte;
|
|
13
13
|
use Latte\Compiler\Nodes\Php\Scalar;
|
|
14
|
-
use Latte\Compiler\Nodes\TemplateNode;
|
|
15
14
|
use Latte\Compiler\Nodes\TextNode;
|
|
16
15
|
use Latte\Compiler\Tag;
|
|
17
16
|
use Latte\Compiler\TemplateParser;
|
|
17
|
+
use Latte\Runtime;
|
|
18
18
|
use Latte\RuntimeException;
|
|
19
19
|
use Nette;
|
|
20
20
|
|
|
@@ -24,14 +24,19 @@ use Nette;
|
|
|
24
24
|
*/
|
|
25
25
|
final class CoreExtension extends Latte\Extension
|
|
26
26
|
{
|
|
27
|
-
|
|
27
|
+
private Latte\Engine $engine;
|
|
28
|
+
private Filters $filters;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
public function __construct()
|
|
32
|
+
{
|
|
33
|
+
$this->filters = new Filters;
|
|
34
|
+
}
|
|
30
35
|
|
|
31
36
|
|
|
32
37
|
public function beforeCompile(Latte\Engine $engine): void
|
|
33
38
|
{
|
|
34
|
-
$this->
|
|
39
|
+
$this->engine = $engine;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
|
|
@@ -92,6 +97,7 @@ final class CoreExtension extends Latte\Extension
|
|
|
92
97
|
'ifset' => [Nodes\IfNode::class, 'create'],
|
|
93
98
|
'ifchanged' => [Nodes\IfChangedNode::class, 'create'],
|
|
94
99
|
'n:ifcontent' => [Nodes\IfContentNode::class, 'create'],
|
|
100
|
+
'n:else' => [Nodes\NElseNode::class, 'create'],
|
|
95
101
|
'switch' => [Nodes\SwitchNode::class, 'create'],
|
|
96
102
|
];
|
|
97
103
|
}
|
|
@@ -100,18 +106,19 @@ final class CoreExtension extends Latte\Extension
|
|
|
100
106
|
public function getFilters(): array
|
|
101
107
|
{
|
|
102
108
|
return [
|
|
103
|
-
'batch' => [
|
|
104
|
-
'breakLines' => [
|
|
105
|
-
'breaklines' => [
|
|
106
|
-
'bytes' => [
|
|
109
|
+
'batch' => [$this->filters, 'batch'],
|
|
110
|
+
'breakLines' => [$this->filters, 'breaklines'],
|
|
111
|
+
'breaklines' => [$this->filters, 'breaklines'],
|
|
112
|
+
'bytes' => [$this->filters, 'bytes'],
|
|
107
113
|
'capitalize' => extension_loaded('mbstring')
|
|
108
|
-
? [
|
|
109
|
-
:
|
|
110
|
-
'ceil' => [
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
'
|
|
114
|
-
'
|
|
114
|
+
? [$this->filters, 'capitalize']
|
|
115
|
+
: fn() => throw new RuntimeException('Filter |capitalize requires mbstring extension.'),
|
|
116
|
+
'ceil' => [$this->filters, 'ceil'],
|
|
117
|
+
'checkUrl' => [Latte\Runtime\Filters::class, 'safeUrl'],
|
|
118
|
+
'clamp' => [$this->filters, 'clamp'],
|
|
119
|
+
'dataStream' => [$this->filters, 'dataStream'],
|
|
120
|
+
'datastream' => [$this->filters, 'dataStream'],
|
|
121
|
+
'date' => [$this->filters, 'date'],
|
|
115
122
|
'escape' => [Latte\Runtime\Filters::class, 'nop'],
|
|
116
123
|
'escapeCss' => [Latte\Runtime\Filters::class, 'escapeCss'],
|
|
117
124
|
'escapeHtml' => [Latte\Runtime\Filters::class, 'escapeHtml'],
|
|
@@ -120,50 +127,50 @@ final class CoreExtension extends Latte\Extension
|
|
|
120
127
|
'escapeJs' => [Latte\Runtime\Filters::class, 'escapeJs'],
|
|
121
128
|
'escapeUrl' => 'rawurlencode',
|
|
122
129
|
'escapeXml' => [Latte\Runtime\Filters::class, 'escapeXml'],
|
|
123
|
-
'explode' => [
|
|
124
|
-
'first' => [
|
|
130
|
+
'explode' => [$this->filters, 'explode'],
|
|
131
|
+
'first' => [$this->filters, 'first'],
|
|
125
132
|
'firstUpper' => extension_loaded('mbstring')
|
|
126
|
-
? [
|
|
127
|
-
:
|
|
128
|
-
'floor' => [
|
|
129
|
-
'
|
|
130
|
-
'implode' => [
|
|
131
|
-
'indent' => [
|
|
132
|
-
'join' => [
|
|
133
|
-
'last' => [
|
|
134
|
-
'length' => [
|
|
133
|
+
? [$this->filters, 'firstUpper']
|
|
134
|
+
: fn() => throw new RuntimeException('Filter |firstUpper requires mbstring extension.'),
|
|
135
|
+
'floor' => [$this->filters, 'floor'],
|
|
136
|
+
'group' => [$this->filters, 'group'],
|
|
137
|
+
'implode' => [$this->filters, 'implode'],
|
|
138
|
+
'indent' => [$this->filters, 'indent'],
|
|
139
|
+
'join' => [$this->filters, 'implode'],
|
|
140
|
+
'last' => [$this->filters, 'last'],
|
|
141
|
+
'length' => [$this->filters, 'length'],
|
|
135
142
|
'lower' => extension_loaded('mbstring')
|
|
136
|
-
? [
|
|
137
|
-
:
|
|
143
|
+
? [$this->filters, 'lower']
|
|
144
|
+
: fn() => throw new RuntimeException('Filter |lower requires mbstring extension.'),
|
|
138
145
|
'number' => 'number_format',
|
|
139
|
-
'padLeft' => [
|
|
140
|
-
'padRight' => [
|
|
141
|
-
'query' => [
|
|
142
|
-
'random' => [
|
|
143
|
-
'repeat' => [
|
|
144
|
-
'replace' => [
|
|
145
|
-
'replaceRe' => [
|
|
146
|
-
'replaceRE' => [
|
|
147
|
-
'reverse' => [
|
|
148
|
-
'round' => [
|
|
149
|
-
'slice' => [
|
|
150
|
-
'sort' => [
|
|
151
|
-
'spaceless' => [
|
|
152
|
-
'split' => [
|
|
153
|
-
'strip' => [
|
|
154
|
-
'stripHtml' => [
|
|
155
|
-
'striphtml' => [
|
|
156
|
-
'stripTags' => [
|
|
157
|
-
'striptags' => [
|
|
158
|
-
'substr' => [
|
|
159
|
-
'trim' => [
|
|
160
|
-
'truncate' => [
|
|
146
|
+
'padLeft' => [$this->filters, 'padLeft'],
|
|
147
|
+
'padRight' => [$this->filters, 'padRight'],
|
|
148
|
+
'query' => [$this->filters, 'query'],
|
|
149
|
+
'random' => [$this->filters, 'random'],
|
|
150
|
+
'repeat' => [$this->filters, 'repeat'],
|
|
151
|
+
'replace' => [$this->filters, 'replace'],
|
|
152
|
+
'replaceRe' => [$this->filters, 'replaceRe'],
|
|
153
|
+
'replaceRE' => [$this->filters, 'replaceRe'],
|
|
154
|
+
'reverse' => [$this->filters, 'reverse'],
|
|
155
|
+
'round' => [$this->filters, 'round'],
|
|
156
|
+
'slice' => [$this->filters, 'slice'],
|
|
157
|
+
'sort' => [$this->filters, 'sort'],
|
|
158
|
+
'spaceless' => [$this->filters, 'strip'],
|
|
159
|
+
'split' => [$this->filters, 'explode'],
|
|
160
|
+
'strip' => [$this->filters, 'strip'], // obsolete
|
|
161
|
+
'stripHtml' => [$this->filters, 'stripHtml'],
|
|
162
|
+
'striphtml' => [$this->filters, 'stripHtml'],
|
|
163
|
+
'stripTags' => [$this->filters, 'stripTags'],
|
|
164
|
+
'striptags' => [$this->filters, 'stripTags'],
|
|
165
|
+
'substr' => [$this->filters, 'substring'],
|
|
166
|
+
'trim' => [$this->filters, 'trim'],
|
|
167
|
+
'truncate' => [$this->filters, 'truncate'],
|
|
161
168
|
'upper' => extension_loaded('mbstring')
|
|
162
|
-
? [
|
|
163
|
-
:
|
|
169
|
+
? [$this->filters, 'upper']
|
|
170
|
+
: fn() => throw new RuntimeException('Filter |upper requires mbstring extension.'),
|
|
164
171
|
'webalize' => class_exists(Nette\Utils\Strings::class)
|
|
165
172
|
? [Nette\Utils\Strings::class, 'webalize']
|
|
166
|
-
:
|
|
173
|
+
: fn() => throw new RuntimeException('Filter |webalize requires nette/utils package.'),
|
|
167
174
|
];
|
|
168
175
|
}
|
|
169
176
|
|
|
@@ -171,24 +178,28 @@ final class CoreExtension extends Latte\Extension
|
|
|
171
178
|
public function getFunctions(): array
|
|
172
179
|
{
|
|
173
180
|
return [
|
|
174
|
-
'clamp' => [
|
|
175
|
-
'divisibleBy' => [
|
|
176
|
-
'even' => [
|
|
177
|
-
'first' => [
|
|
178
|
-
'
|
|
179
|
-
'
|
|
180
|
-
'
|
|
181
|
+
'clamp' => [$this->filters, 'clamp'],
|
|
182
|
+
'divisibleBy' => [$this->filters, 'divisibleBy'],
|
|
183
|
+
'even' => [$this->filters, 'even'],
|
|
184
|
+
'first' => [$this->filters, 'first'],
|
|
185
|
+
'group' => [$this->filters, 'group'],
|
|
186
|
+
'last' => [$this->filters, 'last'],
|
|
187
|
+
'odd' => [$this->filters, 'odd'],
|
|
188
|
+
'slice' => [$this->filters, 'slice'],
|
|
189
|
+
'hasBlock' => fn(Runtime\Template $template, string $name): bool => $template->hasBlock($name),
|
|
181
190
|
];
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
|
|
185
194
|
public function getPasses(): array
|
|
186
195
|
{
|
|
196
|
+
$passes = new Passes($this->engine);
|
|
187
197
|
return [
|
|
188
|
-
'internalVariables' => [
|
|
189
|
-
'overwrittenVariables' => [
|
|
190
|
-
'customFunctions' =>
|
|
191
|
-
'moveTemplatePrintToHead' => [
|
|
198
|
+
'internalVariables' => [$passes, 'forbiddenVariablesPass'],
|
|
199
|
+
'overwrittenVariables' => [Nodes\ForeachNode::class, 'overwrittenVariablesPass'],
|
|
200
|
+
'customFunctions' => [$passes, 'customFunctionsPass'],
|
|
201
|
+
'moveTemplatePrintToHead' => [Nodes\TemplatePrintNode::class, 'moveToHeadPass'],
|
|
202
|
+
'nElse' => [Nodes\NElseNode::class, 'processPass'],
|
|
192
203
|
];
|
|
193
204
|
}
|
|
194
205
|
|
|
@@ -222,13 +233,17 @@ final class CoreExtension extends Latte\Extension
|
|
|
222
233
|
*/
|
|
223
234
|
private function parseSyntax(Tag $tag, TemplateParser $parser): \Generator
|
|
224
235
|
{
|
|
236
|
+
if ($tag->isNAttribute() && $tag->prefix !== $tag::PrefixNone) {
|
|
237
|
+
throw new Latte\CompileException("Use n:syntax instead of {$tag->getNotation()}", $tag->position);
|
|
238
|
+
}
|
|
225
239
|
$tag->expectArguments();
|
|
226
240
|
$token = $tag->parser->stream->consume();
|
|
227
241
|
$lexer = $parser->getLexer();
|
|
228
|
-
$saved = [$lexer->openDelimiter, $lexer->closeDelimiter];
|
|
229
242
|
$lexer->setSyntax($token->text, $tag->isNAttribute() ? null : $tag->name);
|
|
230
243
|
[$inner] = yield;
|
|
231
|
-
|
|
244
|
+
if (!$tag->isNAttribute()) {
|
|
245
|
+
$lexer->popSyntax();
|
|
246
|
+
}
|
|
232
247
|
return $inner;
|
|
233
248
|
}
|
|
234
249
|
}
|
|
@@ -120,7 +120,7 @@ final class Filters
|
|
|
120
120
|
} elseif ($info->contentType === ContentType::Html) {
|
|
121
121
|
$s = preg_replace_callback('#<(textarea|pre).*?</\1#si', fn($m) => strtr($m[0], " \t\r\n", "\x1F\x1E\x1D\x1A"), $s);
|
|
122
122
|
if (preg_last_error()) {
|
|
123
|
-
throw new Latte\
|
|
123
|
+
throw new Latte\RuntimeException(preg_last_error_msg());
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
$s = preg_replace('#(?:^|[\r\n]+)(?=[^\r\n])#', '$0' . str_repeat($chars, $level), $s);
|
|
@@ -172,17 +172,12 @@ final class Filters
|
|
|
172
172
|
return null;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
$format = Latte\Runtime\Filters::$dateFormat;
|
|
177
|
-
}
|
|
178
|
-
|
|
175
|
+
$format ??= Latte\Runtime\Filters::$dateFormat;
|
|
179
176
|
if ($time instanceof \DateInterval) {
|
|
180
177
|
return $time->format($format);
|
|
181
178
|
|
|
182
179
|
} elseif (is_numeric($time)) {
|
|
183
|
-
$time = new \DateTime(
|
|
184
|
-
$time->setTimeZone(new \DateTimeZone(date_default_timezone_get()));
|
|
185
|
-
|
|
180
|
+
$time = (new \DateTime)->setTimestamp((int) $time);
|
|
186
181
|
} elseif (!$time instanceof \DateTimeInterface) {
|
|
187
182
|
$time = new \DateTime($time);
|
|
188
183
|
}
|
|
@@ -200,7 +195,7 @@ final class Filters
|
|
|
200
195
|
|
|
201
196
|
|
|
202
197
|
/**
|
|
203
|
-
* Converts to human
|
|
198
|
+
* Converts to human-readable file size.
|
|
204
199
|
*/
|
|
205
200
|
public static function bytes(float $bytes, int $precision = 2): string
|
|
206
201
|
{
|
|
@@ -250,7 +245,7 @@ final class Filters
|
|
|
250
245
|
{
|
|
251
246
|
$res = preg_replace($pattern, $replacement, $subject);
|
|
252
247
|
if (preg_last_error()) {
|
|
253
|
-
throw new Latte\
|
|
248
|
+
throw new Latte\RuntimeException(preg_last_error_msg());
|
|
254
249
|
}
|
|
255
250
|
|
|
256
251
|
return $res;
|
|
@@ -262,10 +257,7 @@ final class Filters
|
|
|
262
257
|
*/
|
|
263
258
|
public static function dataStream(string $data, ?string $type = null): string
|
|
264
259
|
{
|
|
265
|
-
|
|
266
|
-
$type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
|
267
|
-
}
|
|
268
|
-
|
|
260
|
+
$type ??= finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data);
|
|
269
261
|
return 'data:' . ($type ? "$type;" : '') . 'base64,' . base64_encode($data);
|
|
270
262
|
}
|
|
271
263
|
|
|
@@ -273,7 +265,7 @@ final class Filters
|
|
|
273
265
|
public static function breaklines(string|Stringable|null $s): Html
|
|
274
266
|
{
|
|
275
267
|
$s = htmlspecialchars((string) $s, ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
276
|
-
return new Html(nl2br($s,
|
|
268
|
+
return new Html(nl2br($s, false));
|
|
277
269
|
}
|
|
278
270
|
|
|
279
271
|
|
|
@@ -283,15 +275,11 @@ final class Filters
|
|
|
283
275
|
public static function substring(string|Stringable|null $s, int $start, ?int $length = null): string
|
|
284
276
|
{
|
|
285
277
|
$s = (string) $s;
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
return mb_substr($s, $start, $length, 'UTF-8'); // MB is much faster
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return iconv_substr($s, $start, $length, 'UTF-8');
|
|
278
|
+
return match (true) {
|
|
279
|
+
extension_loaded('mbstring') => mb_substr($s, $start, $length, 'UTF-8'),
|
|
280
|
+
extension_loaded('iconv') => iconv_substr($s, $start, $length, 'UTF-8'),
|
|
281
|
+
default => throw new Latte\RuntimeException("Filter |substr requires 'mbstring' or 'iconv' extension."),
|
|
282
|
+
};
|
|
295
283
|
}
|
|
296
284
|
|
|
297
285
|
|
|
@@ -372,9 +360,11 @@ final class Filters
|
|
|
372
360
|
|
|
373
361
|
private static function strLength(string $s): int
|
|
374
362
|
{
|
|
375
|
-
return
|
|
376
|
-
|
|
377
|
-
|
|
363
|
+
return match (true) {
|
|
364
|
+
extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'),
|
|
365
|
+
extension_loaded('iconv') => iconv_strlen($s, 'UTF-8'),
|
|
366
|
+
default => strlen(@utf8_decode($s)), // deprecated
|
|
367
|
+
};
|
|
378
368
|
}
|
|
379
369
|
|
|
380
370
|
|
|
@@ -386,7 +376,7 @@ final class Filters
|
|
|
386
376
|
$charlist = preg_quote($charlist, '#');
|
|
387
377
|
$s = preg_replace('#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', '', (string) $s);
|
|
388
378
|
if (preg_last_error()) {
|
|
389
|
-
throw new Latte\
|
|
379
|
+
throw new Latte\RuntimeException(preg_last_error_msg());
|
|
390
380
|
}
|
|
391
381
|
|
|
392
382
|
return $s;
|
|
@@ -420,7 +410,7 @@ final class Filters
|
|
|
420
410
|
/**
|
|
421
411
|
* Reverses string or array.
|
|
422
412
|
*/
|
|
423
|
-
public static function reverse(string|
|
|
413
|
+
public static function reverse(string|iterable $val, bool $preserveKeys = false): string|array
|
|
424
414
|
{
|
|
425
415
|
if (is_array($val)) {
|
|
426
416
|
return array_reverse($val, $preserveKeys);
|
|
@@ -435,7 +425,7 @@ final class Filters
|
|
|
435
425
|
/**
|
|
436
426
|
* Chunks items by returning an array of arrays with the given number of items.
|
|
437
427
|
*/
|
|
438
|
-
public static function batch(
|
|
428
|
+
public static function batch(iterable $list, int $length, $rest = null): \Generator
|
|
439
429
|
{
|
|
440
430
|
$batch = [];
|
|
441
431
|
foreach ($list as $key => $value) {
|
|
@@ -459,14 +449,78 @@ final class Filters
|
|
|
459
449
|
|
|
460
450
|
|
|
461
451
|
/**
|
|
462
|
-
* Sorts
|
|
463
|
-
* @
|
|
464
|
-
* @
|
|
452
|
+
* Sorts elements using the comparison function and preserves the key association.
|
|
453
|
+
* @template K
|
|
454
|
+
* @template V
|
|
455
|
+
* @param iterable<K, V> $data
|
|
456
|
+
* @return iterable<K, V>
|
|
465
457
|
*/
|
|
466
|
-
public static function sort(
|
|
458
|
+
public static function sort(
|
|
459
|
+
iterable $data,
|
|
460
|
+
?\Closure $comparison = null,
|
|
461
|
+
string|int|\Closure|null $by = null,
|
|
462
|
+
string|int|\Closure|bool $byKey = false,
|
|
463
|
+
): iterable
|
|
467
464
|
{
|
|
468
|
-
|
|
469
|
-
|
|
465
|
+
if ($byKey !== false) {
|
|
466
|
+
if ($by !== null) {
|
|
467
|
+
throw new \InvalidArgumentException('Filter |sort cannot use both $by and $byKey.');
|
|
468
|
+
}
|
|
469
|
+
$by = $byKey === true ? null : $byKey;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
$comparison ??= fn($a, $b) => $a <=> $b;
|
|
473
|
+
$comparison = match (true) {
|
|
474
|
+
$by === null => $comparison,
|
|
475
|
+
$by instanceof \Closure => fn($a, $b) => $comparison($by($a), $by($b)),
|
|
476
|
+
default => fn($a, $b) => $comparison(is_array($a) ? $a[$by] : $a->$by, is_array($b) ? $b[$by] : $b->$by),
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
if (is_array($data)) {
|
|
480
|
+
$byKey ? uksort($data, $comparison) : uasort($data, $comparison);
|
|
481
|
+
return $data;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
$pairs = [];
|
|
485
|
+
foreach ($data as $key => $value) {
|
|
486
|
+
$pairs[] = [$key, $value];
|
|
487
|
+
}
|
|
488
|
+
uasort($pairs, fn($a, $b) => $byKey ? $comparison($a[0], $b[0]) : $comparison($a[1], $b[1]));
|
|
489
|
+
|
|
490
|
+
return new AuxiliaryIterator($pairs);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Groups elements by the element indices and preserves the key association and order.
|
|
496
|
+
* @template K
|
|
497
|
+
* @template V
|
|
498
|
+
* @param iterable<K, V> $data
|
|
499
|
+
* @return iterable<iterable<K, V>>
|
|
500
|
+
*/
|
|
501
|
+
public static function group(iterable $data, string|int|\Closure $by): iterable
|
|
502
|
+
{
|
|
503
|
+
$fn = $by instanceof \Closure ? $by : fn($a) => is_array($a) ? $a[$by] : $a->$by;
|
|
504
|
+
$keys = $groups = [];
|
|
505
|
+
|
|
506
|
+
foreach ($data as $k => $v) {
|
|
507
|
+
$groupKey = $fn($v, $k);
|
|
508
|
+
if (!$groups || $prevKey !== $groupKey) {
|
|
509
|
+
$index = array_search($groupKey, $keys, true);
|
|
510
|
+
if ($index === false) {
|
|
511
|
+
$index = count($keys);
|
|
512
|
+
$keys[$index] = $groupKey;
|
|
513
|
+
}
|
|
514
|
+
$prevKey = $groupKey;
|
|
515
|
+
}
|
|
516
|
+
$groups[$index][] = [$k, $v];
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return new AuxiliaryIterator(array_map(
|
|
520
|
+
fn($key, $group) => [$key, new AuxiliaryIterator($group)],
|
|
521
|
+
$keys,
|
|
522
|
+
$groups,
|
|
523
|
+
));
|
|
470
524
|
}
|
|
471
525
|
|
|
472
526
|
|
|
@@ -522,23 +576,29 @@ final class Filters
|
|
|
522
576
|
|
|
523
577
|
|
|
524
578
|
/**
|
|
525
|
-
* Returns the first
|
|
579
|
+
* Returns the first element in an array or character in a string, or null if none.
|
|
526
580
|
*/
|
|
527
|
-
public static function first(string|
|
|
581
|
+
public static function first(string|iterable $value): mixed
|
|
528
582
|
{
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
583
|
+
if (is_string($value)) {
|
|
584
|
+
return self::substring($value, 0, 1);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
foreach ($value as $item) {
|
|
588
|
+
return $item;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return null;
|
|
532
592
|
}
|
|
533
593
|
|
|
534
594
|
|
|
535
595
|
/**
|
|
536
|
-
* Returns the last
|
|
596
|
+
* Returns the last element in an array or character in a string, or null if none.
|
|
537
597
|
*/
|
|
538
598
|
public static function last(string|array $value): mixed
|
|
539
599
|
{
|
|
540
600
|
return is_array($value)
|
|
541
|
-
? (
|
|
601
|
+
? ($value[array_key_last($value)] ?? null)
|
|
542
602
|
: self::substring($value, -1);
|
|
543
603
|
}
|
|
544
604
|
|
|
@@ -40,7 +40,7 @@ class BlockNode extends StatementNode
|
|
|
40
40
|
{
|
|
41
41
|
$tag->outputMode = $tag::OutputRemoveIndentation;
|
|
42
42
|
$stream = $tag->parser->stream;
|
|
43
|
-
$node = new static;
|
|
43
|
+
$node = $tag->node = new static;
|
|
44
44
|
|
|
45
45
|
if (!$stream->is('|', Token::End)) {
|
|
46
46
|
$layer = $tag->parser->tryConsumeTokenBeforeUnquotedString('local')
|
|
@@ -52,7 +52,6 @@ class BlockNode extends StatementNode
|
|
|
52
52
|
|
|
53
53
|
if (!$node->block->isDynamic()) {
|
|
54
54
|
$parser->checkBlockIsUnique($node->block);
|
|
55
|
-
$tag->data->block = $node->block; // for {include}
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
|
|
@@ -11,9 +11,7 @@ namespace Latte\Essential\Nodes;
|
|
|
11
11
|
|
|
12
12
|
use Latte\CompileException;
|
|
13
13
|
use Latte\Compiler\Escaper;
|
|
14
|
-
use Latte\Compiler\Node;
|
|
15
14
|
use Latte\Compiler\Nodes\AreaNode;
|
|
16
|
-
use Latte\Compiler\Nodes\Php\Expression;
|
|
17
15
|
use Latte\Compiler\Nodes\Php\ExpressionNode;
|
|
18
16
|
use Latte\Compiler\Nodes\Php\ModifierNode;
|
|
19
17
|
use Latte\Compiler\Nodes\StatementNode;
|
|
@@ -36,7 +34,7 @@ class CaptureNode extends StatementNode
|
|
|
36
34
|
{
|
|
37
35
|
$tag->expectArguments();
|
|
38
36
|
$variable = $tag->parser->parseExpression();
|
|
39
|
-
if (
|
|
37
|
+
if (!$variable->isWritable()) {
|
|
40
38
|
$text = '';
|
|
41
39
|
$i = 0;
|
|
42
40
|
while ($token = $tag->parser->stream->peek(--$i)) {
|
|
@@ -45,7 +43,7 @@ class CaptureNode extends StatementNode
|
|
|
45
43
|
|
|
46
44
|
throw new CompileException("It is not possible to write into '$text' in " . $tag->getNotation(), $tag->position);
|
|
47
45
|
}
|
|
48
|
-
$node = new static;
|
|
46
|
+
$node = $tag->node = new static;
|
|
49
47
|
$node->variable = $variable;
|
|
50
48
|
$node->modifier = $tag->parser->parseModifier();
|
|
51
49
|
[$node->content] = yield;
|
|
@@ -80,15 +78,6 @@ class CaptureNode extends StatementNode
|
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
|
|
83
|
-
private static function canBeAssignedTo(Node $node): bool
|
|
84
|
-
{
|
|
85
|
-
return $node instanceof Expression\VariableNode
|
|
86
|
-
|| $node instanceof Expression\ArrayAccessNode
|
|
87
|
-
|| $node instanceof Expression\PropertyFetchNode
|
|
88
|
-
|| $node instanceof Expression\StaticPropertyFetchNode;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
81
|
public function &getIterator(): \Generator
|
|
93
82
|
{
|
|
94
83
|
yield $this->variable;
|
|
@@ -24,6 +24,7 @@ class ContentTypeNode extends StatementNode
|
|
|
24
24
|
{
|
|
25
25
|
public string $contentType;
|
|
26
26
|
public ?string $mimeType = null;
|
|
27
|
+
public bool $inScript;
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
public static function create(Tag $tag, TemplateParser $parser): static
|
|
@@ -32,11 +33,12 @@ class ContentTypeNode extends StatementNode
|
|
|
32
33
|
while (!$tag->parser->stream->consume()->isEnd());
|
|
33
34
|
$type = trim($tag->parser->text);
|
|
34
35
|
|
|
35
|
-
if (!$tag->isInHead() && !($tag->htmlElement?->
|
|
36
|
+
if (!$tag->isInHead() && !($tag->htmlElement?->is('script') && str_contains($type, 'html'))) {
|
|
36
37
|
throw new CompileException('{contentType} is allowed only in template header.', $tag->position);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
$node = new static;
|
|
41
|
+
$node->inScript = (bool) $tag->htmlElement;
|
|
40
42
|
$node->contentType = match (true) {
|
|
41
43
|
str_contains($type, 'html') => ContentType::Html,
|
|
42
44
|
str_contains($type, 'xml') => ContentType::Xml,
|
|
@@ -56,6 +58,11 @@ class ContentTypeNode extends StatementNode
|
|
|
56
58
|
|
|
57
59
|
public function print(PrintContext $context): string
|
|
58
60
|
{
|
|
61
|
+
if ($this->inScript) {
|
|
62
|
+
$context->getEscaper()->enterHtmlRaw($this->contentType);
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
$context->beginEscape()->enterContentType($this->contentType);
|
|
60
67
|
|
|
61
68
|
return $this->mimeType
|
|
@@ -42,11 +42,10 @@ class DefineNode extends StatementNode
|
|
|
42
42
|
$tag->parser->stream->tryConsume('#');
|
|
43
43
|
$name = $tag->parser->parseUnquotedStringOrExpression();
|
|
44
44
|
|
|
45
|
-
$node = new static;
|
|
45
|
+
$node = $tag->node = new static;
|
|
46
46
|
$node->block = new Block($name, $layer, $tag);
|
|
47
47
|
if (!$node->block->isDynamic()) {
|
|
48
48
|
$parser->checkBlockIsUnique($node->block);
|
|
49
|
-
$tag->data->block = $node->block; // for {include}
|
|
50
49
|
$tag->parser->stream->tryConsume(',');
|
|
51
50
|
$node->block->parameters = self::parseParameters($tag);
|
|
52
51
|
}
|
|
@@ -43,7 +43,7 @@ class EmbedNode extends StatementNode
|
|
|
43
43
|
$tag->outputMode = $tag::OutputRemoveIndentation;
|
|
44
44
|
$tag->expectArguments();
|
|
45
45
|
|
|
46
|
-
$node = new static;
|
|
46
|
+
$node = $tag->node = new static;
|
|
47
47
|
$mode = $tag->parser->tryConsumeTokenBeforeUnquotedString('block', 'file')?->text;
|
|
48
48
|
$node->name = $tag->parser->parseUnquotedStringOrExpression();
|
|
49
49
|
$node->mode = $mode ?? ($node->name instanceof StringNode && preg_match('~[\w-]+$~DA', $node->name->value) ? 'block' : 'file');
|
|
@@ -33,8 +33,6 @@ class ExtendsNode extends StatementNode
|
|
|
33
33
|
$node = new static;
|
|
34
34
|
if (!$tag->isInHead()) {
|
|
35
35
|
throw new CompileException("{{$tag->name}} must be placed in template head.", $tag->position);
|
|
36
|
-
} elseif (isset($tag->data->extends)) {
|
|
37
|
-
throw new CompileException("Multiple {{$tag->name}} declarations are not allowed.", $tag->position);
|
|
38
36
|
} elseif ($tag->parser->stream->tryConsume('auto')) {
|
|
39
37
|
$node->extends = new NullNode;
|
|
40
38
|
} elseif ($tag->parser->stream->tryConsume('none')) {
|
|
@@ -42,7 +40,6 @@ class ExtendsNode extends StatementNode
|
|
|
42
40
|
} else {
|
|
43
41
|
$node->extends = $tag->parser->parseUnquotedStringOrExpression();
|
|
44
42
|
}
|
|
45
|
-
$tag->data->extends = true;
|
|
46
43
|
return $node;
|
|
47
44
|
}
|
|
48
45
|
|
|
@@ -34,7 +34,7 @@ class FirstLastSepNode extends StatementNode
|
|
|
34
34
|
/** @return \Generator<int, ?array, array{AreaNode, ?Tag}, static> */
|
|
35
35
|
public static function create(Tag $tag): \Generator
|
|
36
36
|
{
|
|
37
|
-
$node = new static;
|
|
37
|
+
$node = $tag->node = new static;
|
|
38
38
|
$node->name = $tag->name;
|
|
39
39
|
$node->width = $tag->parser->isEnd() ? null : $tag->parser->parseExpression();
|
|
40
40
|
|
|
@@ -35,7 +35,7 @@ class ForNode extends StatementNode
|
|
|
35
35
|
{
|
|
36
36
|
$tag->expectArguments();
|
|
37
37
|
$stream = $tag->parser->stream;
|
|
38
|
-
$node = new static;
|
|
38
|
+
$node = $tag->node = new static;
|
|
39
39
|
while (!$stream->is(';')) {
|
|
40
40
|
$node->init[] = $tag->parser->parseExpression();
|
|
41
41
|
$stream->tryConsume(',');
|