ScriptLite — 一个为 PHP 的沙箱化 ECMAScript 子集解释器(可选 C 扩展)

发布: (2026年3月7日 GMT+8 11:50)
4 分钟阅读
原文: Dev.to

Source: Dev.to

它的功能

它可以在 PHP 中运行 JavaScript(ES5/ES6 子集)。没有文件系统访问、没有网络、没有 eval、没有 require —— 脚本只能操作你显式传入的数据。可以把它看作一个沙箱,用户编写逻辑,而你完全控制他们能看到和能做的事情。

$engine = new ScriptLite\Engine();

// 存在于数据库中的用户自定义定价规则
$rule = '
    let total = items.reduce((sum, item) => sum + item.price * item.qty, 0);
    if (total > 100) total *= (1 - discount);
    Math.round(total * 100) / 100;
';

$result = $engine->eval($rule, [
    "items" => [
        ["price" => 29.99, "qty" => 2],
        ["price" => 49.99, "qty" => 1],
    ],
    "discount" => 0.1,
]);
// $result === 98.97

它支持人们日常实际使用的特性:箭头函数、解构、模板字面量、展开/剩余参数、数组方法(mapfilterreduce …)、对象方法、正则、try/catchMathJSONDate 等等。

PHP 互操作

你可以直接传入 PHP 对象。脚本可以读取属性、调用方法,且修改会回流到 PHP 端:

$order = new Order(id: 42, status: 'pending');

$engine->eval('
    if (order.total() > 500) {
        order.applyDiscount(10);
        order.setStatus("vip");
    }
', ["order" => $order]);

// $order->status 现在是 "vip"

你也可以把 PHP 闭包作为可调用函数传入,这样就能精确控制脚本拥有的能力:

$engine->eval('
    let users = fetchUsers();
    let active = users.filter(u => u.lastLogin > cutoff);
    active.map(u => u.email);
', [
    "fetchUsers" => fn() => $userRepository->findAll(),
    "cutoff" => strtotime("-30 days"),
]);

三种执行后端

字节码 VM

编译为字节码,在纯 PHP 的基于栈的 VM 上运行。 兼容性好,无需依赖。

PHP 转译器

将 JavaScript 转换为 PHP 源码,交由 OPcache/JIT 优化。 比 VM 快约 40 倍,适合热点路径。

C 扩展

使用计算跳转的本地字节码 VM。 比 PHP VM 快约 180 倍,出于对极致速度的好奇而实现。

无论使用哪种后端,API 都保持一致。引擎会自动选择最快的可用实现:

$engine = new Engine();        // 如果加载了 C 扩展则使用,否则使用 PHP VM
$engine = new Engine(false);   // 强制使用纯 PHP

// 相同代码、相同结果、不同速度
$result = $engine->eval('items.filter(x => x > 3)', ["items" => [1, 2, 3, 4, 5]]);

如果想在没有 C 扩展的情况下获得接近原生的速度,转译路径非常有用:

// 转译一次,使用不同数据多次运行
$callback = $engine->getTranspiledCallback($script, ["data", "config"]);
$result1 = $callback(["data" => $batch1, "config" => $cfg]);
$result2 = $callback(["data" => $batch2, "config" => $cfg]);

可能的使用场景

  • 用户自定义公式 —— 让用户在 CMS、表单构建器或类似电子表格的应用中编写 price * quantity * (1 - discount)
  • 验证规则 —— 存储诸如 value.length > 0 && value.length 的规则。

共计 909 条测试,覆盖所有后端。MIT 许可证。支持 PHP 8.3+。

欢迎分享你的想法,尤其是如果你也遇到“需要用户编写逻辑但不能让他们写 PHP”这种情况时的解决方案。你最终是怎么做的?

0 浏览
Back to Blog

相关文章

阅读更多 »

高级新拟态滑动抽屉分享按钮

封面图片:Premium Neumorphic Sliding Drawer Share Button https://media2.dev.to/dynamic/image/width=1000,height=420,fit=cover,gravity=auto,format=auto/http...