@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,79 +9,36 @@ declare(strict_types=1);
|
|
|
9
9
|
|
|
10
10
|
namespace Latte\Essential;
|
|
11
11
|
|
|
12
|
-
use Latte;
|
|
13
12
|
use Latte\CompileException;
|
|
14
|
-
use Latte\Compiler\ExpressionBuilder;
|
|
15
13
|
use Latte\Compiler\Node;
|
|
16
|
-
use Latte\Compiler\Nodes\
|
|
17
|
-
use Latte\Compiler\Nodes\Php\Expression\FunctionCallNode;
|
|
14
|
+
use Latte\Compiler\Nodes\Php\Expression;
|
|
18
15
|
use Latte\Compiler\Nodes\Php\Expression\VariableNode;
|
|
19
16
|
use Latte\Compiler\Nodes\Php\NameNode;
|
|
20
17
|
use Latte\Compiler\Nodes\TemplateNode;
|
|
21
18
|
use Latte\Compiler\NodeTraverser;
|
|
22
19
|
use Latte\Compiler\PrintContext;
|
|
23
|
-
use Latte\
|
|
20
|
+
use Latte\Engine;
|
|
24
21
|
|
|
25
22
|
|
|
26
23
|
final class Passes
|
|
27
24
|
{
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
* Checks if foreach overrides template variables.
|
|
32
|
-
*/
|
|
33
|
-
public static function overwrittenVariablesPass(TemplateNode $node): void
|
|
34
|
-
{
|
|
35
|
-
$vars = [];
|
|
36
|
-
(new NodeTraverser)->traverse($node, function (Node $node) use (&$vars) {
|
|
37
|
-
if ($node instanceof ForeachNode && $node->checkArgs) {
|
|
38
|
-
foreach ([$node->key, $node->value] as $var) {
|
|
39
|
-
if ($var instanceof VariableNode) {
|
|
40
|
-
$vars[$var->name][] = $node->position->line;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
if ($vars) {
|
|
46
|
-
array_unshift($node->head->children, new AuxiliaryNode(fn(PrintContext $context) => $context->format(
|
|
47
|
-
<<<'XX'
|
|
48
|
-
if (!$this->getReferringTemplate() || $this->getReferenceType() === 'extends') {
|
|
49
|
-
foreach (array_intersect_key(%dump, $this->params) as $ʟ_v => $ʟ_l) {
|
|
50
|
-
trigger_error("Variable \$$ʟ_v overwritten in foreach on line $ʟ_l");
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
XX,
|
|
55
|
-
array_map(fn($l) => implode(', ', $l), $vars),
|
|
56
|
-
)));
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Move TemplatePrintNode to head.
|
|
63
|
-
*/
|
|
64
|
-
public static function moveTemplatePrintToHeadPass(TemplateNode $templateNode): void
|
|
65
|
-
{
|
|
66
|
-
(new NodeTraverser)->traverse($templateNode->main, function (Node $node) use ($templateNode) {
|
|
67
|
-
if ($node instanceof Latte\Essential\Nodes\TemplatePrintNode) {
|
|
68
|
-
array_unshift($templateNode->head->children, $node);
|
|
69
|
-
return new Latte\Compiler\Nodes\NopNode;
|
|
70
|
-
}
|
|
71
|
-
});
|
|
25
|
+
public function __construct(
|
|
26
|
+
private Engine $engine,
|
|
27
|
+
) {
|
|
72
28
|
}
|
|
73
29
|
|
|
74
30
|
|
|
75
31
|
/**
|
|
76
32
|
* Enable custom functions.
|
|
77
33
|
*/
|
|
78
|
-
public
|
|
34
|
+
public function customFunctionsPass(TemplateNode $node): void
|
|
79
35
|
{
|
|
36
|
+
$functions = $this->engine->getFunctions();
|
|
80
37
|
$names = array_keys($functions);
|
|
81
38
|
$names = array_combine(array_map('strtolower', $names), $names);
|
|
82
39
|
|
|
83
40
|
(new NodeTraverser)->traverse($node, function (Node $node) use ($names) {
|
|
84
|
-
if (($node instanceof FunctionCallNode || $node instanceof FunctionCallableNode)
|
|
41
|
+
if (($node instanceof Expression\FunctionCallNode || $node instanceof Expression\FunctionCallableNode)
|
|
85
42
|
&& $node->name instanceof NameNode
|
|
86
43
|
&& ($orig = $names[strtolower((string) $node->name)] ?? null)
|
|
87
44
|
) {
|
|
@@ -89,24 +46,25 @@ final class Passes
|
|
|
89
46
|
trigger_error("Case mismatch on function name '{$node->name}', correct name is '$orig'.", E_USER_WARNING);
|
|
90
47
|
}
|
|
91
48
|
|
|
92
|
-
return
|
|
93
|
-
|
|
49
|
+
return new Expression\AuxiliaryNode(
|
|
50
|
+
fn(PrintContext $context, ...$args) => '($this->global->fn->' . $orig . ')($this, ' . $context->implode($args) . ')',
|
|
94
51
|
$node->args,
|
|
95
|
-
)
|
|
52
|
+
);
|
|
96
53
|
}
|
|
97
54
|
});
|
|
98
55
|
}
|
|
99
56
|
|
|
100
57
|
|
|
101
58
|
/**
|
|
102
|
-
* $ʟ_xxx
|
|
59
|
+
* $ʟ_xxx, $GLOBALS and $this are forbidden
|
|
103
60
|
*/
|
|
104
|
-
public
|
|
61
|
+
public function forbiddenVariablesPass(TemplateNode $node): void
|
|
105
62
|
{
|
|
106
|
-
|
|
63
|
+
$forbidden = $this->engine->isStrictParsing() ? ['GLOBALS', 'this'] : ['GLOBALS'];
|
|
64
|
+
(new NodeTraverser)->traverse($node, function (Node $node) use ($forbidden) {
|
|
107
65
|
if ($node instanceof VariableNode
|
|
108
66
|
&& is_string($node->name)
|
|
109
|
-
&& (str_starts_with($node->name, 'ʟ_'))
|
|
67
|
+
&& (str_starts_with($node->name, 'ʟ_') || in_array($node->name, $forbidden, true))
|
|
110
68
|
) {
|
|
111
69
|
throw new CompileException("Forbidden variable \$$node->name.", $node->position);
|
|
112
70
|
}
|
|
@@ -23,10 +23,15 @@ use Nette\Localization\Translator;
|
|
|
23
23
|
*/
|
|
24
24
|
final class TranslatorExtension extends Latte\Extension
|
|
25
25
|
{
|
|
26
|
+
/** @var callable|Translator|null */
|
|
27
|
+
private $translator;
|
|
28
|
+
|
|
29
|
+
|
|
26
30
|
public function __construct(
|
|
27
|
-
|
|
31
|
+
callable|Translator|null $translator,
|
|
28
32
|
private ?string $key = null,
|
|
29
33
|
) {
|
|
34
|
+
$this->translator = $translator;
|
|
30
35
|
if ($translator instanceof Translator) {
|
|
31
36
|
$this->translator = [$translator, 'translate'];
|
|
32
37
|
}
|
|
@@ -46,7 +46,9 @@ class Helpers
|
|
|
46
46
|
public static function toReflection($callable): \ReflectionFunctionAbstract
|
|
47
47
|
{
|
|
48
48
|
if (is_string($callable) && strpos($callable, '::')) {
|
|
49
|
-
return
|
|
49
|
+
return PHP_VERSION_ID < 80300
|
|
50
|
+
? new \ReflectionMethod($callable)
|
|
51
|
+
: \ReflectionMethod::createFromMethodName($callable);
|
|
50
52
|
} elseif (is_array($callable)) {
|
|
51
53
|
return new \ReflectionMethod($callable[0], $callable[1]);
|
|
52
54
|
} elseif (is_object($callable) && !$callable instanceof \Closure) {
|
|
@@ -17,8 +17,6 @@ use Latte;
|
|
|
17
17
|
*/
|
|
18
18
|
class FileLoader implements Latte\Loader
|
|
19
19
|
{
|
|
20
|
-
use Latte\Strict;
|
|
21
|
-
|
|
22
20
|
protected ?string $baseDir = null;
|
|
23
21
|
|
|
24
22
|
|
|
@@ -52,8 +50,7 @@ class FileLoader implements Latte\Loader
|
|
|
52
50
|
|
|
53
51
|
public function isExpired(string $file, int $time): bool
|
|
54
52
|
{
|
|
55
|
-
|
|
56
|
-
return !$mtime || $mtime > $time;
|
|
53
|
+
return false;
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
|
|
@@ -38,7 +38,7 @@ trait PositionAwareException
|
|
|
38
38
|
$info[] = "in '" . str_replace(dirname($this->sourceName, 2), '...', $this->sourceName) . "'";
|
|
39
39
|
}
|
|
40
40
|
if ($this->position) {
|
|
41
|
-
$info[] = $this->position
|
|
41
|
+
$info[] = $this->position;
|
|
42
42
|
}
|
|
43
43
|
$this->message = $info
|
|
44
44
|
? rtrim($this->origMessage, '.') . ' (' . implode(' ', $info) . ')'
|
|
@@ -59,39 +59,10 @@ class FilterExecutor
|
|
|
59
59
|
*/
|
|
60
60
|
public function __get(string $name): callable
|
|
61
61
|
{
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
array_unshift($args, $info = new FilterInfo);
|
|
67
|
-
if ($args[1] instanceof HtmlStringable) {
|
|
68
|
-
$args[1] = $args[1]->__toString();
|
|
69
|
-
$info->contentType = ContentType::Html;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
$res = $callback(...$args);
|
|
73
|
-
return $info->contentType === ContentType::Html
|
|
74
|
-
? new Html($res)
|
|
75
|
-
: $res;
|
|
76
|
-
};
|
|
77
|
-
} else { // classic filter
|
|
78
|
-
return $this->$name = $callback;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// dynamic filter
|
|
83
|
-
foreach ($this->_dynamic as $loader) {
|
|
84
|
-
$callback = $loader($name);
|
|
85
|
-
if ($callback !== null) {
|
|
86
|
-
$this->_static[$name] = [$callback, null];
|
|
87
|
-
return $this->__get($name);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
$hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name))
|
|
92
|
-
? ", did you mean '$t'?"
|
|
93
|
-
: '.';
|
|
94
|
-
throw new \LogicException("Filter '$name' is not defined$hint");
|
|
62
|
+
[$callback, $infoAware] = $this->prepareFilter($name);
|
|
63
|
+
return $this->$name = $infoAware
|
|
64
|
+
? fn(...$args) => $this->callInfoAwareAsClassic($callback, ...$args)
|
|
65
|
+
: $callback;
|
|
95
66
|
}
|
|
96
67
|
|
|
97
68
|
|
|
@@ -100,31 +71,23 @@ class FilterExecutor
|
|
|
100
71
|
*/
|
|
101
72
|
public function filterContent(string $name, FilterInfo $info, mixed ...$args): mixed
|
|
102
73
|
{
|
|
103
|
-
if (!isset($this->_static[$name])) {
|
|
104
|
-
$hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name))
|
|
105
|
-
? ", did you mean '$t'?"
|
|
106
|
-
: '.';
|
|
107
|
-
throw new \LogicException("Filter |$name is not defined$hint");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
[$callback, $aware] = $this->prepareFilter($name);
|
|
111
|
-
|
|
112
74
|
if ($info->contentType === ContentType::Html && $args[0] instanceof HtmlStringable) {
|
|
113
75
|
$args[0] = $args[0]->__toString();
|
|
114
76
|
}
|
|
115
77
|
|
|
116
|
-
|
|
78
|
+
[$callback, $infoAware] = $this->prepareFilter($name);
|
|
79
|
+
if ($infoAware) {
|
|
117
80
|
array_unshift($args, $info);
|
|
118
81
|
return $callback(...$args);
|
|
119
82
|
}
|
|
120
83
|
|
|
121
84
|
// classic filter
|
|
122
85
|
if ($info->contentType !== ContentType::Text) {
|
|
123
|
-
throw new Latte\RuntimeException("Filter |$name is called with incompatible content type " . strtoupper($info->contentType)
|
|
86
|
+
throw new Latte\RuntimeException("Filter |$name is called with incompatible content type " . strtoupper($info->contentType ?? 'NULL')
|
|
124
87
|
. ($info->contentType === ContentType::Html ? ', try to prepend |stripHtml.' : '.'));
|
|
125
88
|
}
|
|
126
89
|
|
|
127
|
-
$res =
|
|
90
|
+
$res = $callback(...$args);
|
|
128
91
|
if ($res instanceof HtmlStringable) {
|
|
129
92
|
trigger_error("Filter |$name should be changed to content-aware filter.");
|
|
130
93
|
$info->contentType = ContentType::Html;
|
|
@@ -140,13 +103,42 @@ class FilterExecutor
|
|
|
140
103
|
*/
|
|
141
104
|
private function prepareFilter(string $name): array
|
|
142
105
|
{
|
|
143
|
-
if (
|
|
144
|
-
$
|
|
145
|
-
$this->_static[$name]
|
|
146
|
-
|
|
147
|
-
|
|
106
|
+
if (isset($this->_static[$name])) {
|
|
107
|
+
$this->_static[$name][1] ??= $this->isInfoAware($this->_static[$name][0]);
|
|
108
|
+
return $this->_static[$name];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
foreach ($this->_dynamic as $loader) {
|
|
112
|
+
if ($callback = $loader($name)) {
|
|
113
|
+
return $this->_static[$name] = [$callback, $this->isInfoAware($callback)];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
$hint = ($t = Helpers::getSuggestion(array_keys($this->_static), $name))
|
|
118
|
+
? ", did you mean '$t'?"
|
|
119
|
+
: '.';
|
|
120
|
+
throw new \LogicException("Filter '$name' is not defined$hint");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
private function isInfoAware(callable $filter): bool
|
|
125
|
+
{
|
|
126
|
+
$params = Helpers::toReflection($filter)->getParameters();
|
|
127
|
+
return $params && (string) $params[0]->getType() === FilterInfo::class;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
private function callInfoAwareAsClassic(callable $filter, mixed ...$args): mixed
|
|
132
|
+
{
|
|
133
|
+
array_unshift($args, $info = new FilterInfo);
|
|
134
|
+
if ($args[1] instanceof HtmlStringable) {
|
|
135
|
+
$args[1] = $args[1]->__toString();
|
|
136
|
+
$info->contentType = ContentType::Html;
|
|
148
137
|
}
|
|
149
138
|
|
|
150
|
-
|
|
139
|
+
$res = $filter(...$args);
|
|
140
|
+
return $info->contentType === ContentType::Html
|
|
141
|
+
? new Html($res)
|
|
142
|
+
: $res;
|
|
151
143
|
}
|
|
152
144
|
}
|
|
@@ -24,9 +24,6 @@ class Filters
|
|
|
24
24
|
/** @deprecated */
|
|
25
25
|
public static string $dateFormat = "j.\u{a0}n.\u{a0}Y";
|
|
26
26
|
|
|
27
|
-
/** @internal use XML syntax? */
|
|
28
|
-
public static bool $xml = false;
|
|
29
|
-
|
|
30
27
|
|
|
31
28
|
/**
|
|
32
29
|
* Escapes string for use everywhere inside HTML (except for comments).
|
|
@@ -75,9 +72,12 @@ class Filters
|
|
|
75
72
|
public static function escapeHtmlTag($s): string
|
|
76
73
|
{
|
|
77
74
|
$s = (string) $s;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
75
|
+
$s = htmlspecialchars($s, ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8');
|
|
76
|
+
return preg_replace_callback(
|
|
77
|
+
'#[=/\s]#',
|
|
78
|
+
fn($m) => '&#' . ord($m[0]) . ';',
|
|
79
|
+
$s,
|
|
80
|
+
);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
|
|
@@ -100,6 +100,28 @@ class Filters
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Escapes HTML for usage in <script type=text/html>
|
|
105
|
+
*/
|
|
106
|
+
public static function escapeHtmlRawTextHtml($s): string
|
|
107
|
+
{
|
|
108
|
+
if ($s instanceof HtmlStringable || $s instanceof Nette\HtmlStringable) {
|
|
109
|
+
return self::convertHtmlToHtmlRawText($s->__toString());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return htmlspecialchars((string) $s, ENT_QUOTES | ENT_HTML5 | ENT_SUBSTITUTE, 'UTF-8');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Escapes only quotes.
|
|
118
|
+
*/
|
|
119
|
+
public static function escapeHtmlQuotes($s): string
|
|
120
|
+
{
|
|
121
|
+
return strtr((string) $s, ['"' => '"', "'" => ''']);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
103
125
|
/**
|
|
104
126
|
* Escapes string for use everywhere inside XML (except for comments and tags).
|
|
105
127
|
*/
|
|
@@ -122,10 +144,12 @@ class Filters
|
|
|
122
144
|
*/
|
|
123
145
|
public static function escapeXmlTag($s): string
|
|
124
146
|
{
|
|
125
|
-
$s = (string) $s;
|
|
126
|
-
return
|
|
127
|
-
|
|
128
|
-
|
|
147
|
+
$s = self::escapeXml((string) $s);
|
|
148
|
+
return preg_replace_callback(
|
|
149
|
+
'#[=/\s]#',
|
|
150
|
+
fn($m) => '&#' . ord($m[0]) . ';',
|
|
151
|
+
$s,
|
|
152
|
+
);
|
|
129
153
|
}
|
|
130
154
|
|
|
131
155
|
|
|
@@ -150,7 +174,7 @@ class Filters
|
|
|
150
174
|
|
|
151
175
|
$json = json_encode($s, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
|
|
152
176
|
if ($error = json_last_error()) {
|
|
153
|
-
throw new Latte\RuntimeException(json_last_error_msg()
|
|
177
|
+
throw new Latte\RuntimeException(json_last_error_msg());
|
|
154
178
|
}
|
|
155
179
|
|
|
156
180
|
return str_replace([']]>', '<!', '</'], [']]\u003E', '\u003C!', '<\/'], $json);
|
|
@@ -169,15 +193,6 @@ class Filters
|
|
|
169
193
|
}
|
|
170
194
|
|
|
171
195
|
|
|
172
|
-
/**
|
|
173
|
-
* Converts JS and CSS for usage in <script> or <style>
|
|
174
|
-
*/
|
|
175
|
-
public static function convertJSToHtmlRawText($s): string
|
|
176
|
-
{
|
|
177
|
-
return preg_replace('#</(script|style)#i', '<\/$1', (string) $s);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
196
|
/**
|
|
182
197
|
* Converts ... to ...
|
|
183
198
|
*/
|
|
@@ -202,29 +217,29 @@ class Filters
|
|
|
202
217
|
|
|
203
218
|
|
|
204
219
|
/**
|
|
205
|
-
* Converts
|
|
220
|
+
* Converts JS and CSS for usage in <script> or <style>
|
|
206
221
|
*/
|
|
207
|
-
public static function
|
|
222
|
+
public static function convertJSToHtmlRawText($s): string
|
|
208
223
|
{
|
|
209
|
-
return
|
|
224
|
+
return preg_replace('#</(script|style)#i', '<\/$1', (string) $s);
|
|
210
225
|
}
|
|
211
226
|
|
|
212
227
|
|
|
213
228
|
/**
|
|
214
|
-
*
|
|
229
|
+
* Sanitizes <script> in <script type=text/html>
|
|
215
230
|
*/
|
|
216
|
-
public static function
|
|
231
|
+
public static function convertHtmlToHtmlRawText(string $s): string
|
|
217
232
|
{
|
|
218
|
-
return '
|
|
233
|
+
return preg_replace('#(</?)(script)#i', '$1x-$2', $s);
|
|
219
234
|
}
|
|
220
235
|
|
|
221
236
|
|
|
222
237
|
/**
|
|
223
|
-
* Converts HTML quoted attribute to
|
|
238
|
+
* Converts HTML text to quoted attribute. The quotation marks need to be escaped.
|
|
224
239
|
*/
|
|
225
|
-
public static function
|
|
240
|
+
public static function convertHtmlToHtmlAttr(string $s): string
|
|
226
241
|
{
|
|
227
|
-
return
|
|
242
|
+
return self::escapeHtmlAttr($s, false);
|
|
228
243
|
}
|
|
229
244
|
|
|
230
245
|
|
|
@@ -241,12 +256,28 @@ class Filters
|
|
|
241
256
|
/**
|
|
242
257
|
* Sanitizes string for use inside href attribute.
|
|
243
258
|
*/
|
|
244
|
-
public static function safeUrl(
|
|
259
|
+
public static function safeUrl($s): string
|
|
245
260
|
{
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
$s = $s instanceof HtmlStringable
|
|
262
|
+
? self::convertHtmlToText((string) $s)
|
|
263
|
+
: (string) $s;
|
|
249
264
|
|
|
250
265
|
return preg_match('~^(?:(?:https?|ftp)://[^@]+(?:/.*)?|(?:mailto|tel|sms):.+|[/?#].*|[^:]+)$~Di', $s) ? $s : '';
|
|
251
266
|
}
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Validates HTML tag name.
|
|
271
|
+
*/
|
|
272
|
+
public static function safeTag(mixed $name, bool $xml = false): string
|
|
273
|
+
{
|
|
274
|
+
if (!is_string($name)) {
|
|
275
|
+
throw new Latte\RuntimeException('Tag name must be string, ' . get_debug_type($name) . ' given');
|
|
276
|
+
} elseif (!preg_match('~' . Latte\Compiler\TemplateLexer::ReTagName . '$~DA', $name)) {
|
|
277
|
+
throw new Latte\RuntimeException("Invalid tag name '$name'");
|
|
278
|
+
} elseif (!$xml && in_array(strtolower($name), ['style', 'script'], true)) {
|
|
279
|
+
throw new Latte\RuntimeException("Forbidden variable tag name <$name>");
|
|
280
|
+
}
|
|
281
|
+
return $name;
|
|
282
|
+
}
|
|
252
283
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This file is part of the Latte (https://latte.nette.org)
|
|
5
|
+
* Copyright (c) 2008 David Grudl (https://davidgrudl.com)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare(strict_types=1);
|
|
9
|
+
|
|
10
|
+
namespace Latte\Runtime;
|
|
11
|
+
|
|
12
|
+
use Latte\Helpers;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Functions executor.
|
|
17
|
+
* @internal
|
|
18
|
+
*/
|
|
19
|
+
#[\AllowDynamicProperties]
|
|
20
|
+
class FunctionExecutor
|
|
21
|
+
{
|
|
22
|
+
/** @var callable[] */
|
|
23
|
+
private array $_list = [];
|
|
24
|
+
|
|
25
|
+
/** @var bool[] */
|
|
26
|
+
private array $_aware = [];
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Registers run-time function.
|
|
31
|
+
*/
|
|
32
|
+
public function add(string $name, callable $callback): static
|
|
33
|
+
{
|
|
34
|
+
$this->_list[$name] = $callback;
|
|
35
|
+
unset($this->$name, $this->_aware[$name]);
|
|
36
|
+
return $this;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns all run-time functions.
|
|
42
|
+
* @return callable[]
|
|
43
|
+
*/
|
|
44
|
+
public function getAll(): array
|
|
45
|
+
{
|
|
46
|
+
return $this->_list;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
public function __get(string $name): callable
|
|
51
|
+
{
|
|
52
|
+
$callback = $this->_list[$name] ?? null;
|
|
53
|
+
if (!$callback) {
|
|
54
|
+
$hint = ($t = Helpers::getSuggestion(array_keys($this->_list), $name))
|
|
55
|
+
? ", did you mean '$t'?"
|
|
56
|
+
: '.';
|
|
57
|
+
throw new \LogicException("Function '$name' is not defined$hint");
|
|
58
|
+
|
|
59
|
+
} elseif (!isset($this->_aware[$name])) {
|
|
60
|
+
$params = Helpers::toReflection($callback)->getParameters();
|
|
61
|
+
$this->_aware[$name] = $params && (string) $params[0]->getType() === Template::class;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return $this->$name = $this->_aware[$name]
|
|
65
|
+
? $callback
|
|
66
|
+
: fn($info, ...$args) => $callback(...$args);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -19,8 +19,6 @@ use Latte\Engine;
|
|
|
19
19
|
*/
|
|
20
20
|
class Template
|
|
21
21
|
{
|
|
22
|
-
use Latte\Strict;
|
|
23
|
-
|
|
24
22
|
public const
|
|
25
23
|
LayerTop = 0,
|
|
26
24
|
LayerSnippet = 'snippet',
|
|
@@ -28,6 +26,8 @@ class Template
|
|
|
28
26
|
|
|
29
27
|
public const ContentType = Latte\ContentType::Html;
|
|
30
28
|
|
|
29
|
+
public const Source = null;
|
|
30
|
+
|
|
31
31
|
public const Blocks = [];
|
|
32
32
|
|
|
33
33
|
/** global accumulators for intermediate results */
|
|
@@ -142,8 +142,6 @@ class Template
|
|
|
142
142
|
$this->parentName = ($this->global->coreParentFinder)($this);
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
Filters::$xml = static::ContentType === Latte\ContentType::Xml;
|
|
146
|
-
|
|
147
145
|
if ($this->referenceType === 'import') {
|
|
148
146
|
if ($this->parentName) {
|
|
149
147
|
throw new Latte\RuntimeException('Imported template cannot use {extends} or {layout}, use {import}');
|
|
@@ -172,7 +170,7 @@ class Template
|
|
|
172
170
|
$name = $this->engine->getLoader()->getReferredName($name, $this->name);
|
|
173
171
|
$referred = $referenceType === 'sandbox'
|
|
174
172
|
? (clone $this->engine)->setSandboxMode()->createTemplate($name, $params)
|
|
175
|
-
: $this->engine->createTemplate($name, $params);
|
|
173
|
+
: $this->engine->createTemplate($name, $params, clearCache: false);
|
|
176
174
|
|
|
177
175
|
$referred->referringTemplate = $this;
|
|
178
176
|
$referred->referenceType = $referenceType;
|
|
@@ -25,6 +25,7 @@ class FunctionCallNode extends Expression\FunctionCallNode
|
|
|
25
25
|
{
|
|
26
26
|
return '$this->global->sandbox->call('
|
|
27
27
|
. $context->memberAsString($this->name) . ', '
|
|
28
|
-
.
|
|
28
|
+
. $context->argumentsAsArray($this->args) . ')';
|
|
29
|
+
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -26,7 +26,7 @@ class MethodCallNode extends Expression\MethodCallNode
|
|
|
26
26
|
return '$this->global->sandbox->callMethod('
|
|
27
27
|
. $this->object->print($context) . ', '
|
|
28
28
|
. $context->memberAsString($this->name) . ', '
|
|
29
|
-
.
|
|
29
|
+
. $context->argumentsAsArray($this->args)
|
|
30
30
|
. ', ' . var_export($this->nullsafe, true) . ')';
|
|
31
31
|
}
|
|
32
32
|
}
|