@vituum/vite-plugin-latte 0.1.8
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/README.md +39 -0
- package/handler.js +9 -0
- package/index.js +138 -0
- package/index.php +173 -0
- package/latte/AssetFilter.php +11 -0
- package/latte/FetchFunction.php +28 -0
- package/latte/IconFilter.php +14 -0
- package/latte/JsonTag.php +64 -0
- package/latte/PlaceholderFunction.php +11 -0
- package/latte/RandomColorFunction.php +14 -0
- package/latte/TelFilter.php +22 -0
- package/package.json +35 -0
- package/vendor/autoload.php +7 -0
- package/vendor/bin/latte-lint +107 -0
- package/vendor/composer/ClassLoader.php +572 -0
- package/vendor/composer/InstalledVersions.php +350 -0
- package/vendor/composer/LICENSE +21 -0
- package/vendor/composer/autoload_classmap.php +183 -0
- package/vendor/composer/autoload_namespaces.php +9 -0
- package/vendor/composer/autoload_psr4.php +9 -0
- package/vendor/composer/autoload_real.php +57 -0
- package/vendor/composer/autoload_static.php +193 -0
- package/vendor/composer/installed.json +93 -0
- package/vendor/composer/installed.php +32 -0
- package/vendor/composer/platform_check.php +26 -0
- package/vendor/latte/latte/bin/latte-lint +29 -0
- package/vendor/latte/latte/composer.json +53 -0
- package/vendor/latte/latte/license.md +60 -0
- package/vendor/latte/latte/ncs.php +14 -0
- package/vendor/latte/latte/ncs.xml +9 -0
- package/vendor/latte/latte/readme.md +21 -0
- package/vendor/latte/latte/src/Bridges/Tracy/BlueScreenPanel.php +114 -0
- package/vendor/latte/latte/src/Bridges/Tracy/LattePanel.php +99 -0
- package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.panel.phtml +98 -0
- package/vendor/latte/latte/src/Bridges/Tracy/templates/LattePanel.tab.phtml +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Block.php +44 -0
- package/vendor/latte/latte/src/Latte/Compiler/Escaper.php +244 -0
- package/vendor/latte/latte/src/Latte/Compiler/ExpressionBuilder.php +128 -0
- package/vendor/latte/latte/src/Latte/Compiler/Node.php +28 -0
- package/vendor/latte/latte/src/Latte/Compiler/NodeHelpers.php +132 -0
- package/vendor/latte/latte/src/Latte/Compiler/NodeTraverser.php +77 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/AreaNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/AuxiliaryNode.php +27 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/FragmentNode.php +59 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/AttributeNode.php +48 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/BogusTagNode.php +46 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/CommentNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/ElementNode.php +129 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Html/QuotedValue.php +53 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/NopNode.php +21 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ArgumentNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ComplexTypeNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayAccessNode.php +41 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayItemNode.php +52 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ArrayNode.php +94 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/AssignOpNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/BinaryOpNode.php +65 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CastNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClassConstantFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/CloneNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureNode.php +73 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ClosureUseNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ConstantFetchNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/EmptyNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/ErrorSuppressNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FilterCallNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/FunctionCallableNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InRangeNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/InstanceofNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/IssetNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MatchNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallNode.php +50 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/MethodCallableNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NewNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/NotNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PostOpNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PreOpNode.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/PropertyFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallNode.php +55 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticCallableNode.php +47 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/StaticPropertyFetchNode.php +42 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/TernaryNode.php +50 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/UnaryOpNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Expression/VariableNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ExpressionNode.php +17 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/FilterNode.php +67 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IdentifierNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/IntersectionTypeNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/MatchArmNode.php +45 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ModifierNode.php +107 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NameNode.php +89 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/NullableTypeNode.php +35 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ParameterNode.php +54 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/BooleanNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringNode.php +80 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/EncapsedStringPartNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/FloatNode.php +63 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/IntegerNode.php +70 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/NullNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/Scalar/StringNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/ScalarNode.php +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/SuperiorTypeNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/UnionTypeNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/Php/VarLikeIdentifierNode.php +21 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/StatementNode.php +15 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/TemplateNode.php +34 -0
- package/vendor/latte/latte/src/Latte/Compiler/Nodes/TextNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Compiler/PhpHelpers.php +227 -0
- package/vendor/latte/latte/src/Latte/Compiler/Position.php +43 -0
- package/vendor/latte/latte/src/Latte/Compiler/PrintContext.php +334 -0
- package/vendor/latte/latte/src/Latte/Compiler/Tag.php +117 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagLexer.php +399 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagParser.php +388 -0
- package/vendor/latte/latte/src/Latte/Compiler/TagParserData.php +580 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateGenerator.php +206 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateLexer.php +391 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParser.php +448 -0
- package/vendor/latte/latte/src/Latte/Compiler/TemplateParserHtml.php +506 -0
- package/vendor/latte/latte/src/Latte/Compiler/Token.php +259 -0
- package/vendor/latte/latte/src/Latte/Compiler/TokenStream.php +151 -0
- package/vendor/latte/latte/src/Latte/ContentType.php +22 -0
- package/vendor/latte/latte/src/Latte/Engine.php +591 -0
- package/vendor/latte/latte/src/Latte/Essential/Blueprint.php +160 -0
- package/vendor/latte/latte/src/Latte/Essential/CachingIterator.php +232 -0
- package/vendor/latte/latte/src/Latte/Essential/CoreExtension.php +232 -0
- package/vendor/latte/latte/src/Latte/Essential/Filters.php +591 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/BlockNode.php +161 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/CaptureNode.php +98 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ContentTypeNode.php +74 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DebugbreakNode.php +53 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DefineNode.php +134 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DoNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/DumpNode.php +58 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/EmbedNode.php +125 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ExtendsNode.php +60 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/FirstLastSepNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForNode.php +91 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ForeachNode.php +155 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfChangedNode.php +141 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfContentNode.php +82 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IfNode.php +180 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ImportNode.php +49 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeBlockNode.php +140 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IncludeFileNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/IterateWhileNode.php +94 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NAttrNode.php +105 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NClassNode.php +54 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/NTagNode.php +79 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/ParametersNode.php +84 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/PrintNode.php +70 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/RawPhpNode.php +46 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/RollbackNode.php +37 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SkipNode.php +71 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SpacelessNode.php +62 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/SwitchNode.php +118 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplatePrintNode.php +39 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TemplateTypeNode.php +38 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TraceNode.php +35 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TranslateNode.php +104 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/TryNode.php +75 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarNode.php +106 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarPrintNode.php +40 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/VarTypeNode.php +36 -0
- package/vendor/latte/latte/src/Latte/Essential/Nodes/WhileNode.php +81 -0
- package/vendor/latte/latte/src/Latte/Essential/Passes.php +115 -0
- package/vendor/latte/latte/src/Latte/Essential/RawPhpExtension.php +28 -0
- package/vendor/latte/latte/src/Latte/Essential/RollbackException.php +16 -0
- package/vendor/latte/latte/src/Latte/Essential/Tracer.php +117 -0
- package/vendor/latte/latte/src/Latte/Essential/TranslatorExtension.php +101 -0
- package/vendor/latte/latte/src/Latte/Extension.php +97 -0
- package/vendor/latte/latte/src/Latte/Helpers.php +96 -0
- package/vendor/latte/latte/src/Latte/Loader.php +37 -0
- package/vendor/latte/latte/src/Latte/Loaders/FileLoader.php +95 -0
- package/vendor/latte/latte/src/Latte/Loaders/StringLoader.php +76 -0
- package/vendor/latte/latte/src/Latte/Policy.php +24 -0
- package/vendor/latte/latte/src/Latte/PositionAwareException.php +50 -0
- package/vendor/latte/latte/src/Latte/Runtime/Block.php +24 -0
- package/vendor/latte/latte/src/Latte/Runtime/FilterExecutor.php +151 -0
- package/vendor/latte/latte/src/Latte/Runtime/FilterInfo.php +39 -0
- package/vendor/latte/latte/src/Latte/Runtime/Filters.php +234 -0
- package/vendor/latte/latte/src/Latte/Runtime/Html.php +35 -0
- package/vendor/latte/latte/src/Latte/Runtime/HtmlStringable.php +17 -0
- package/vendor/latte/latte/src/Latte/Runtime/Template.php +381 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/FunctionCallableNode.php +29 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/MethodCallableNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/PropertyFetchNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/SandboxNode.php +73 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallNode.php +31 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticCallableNode.php +30 -0
- package/vendor/latte/latte/src/Latte/Sandbox/Nodes/StaticPropertyFetchNode.php +32 -0
- package/vendor/latte/latte/src/Latte/Sandbox/RuntimeChecker.php +116 -0
- package/vendor/latte/latte/src/Latte/Sandbox/SandboxExtension.php +132 -0
- package/vendor/latte/latte/src/Latte/Sandbox/SecurityPolicy.php +185 -0
- package/vendor/latte/latte/src/Latte/Strict.php +101 -0
- package/vendor/latte/latte/src/Latte/attributes.php +24 -0
- package/vendor/latte/latte/src/Latte/exceptions.php +69 -0
- package/vendor/latte/latte/src/Tools/Linter.php +175 -0
|
@@ -0,0 +1,116 @@
|
|
|
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\Sandbox;
|
|
11
|
+
|
|
12
|
+
use Latte;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/** @internal */
|
|
16
|
+
final class RuntimeChecker
|
|
17
|
+
{
|
|
18
|
+
use Latte\Strict;
|
|
19
|
+
|
|
20
|
+
public function __construct(
|
|
21
|
+
public Latte\Policy $policy,
|
|
22
|
+
) {
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
public function call(mixed $callable, array $args): mixed
|
|
27
|
+
{
|
|
28
|
+
self::checkCallable($callable);
|
|
29
|
+
self::args(...$args);
|
|
30
|
+
return $callable(...$args);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
public function callMethod(mixed $object, mixed $method, array $args, bool $nullsafe = false): mixed
|
|
35
|
+
{
|
|
36
|
+
if ($object === null) {
|
|
37
|
+
if ($nullsafe) {
|
|
38
|
+
throw new \Error("Call to a member function $method() on null");
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
|
|
42
|
+
} elseif (!is_object($object) || !is_string($method)) {
|
|
43
|
+
throw new Latte\SecurityViolationException('Invalid callable.');
|
|
44
|
+
|
|
45
|
+
} elseif (!$this->policy->isMethodAllowed($class = $object::class, $method)) {
|
|
46
|
+
throw new Latte\SecurityViolationException("Calling $class::$method() is not allowed.");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
self::args(...$args);
|
|
50
|
+
return [$object, $method](...$args);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
public function closure(mixed $callable): \Closure
|
|
55
|
+
{
|
|
56
|
+
self::checkCallable($callable);
|
|
57
|
+
return \Closure::fromCallable($callable);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
public function args(...$args): array
|
|
62
|
+
{
|
|
63
|
+
foreach ($args as $arg) {
|
|
64
|
+
if (
|
|
65
|
+
is_array($arg)
|
|
66
|
+
&& is_callable($arg, true, $text)
|
|
67
|
+
&& !$this->policy->isMethodAllowed(is_object($arg[0]) ? $arg[0]::class : $arg[0], $arg[1])
|
|
68
|
+
) {
|
|
69
|
+
throw new Latte\SecurityViolationException("Calling $text() is not allowed.");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return $args;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
public function prop(mixed $object, mixed $property): mixed
|
|
78
|
+
{
|
|
79
|
+
$class = is_object($object) ? $object::class : $object;
|
|
80
|
+
if (is_string($class) && !$this->policy->isPropertyAllowed($class, (string) $property)) {
|
|
81
|
+
throw new Latte\SecurityViolationException("Access to '$property' property on a $class object is not allowed.");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return $object;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
private function checkCallable(mixed $callable): void
|
|
89
|
+
{
|
|
90
|
+
if (!is_callable($callable)) {
|
|
91
|
+
throw new Latte\SecurityViolationException('Invalid callable.');
|
|
92
|
+
|
|
93
|
+
} elseif (is_string($callable)) {
|
|
94
|
+
$parts = explode('::', $callable);
|
|
95
|
+
$allowed = count($parts) === 1
|
|
96
|
+
? $this->policy->isFunctionAllowed($parts[0])
|
|
97
|
+
: $this->policy->isMethodAllowed(...$parts);
|
|
98
|
+
|
|
99
|
+
} elseif (is_array($callable)) {
|
|
100
|
+
$allowed = $this->policy->isMethodAllowed(is_object($callable[0]) ? $callable[0]::class : $callable[0], $callable[1]);
|
|
101
|
+
|
|
102
|
+
} elseif (is_object($callable)) {
|
|
103
|
+
$allowed = $callable instanceof \Closure
|
|
104
|
+
? true
|
|
105
|
+
: $this->policy->isMethodAllowed($callable::class, '__invoke');
|
|
106
|
+
|
|
107
|
+
} else {
|
|
108
|
+
$allowed = false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!$allowed) {
|
|
112
|
+
is_callable($callable, false, $text);
|
|
113
|
+
throw new Latte\SecurityViolationException("Calling $text() is not allowed.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
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\Sandbox;
|
|
11
|
+
|
|
12
|
+
use Latte;
|
|
13
|
+
use Latte\Compiler\ExpressionBuilder;
|
|
14
|
+
use Latte\Compiler\Node;
|
|
15
|
+
use Latte\Compiler\Nodes\Php;
|
|
16
|
+
use Latte\Compiler\Nodes\Php\Expression;
|
|
17
|
+
use Latte\Compiler\Nodes\TemplateNode;
|
|
18
|
+
use Latte\Compiler\NodeTraverser;
|
|
19
|
+
use Latte\Engine;
|
|
20
|
+
use Latte\SecurityViolationException;
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Security protection for the sandbox.
|
|
25
|
+
*/
|
|
26
|
+
final class SandboxExtension extends Latte\Extension
|
|
27
|
+
{
|
|
28
|
+
use Latte\Strict;
|
|
29
|
+
|
|
30
|
+
private ?Latte\Policy $policy;
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
public function beforeCompile(Engine $engine): void
|
|
34
|
+
{
|
|
35
|
+
$this->policy = $engine->getPolicy(effective: true);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
public function getTags(): array
|
|
40
|
+
{
|
|
41
|
+
return [
|
|
42
|
+
'sandbox' => [Nodes\SandboxNode::class, 'create'],
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
public function getPasses(): array
|
|
48
|
+
{
|
|
49
|
+
return $this->policy
|
|
50
|
+
? ['sandbox' => self::order([$this, 'processPass'], before: '*')]
|
|
51
|
+
: [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
public function beforeRender(Engine $engine): void
|
|
56
|
+
{
|
|
57
|
+
if ($policy = $engine->getPolicy()) {
|
|
58
|
+
$engine->addProvider('sandbox', new RuntimeChecker($policy));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
public function getCacheKey(Engine $engine): mixed
|
|
64
|
+
{
|
|
65
|
+
return (bool) $engine->getPolicy(effective: true);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
public function processPass(TemplateNode $node): void
|
|
70
|
+
{
|
|
71
|
+
(new NodeTraverser)->traverse($node, leave: \Closure::fromCallable([$this, 'sandboxVisitor']));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
private function sandboxVisitor(Node $node): Node
|
|
76
|
+
{
|
|
77
|
+
if ($node instanceof Expression\VariableNode) {
|
|
78
|
+
if ($node->name === 'this') {
|
|
79
|
+
throw new SecurityViolationException("Forbidden variable \${$node->name}.", $node->position);
|
|
80
|
+
} elseif (!is_string($node->name)) {
|
|
81
|
+
throw new SecurityViolationException('Forbidden variable variables.', $node->position);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return $node;
|
|
85
|
+
|
|
86
|
+
} elseif ($node instanceof Expression\NewNode) {
|
|
87
|
+
throw new SecurityViolationException("Forbidden keyword 'new'", $node->position);
|
|
88
|
+
|
|
89
|
+
} elseif ($node instanceof Expression\FunctionCallNode
|
|
90
|
+
&& $node->name instanceof Php\NameNode
|
|
91
|
+
) {
|
|
92
|
+
if (!$this->policy->isFunctionAllowed((string) $node->name)) {
|
|
93
|
+
throw new SecurityViolationException("Function $node->name() is not allowed.", $node->position);
|
|
94
|
+
|
|
95
|
+
} elseif ($node->args) {
|
|
96
|
+
$arg = ExpressionBuilder::variable('$this')->property('global')->property('sandbox')->method('args', $node->args)
|
|
97
|
+
->build();
|
|
98
|
+
$node->args = [new Php\ArgumentNode($arg, unpack: true)];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return $node;
|
|
102
|
+
|
|
103
|
+
} elseif ($node instanceof Php\FilterNode) {
|
|
104
|
+
$name = (string) $node->name;
|
|
105
|
+
if (!$this->policy->isFilterAllowed($name)) {
|
|
106
|
+
throw new SecurityViolationException("Filter |$name is not allowed.", $node->position);
|
|
107
|
+
|
|
108
|
+
} elseif ($node->args) {
|
|
109
|
+
$arg = ExpressionBuilder::variable('$this')->property('global')->property('sandbox')->method('args', $node->args)
|
|
110
|
+
->build();
|
|
111
|
+
$node->args = [new Php\ArgumentNode($arg, unpack: true)];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return $node;
|
|
115
|
+
|
|
116
|
+
} elseif ($node instanceof Expression\PropertyFetchNode
|
|
117
|
+
|| $node instanceof Expression\StaticPropertyFetchNode
|
|
118
|
+
|| $node instanceof Expression\FunctionCallNode
|
|
119
|
+
|| $node instanceof Expression\FunctionCallableNode
|
|
120
|
+
|| $node instanceof Expression\MethodCallNode
|
|
121
|
+
|| $node instanceof Expression\MethodCallableNode
|
|
122
|
+
|| $node instanceof Expression\StaticCallNode
|
|
123
|
+
|| $node instanceof Expression\StaticCallableNode
|
|
124
|
+
) {
|
|
125
|
+
$class = namespace\Nodes::class . strrchr($node::class, '\\');
|
|
126
|
+
return new $class($node);
|
|
127
|
+
|
|
128
|
+
} else {
|
|
129
|
+
return $node;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
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\Sandbox;
|
|
11
|
+
|
|
12
|
+
use Latte;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Default-deny policy.
|
|
17
|
+
*/
|
|
18
|
+
class SecurityPolicy implements Latte\Policy
|
|
19
|
+
{
|
|
20
|
+
use Latte\Strict;
|
|
21
|
+
|
|
22
|
+
public const All = ['*'];
|
|
23
|
+
public const ALL = self::All;
|
|
24
|
+
|
|
25
|
+
/** @var string[] */
|
|
26
|
+
private array $tags = [];
|
|
27
|
+
|
|
28
|
+
/** @var string[] */
|
|
29
|
+
private array $filters = [];
|
|
30
|
+
|
|
31
|
+
/** @var string[] */
|
|
32
|
+
private array $functions = [];
|
|
33
|
+
|
|
34
|
+
/** @var string[][] */
|
|
35
|
+
private array $methods = [];
|
|
36
|
+
|
|
37
|
+
/** @var string[][] */
|
|
38
|
+
private array $properties = [];
|
|
39
|
+
|
|
40
|
+
/** @var array<string, array<string, bool>> */
|
|
41
|
+
private array $methodCache = [];
|
|
42
|
+
|
|
43
|
+
/** @var array<string, array<string, bool>> */
|
|
44
|
+
private array $propertyCache = [];
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
public static function createSafePolicy(): self
|
|
48
|
+
{
|
|
49
|
+
$policy = new self;
|
|
50
|
+
|
|
51
|
+
// does not include: contentType, debugbreak, dump, extends, import, include, layout,
|
|
52
|
+
// php (but 'do' is allowed), sandbox, snippet, snippetArea, templatePrint, varPrint, embed
|
|
53
|
+
$policy->allowTags([
|
|
54
|
+
'_', '=', 'attr', 'block', 'breakIf', 'capture', 'case', 'class', 'continueIf', 'default',
|
|
55
|
+
'define', 'do', 'else', 'elseif', 'elseifset', 'first', 'for', 'foreach', 'if', 'ifchanged',
|
|
56
|
+
'ifcontent', 'iterateWhile', 'ifset', 'l', 'last', 'r', 'rollback', 'sep', 'skipIf', 'spaceless',
|
|
57
|
+
'switch', 'templateType', 'translate', 'try', 'var', 'varType', 'while',
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
// does not include: dataStream, noEscape, noCheck
|
|
61
|
+
$policy->allowFilters([
|
|
62
|
+
'batch', 'breaklines', 'breakLines', 'bytes', 'capitalize', 'ceil', 'clamp', 'date', 'escapeCss', 'escapeHtml',
|
|
63
|
+
'escapeHtmlComment', 'escapeICal', 'escapeJs', 'escapeUrl', 'escapeXml', 'explode', 'first',
|
|
64
|
+
'firstUpper', 'floor', 'checkUrl', 'implode', 'indent', 'join', 'last', 'length', 'lower',
|
|
65
|
+
'number', 'padLeft', 'padRight', 'query', 'random', 'repeat', 'replace', 'replaceRe', 'reverse',
|
|
66
|
+
'round', 'slice', 'sort', 'spaceless', 'split', 'strip', 'striphtml', 'stripHtml', 'striptags', 'stripTags', 'substr',
|
|
67
|
+
'trim', 'truncate', 'upper', 'webalize',
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
$policy->allowFunctions(['clamp', 'divisibleBy', 'even', 'first', 'last', 'odd', 'slice']);
|
|
71
|
+
|
|
72
|
+
$policy->allowMethods(Latte\Essential\CachingIterator::class, self::All);
|
|
73
|
+
$policy->allowProperties(Latte\Essential\CachingIterator::class, self::All);
|
|
74
|
+
|
|
75
|
+
return $policy;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param string[] $tags
|
|
81
|
+
*/
|
|
82
|
+
public function allowTags(array $tags): self
|
|
83
|
+
{
|
|
84
|
+
$this->tags += array_flip(array_map('strtolower', $tags));
|
|
85
|
+
return $this;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param string[] $filters
|
|
91
|
+
*/
|
|
92
|
+
public function allowFilters(array $filters): self
|
|
93
|
+
{
|
|
94
|
+
$this->filters += array_flip(array_map('strtolower', $filters));
|
|
95
|
+
return $this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param string[] $functions
|
|
101
|
+
*/
|
|
102
|
+
public function allowFunctions(array $functions): self
|
|
103
|
+
{
|
|
104
|
+
$this->functions += array_flip(array_map('strtolower', $functions));
|
|
105
|
+
return $this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param string[] $methods
|
|
111
|
+
*/
|
|
112
|
+
public function allowMethods(string $class, array $methods): self
|
|
113
|
+
{
|
|
114
|
+
$this->methodCache = [];
|
|
115
|
+
$this->methods[$class] = array_flip(array_map('strtolower', $methods));
|
|
116
|
+
return $this;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param string[] $properties
|
|
122
|
+
*/
|
|
123
|
+
public function allowProperties(string $class, array $properties): self
|
|
124
|
+
{
|
|
125
|
+
$this->propertyCache = [];
|
|
126
|
+
$this->properties[$class] = array_flip(array_map('strtolower', $properties));
|
|
127
|
+
return $this;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
public function isTagAllowed(string $tag): bool
|
|
132
|
+
{
|
|
133
|
+
return isset($this->tags[strtolower($tag)]) || isset($this->tags['*']);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
public function isFilterAllowed(string $filter): bool
|
|
138
|
+
{
|
|
139
|
+
return isset($this->filters[strtolower($filter)]) || isset($this->filters['*']);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
public function isFunctionAllowed(string $function): bool
|
|
144
|
+
{
|
|
145
|
+
return isset($this->functions[strtolower($function)]) || isset($this->functions['*']);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
public function isMethodAllowed(string $class, string $method): bool
|
|
150
|
+
{
|
|
151
|
+
$method = strtolower($method);
|
|
152
|
+
/** @var bool|null $res */
|
|
153
|
+
$res = &$this->methodCache[$class][$method];
|
|
154
|
+
if (isset($res)) {
|
|
155
|
+
return $res;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
foreach ($this->methods as $c => $methods) {
|
|
159
|
+
if (is_a($class, $c, true) && (isset($methods[$method]) || isset($methods['*']))) {
|
|
160
|
+
return $res = true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return $res = false;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
public function isPropertyAllowed(string $class, string $property): bool
|
|
169
|
+
{
|
|
170
|
+
$property = strtolower($property);
|
|
171
|
+
/** @var bool|null $res */
|
|
172
|
+
$res = &$this->propertyCache[$class][$property];
|
|
173
|
+
if (isset($res)) {
|
|
174
|
+
return $res;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
foreach ($this->properties as $c => $properties) {
|
|
178
|
+
if (is_a($class, $c, true) && (isset($properties[$property]) || isset($properties['*']))) {
|
|
179
|
+
return $res = true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return $res = false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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;
|
|
11
|
+
|
|
12
|
+
use LogicException;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Better OOP experience.
|
|
17
|
+
*/
|
|
18
|
+
trait Strict
|
|
19
|
+
{
|
|
20
|
+
/**
|
|
21
|
+
* Call to undefined method.
|
|
22
|
+
* @param mixed[] $args
|
|
23
|
+
* @throws LogicException
|
|
24
|
+
*/
|
|
25
|
+
public function __call(string $name, array $args): mixed
|
|
26
|
+
{
|
|
27
|
+
$class = method_exists($this, $name) ? 'parent' : static::class;
|
|
28
|
+
$items = (new \ReflectionClass($this))->getMethods(\ReflectionMethod::IS_PUBLIC);
|
|
29
|
+
$items = array_map(fn($item) => $item->getName(), $items);
|
|
30
|
+
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
31
|
+
? ", did you mean $t()?"
|
|
32
|
+
: '.';
|
|
33
|
+
throw new LogicException("Call to undefined method $class::$name()$hint");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Call to undefined static method.
|
|
39
|
+
* @param mixed[] $args
|
|
40
|
+
* @throws LogicException
|
|
41
|
+
*/
|
|
42
|
+
public static function __callStatic(string $name, array $args): mixed
|
|
43
|
+
{
|
|
44
|
+
$rc = new \ReflectionClass(static::class);
|
|
45
|
+
$items = array_filter($rc->getMethods(\ReflectionMethod::IS_STATIC), fn($m) => $m->isPublic());
|
|
46
|
+
$items = array_map(fn($item) => $item->getName(), $items);
|
|
47
|
+
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
48
|
+
? ", did you mean $t()?"
|
|
49
|
+
: '.';
|
|
50
|
+
throw new LogicException("Call to undefined static method $rc->name::$name()$hint");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Access to undeclared property.
|
|
56
|
+
* @throws LogicException
|
|
57
|
+
*/
|
|
58
|
+
public function &__get(string $name): mixed
|
|
59
|
+
{
|
|
60
|
+
$rc = new \ReflectionClass($this);
|
|
61
|
+
$items = array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic());
|
|
62
|
+
$items = array_map(fn($item) => $item->getName(), $items);
|
|
63
|
+
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
64
|
+
? ", did you mean $$t?"
|
|
65
|
+
: '.';
|
|
66
|
+
throw new LogicException("Attempt to read undeclared property $rc->name::$$name$hint");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Access to undeclared property.
|
|
72
|
+
* @throws LogicException
|
|
73
|
+
*/
|
|
74
|
+
public function __set(string $name, mixed $value): void
|
|
75
|
+
{
|
|
76
|
+
$rc = new \ReflectionClass($this);
|
|
77
|
+
$items = array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic());
|
|
78
|
+
$items = array_map(fn($item) => $item->getName(), $items);
|
|
79
|
+
$hint = ($t = Helpers::getSuggestion($items, $name))
|
|
80
|
+
? ", did you mean $$t?"
|
|
81
|
+
: '.';
|
|
82
|
+
throw new LogicException("Attempt to write to undeclared property $rc->name::$$name$hint");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
public function __isset(string $name): bool
|
|
87
|
+
{
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Access to undeclared property.
|
|
94
|
+
* @throws LogicException
|
|
95
|
+
*/
|
|
96
|
+
public function __unset(string $name): void
|
|
97
|
+
{
|
|
98
|
+
$class = static::class;
|
|
99
|
+
throw new LogicException("Attempt to unset undeclared property $class::$$name.");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
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\Attributes;
|
|
11
|
+
|
|
12
|
+
use Attribute;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#[Attribute(Attribute::TARGET_METHOD)]
|
|
16
|
+
class TemplateFunction
|
|
17
|
+
{
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
#[Attribute(Attribute::TARGET_METHOD)]
|
|
22
|
+
class TemplateFilter
|
|
23
|
+
{
|
|
24
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
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;
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
interface Exception
|
|
14
|
+
{
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The exception occurred during Latte compilation.
|
|
20
|
+
*/
|
|
21
|
+
class CompileException extends \Exception implements Exception
|
|
22
|
+
{
|
|
23
|
+
use PositionAwareException;
|
|
24
|
+
|
|
25
|
+
/** @deprecated */
|
|
26
|
+
public ?int $sourceLine;
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
public function __construct(string $message, ?Compiler\Position $position = null, ?\Throwable $previous = null)
|
|
30
|
+
{
|
|
31
|
+
parent::__construct($message, 0, $previous);
|
|
32
|
+
$this->position = $position;
|
|
33
|
+
$this->sourceLine = $position?->line;
|
|
34
|
+
$this->generateMessage();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The exception that indicates error of the last Regexp execution.
|
|
41
|
+
*/
|
|
42
|
+
class RegexpException extends \Exception implements Exception
|
|
43
|
+
{
|
|
44
|
+
public function __construct()
|
|
45
|
+
{
|
|
46
|
+
parent::__construct(preg_last_error_msg(), preg_last_error());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Exception thrown when a not allowed construction is used in a template.
|
|
53
|
+
*/
|
|
54
|
+
class SecurityViolationException extends \Exception implements Exception
|
|
55
|
+
{
|
|
56
|
+
use PositionAwareException;
|
|
57
|
+
|
|
58
|
+
public function __construct(string $message, ?Compiler\Position $position = null)
|
|
59
|
+
{
|
|
60
|
+
parent::__construct($message);
|
|
61
|
+
$this->position = $position;
|
|
62
|
+
$this->generateMessage();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RuntimeException extends \RuntimeException implements Exception
|
|
68
|
+
{
|
|
69
|
+
}
|