leongrdic/smplang

使用 PHP 编写的一种简单语言,可以在不使用 eval 的情况下评估表达式

1.0.2 2022-07-12 13:21 UTC

This package is auto-updated.

Last update: 2024-09-21 00:54:24 UTC


README

release php-version license run-tests

try

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
  • 布尔值 (truefalse)
  • 字符串 ("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.0array['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

try

$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"

try

$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"

try

$smpl = new \Le\SMPLang\SMPLang([
    'foo' => 'bar',
]);

$result = $smpl->evaluate('foo !== "bar" ? "yes" : "no"');
// $result will be "no"

try

// 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);

try