leongrdic / smplang
使用 PHP 编写的一种简单语言,可以在不使用 eval 的情况下评估表达式
Requires
- php: >=8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^v3.8
- pestphp/pest: ^1.21
This package is auto-updated.
Last update: 2024-09-21 00:54:24 UTC
README
SMPLang 是一种用 PHP 编写的简单表达式语言。它可以被视为与 PHP 的原生 eval()
函数类似,但 SMPLang 有自己的语法,表达式在类似沙盒的环境中评估,只能访问传递给它 vars 和函数/闭包。
这种语言部分受到 Symfony Expression Language 的启发,但有一些主要区别,例如数组解包、命名参数和更简单的函数定义,因此 SMPLang 可能不适用于某些用例。
安装
composer require leongrdic/smplang
要使用 SMPLang,创建一个 \Le\SMPLang\SMPLang
类的新实例,并传入一个你想要在表达式中使用的关联数组 vars。
$smpl = new \Le\SMPLang\SMPLang([ 'variableName' => 'value', ]);
然后你可以调用实例上的 evaluate()
方法,传入一个表达式字符串。这将返回表达式的结果。
$result = $smpl->evaluate($expression);
可选地,你可以传入一个关联数组作为第二个参数,以提供额外的局部 vars 用于表达式。
$result = $smpl->evaluate($expression, [ 'localVariable' => 123, ]);
以这种方式传入的 vars 将覆盖构造函数中传入的 vars,并且仅在特定表达式中可用,以防从同一对象评估多个表达式。
如果发生异常,将抛出 \Le\SMPLang\Exception
,并带有错误描述。
表达式语法
通过名称访问 vars。如果 vars 既未在构造函数中定义,也未在 evaluate()
中定义,将抛出异常。
支持的字面量
null
- 布尔值 (
true
和false
) - 字符串 (
"string"
或'string'
或`string`
) - 数字 (
1
,1.2
,-1
,-1.2
) - 数组 (
[23, 'string']
或["key": 23, 'key2': 'string']
) - 对象 (
{foo: "bar", baz: 23}
)
数组
数组定义:[element1, element2]
关联数组定义:["key": element, string_variable: element2]
可以使用字符串 vars 代替键来定义具有动态键的关联数组。
支持数组解包:[element1, ...array, ...array2]
可以使用以下任意一种语法访问数组元素:array.key.0
或 array['key'][0]
(这允许动态数组访问)。
对象
对象定义:{foo: "bar", baz: 23}
(支持数组解包)
对象属性访问:object.property
对象方法调用:object.method(parameters)
函数/闭包调用
调用函数或闭包 var:closure_var(param1, param2)
。
命名参数:foo(search: value, count: 1)
。
函数/闭包调用支持数组解包:bar(param1, ...array, ...array2)
算术运算符
+
:加法-
:减法*
:乘法/
:除法%
:取模(a*b%c*d == (a*b)%(c*d)
)**
:指数(a*b**c*d == (a*b)**(c*d)
)
比较运算符
===
:严格相等!==
:严格不等==
:相等!=
:不等>
:大于<
:小于>=
:大于或等于<=
:小于或等于
逻辑运算符
&&
:逻辑与||
:逻辑或!
:逻辑非
字符串连接
~
:字符串连接
三元表达式
a ? b : c
a ?: b
(等同于a ? a : b
)a ? b
(等同于a ? b : null
)
示例
$smpl = new \Le\SMPLang\SMPLang(); $result = $smpl->evaluate('(1 + 2 * 3) / 7'); // $result will be 1
$smpl = new \Le\SMPLang\SMPLang([ 'foo' => 'bar', 'arr' => [1, 2, 3], 'hash' => ['a' => 'b'], ]); $result = $smpl->evaluate('foo ~ " " ~ arr[1] ~ " " ~ hash.a'); // $result will be "bar 2 b"
$smpl = new \Le\SMPLang\SMPLang([ 'prepend' => fn(string $a): string => "hello $a", 'reverse' => strrev(...), ]); $result = $smpl->evaluate('prepend("simple " ~ reverse("world"))'); // $result will be "hello simple dlrow"
$smpl = new \Le\SMPLang\SMPLang([ 'foo' => 'bar', ]); $result = $smpl->evaluate('foo !== "bar" ? "yes" : "no"'); // $result will be "no"
// SMPL can even be used as an (incredibly slow) JSON parser! $smpl = new \Le\SMPLang\SMPLang(); $json = '{ "foo": null, "bar": 15.23, "baz": true, "arr": [5, 6], "str": "cool" }'; $result = $smpl->evaluate($json); print_r($result);