ScriptLite — PHP용 샌드박스형 ECMAScript 하위집합 인터프리터 (선택적 C 확장 포함)

발행: (2026년 3월 7일 오후 12:50 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

What it does

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

실제 일상에서 사람들이 사용하는 기능들을 지원합니다: 화살표 함수, 구조 분해 할당, 템플릿 리터럴, 스프레드/레스트, 배열 메서드(map, filter, reduce, …), 객체 메서드, 정규식, try/catch, Math, JSON, Date 등.

PHP interop

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 is now "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"),
]);

Three execution backends

Bytecode VM

바이트코드로 컴파일되어 순수 PHP 스택 기반 VM에서 실행됩니다. 어디서든 동작하며, 의존성이 없습니다.

PHP transpiler

JavaScript를 PHP 소스 코드로 변환해 OPcache/JIT가 최적화하도록 합니다. VM보다 약 40배 빠릅니다. 핫 경로에 적합합니다.

C extension

컴퓨티드‑goto 디스패치를 사용하는 네이티브 바이트코드 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]);

Possible use cases

  • User‑defined formulas — CMS, 폼 빌더, 스프레드시트와 같은 앱에서 사용자가 price * quantity * (1 - discount)와 같은 식을 작성하도록 허용합니다.
  • Validation rulesvalue.length > 0 && value.length와 같은 규칙을 저장합니다.

모든 백엔드에서 909개의 테스트를 통과했습니다. MIT 라이선스. PHP 8.3+ 지원.

사용자에게 로직을 작성하게 하면서 PHP는 사용하지 못하게 해야 하는 상황을 겪어보셨다면, 여러분의 접근 방식도 공유해 주세요. 어떤 방법을 최종적으로 선택했나요?

0 조회
Back to Blog

관련 글

더 보기 »