PHPackages                             aheinze/scriptlite - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. aheinze/scriptlite

ActiveLibrary

aheinze/scriptlite
==================

An ECMAScript (ES5/ES6 subset) bytecode-compiled interpreter

0.1.0(2mo ago)1812MITPHPPHP &gt;=8.3

Since Mar 6Pushed 2mo agoCompare

[ Source](https://github.com/aheinze/ScriptLite)[ Packagist](https://packagist.org/packages/aheinze/scriptlite)[ RSS](/packages/aheinze-scriptlite/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (1)Versions (2)Used By (0)

[![Image](https://private-user-images.githubusercontent.com/321047/559694979-20005eaf-cea3-45c7-99f5-f068175b2b41.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzU0MTk4NzAsIm5iZiI6MTc3NTQxOTU3MCwicGF0aCI6Ii8zMjEwNDcvNTU5Njk0OTc5LTIwMDA1ZWFmLWNlYTMtNDVjNy05OWY1LWYwNjgxNzViMmI0MS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwNDA1JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDQwNVQyMDA2MTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kYmRhNDc0NzBlNjBjZGVhNjQwOTIxMjhiM2FjMTQ0ZGYzNWFlNGIyZmY1MGFkNzgzNWI4MzA4ZDJlYjE0YzU5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.E4djXUv2_ei4Rd6kf55GpSe7HQotCg7Y-u1yWnF4KAo)](https://private-user-images.githubusercontent.com/321047/559694979-20005eaf-cea3-45c7-99f5-f068175b2b41.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzU0MTk4NzAsIm5iZiI6MTc3NTQxOTU3MCwicGF0aCI6Ii8zMjEwNDcvNTU5Njk0OTc5LTIwMDA1ZWFmLWNlYTMtNDVjNy05OWY1LWYwNjgxNzViMmI0MS5qcGc_WC1BbXotQWxnb3JpdGhtPUFXUzQtSE1BQy1TSEEyNTYmWC1BbXotQ3JlZGVudGlhbD1BS0lBVkNPRFlMU0E1M1BRSzRaQSUyRjIwMjYwNDA1JTJGdXMtZWFzdC0xJTJGczMlMkZhd3M0X3JlcXVlc3QmWC1BbXotRGF0ZT0yMDI2MDQwNVQyMDA2MTBaJlgtQW16LUV4cGlyZXM9MzAwJlgtQW16LVNpZ25hdHVyZT1kYmRhNDc0NzBlNjBjZGVhNjQwOTIxMjhiM2FjMTQ0ZGYzNWFlNGIyZmY1MGFkNzgzNWI4MzA4ZDJlYjE0YzU5JlgtQW16LVNpZ25lZEhlYWRlcnM9aG9zdCJ9.E4djXUv2_ei4Rd6kf55GpSe7HQotCg7Y-u1yWnF4KAo)

ScriptLite
==========

[](#scriptlite)

A sandboxed interpreter for a subset of ECMAScript, written in PHP. Embed user-provided scripts in your PHP application for data processing, configuration logic, computed fields, template expressions, workflow rules, and more — without giving scripts access to the filesystem, network, database, or any PHP internals. It covers the most useful parts of the language (variables, functions, closures, arrays, objects, regex, error handling, destructuring, etc.) while intentionally omitting modules, classes, async/await, generators, and other heavy runtime features.

Scripts run in a sealed environment: they can only use the ECMAScript built-ins listed below and any globals you explicitly pass in. There is no `require`, no `eval`, no `process`, no `globalThis` — just pure computation on the data you provide.

### Use cases

[](#use-cases)

- **User-defined formulas** — let users write `price * quantity * (1 - discount)` in a CMS or spreadsheet-like app
- **Configuration logic** — express feature flags, A/B rules, or pricing tiers as scripts instead of hardcoded PHP
- **Data transformation** — map, filter, and reshape API payloads or database rows with user-supplied logic
- **Computed fields** — derive values in a form builder or report engine using expressions like `items.reduce((s, i) => s + i.total, 0)`
- **Workflow / automation rules** — evaluate conditions and actions defined by end users at runtime
- **Template expressions** — safely evaluate interpolated expressions in user-generated content

### Execution backends

[](#execution-backends)

- **C extension** — native bytecode VM with computed-goto dispatch (~180x faster than the PHP VM, ~4.5x faster than the transpiler)
- **Bytecode VM** — a stack-based virtual machine with 62 opcodes and register file optimization
- **PHP transpiler** — compiles ECMAScript to PHP source that OPcache/JIT can optimize natively (~40x faster than the PHP VM)

Installation
------------

[](#installation)

```
composer require aheinze/scriptlite
```

Requires **PHP 8.3+**. No external dependencies for the pure-PHP backends.

For the optional C extension (~180x faster VM), see [C extension](#c-extension) below.

Quick start
-----------

[](#quick-start)

```
use ScriptLite\Engine;

$engine = new Engine();

// Evaluate and get the result
$result = $engine->eval('
    function fib(n) {
        if (n  x * 2)', [
    'items' => [1, 2, 3],
]);
// $result === [2, 4, 6]

// PHP associative arrays become objects
$result = $engine->eval('config.host + ":" + config.port', [
    'config' => ['host' => 'localhost', 'port' => 3000],
]);
// $result === "localhost:3000"

// PHP closures become callable functions
$result = $engine->eval('transform("hello")', [
    'transform' => fn(string $s) => strtoupper($s),
]);
// $result === "HELLO"
```

### PHP object instances

[](#php-object-instances)

PHP objects are automatically wrapped so scripts can read/write properties and call methods. Method arguments are auto-coerced to match PHP type hints (ECMAScript numbers are floats, but PHP methods may expect `int`, `string`, etc.).

```
class Account {
    public function __construct(
        public string $owner,
        public float $balance,
    ) {}

    public function deposit(float $amount): float {
        $this->balance += $amount;
        return $this->balance;
    }

    public function withdraw(float $amount): float {
        $this->balance -= $amount;
        return $this->balance;
    }
}

$acc = new Account('Alice', 1000);

$engine->eval('
    acc.deposit(250);
    acc.withdraw(75);
', ['acc' => $acc]);

// The original PHP object is mutated:
// $acc->balance === 1175.0
```

Objects returned from methods are also wrapped, so chained access works. PHP closures and arrays returned from methods are converted to their ECMAScript equivalents.

### Return value conversion

[](#return-value-conversion)

ECMAScript typePHP typenumber (int)`int`number (float)`float`string`string`boolean`bool`null`null`undefined`null`array`array` (indexed)object`array` (associative)### Transpiler path

[](#transpiler-path)

The same globals work with the transpiler. The transpile step only needs variable **names** (so the scope tracker captures them correctly); actual values are provided at execution time:

```
// One-shot: transpile and execute in a single call
$result = $engine->transpileAndEval($script, ['acc' => $acc, 'multiplier' => 2]);

// Transpile once, run many times with different values
$callback = $engine->getTranspiledCallback($script, ['acc', 'multiplier']);
$result = $callback(['acc' => $acc1, 'multiplier' => 2]);
$result = $callback(['acc' => $acc2, 'multiplier' => 3]);

// Or step by step for full control:
$php = $engine->transpile($script, ['acc', 'multiplier']);
$result = $engine->runTranspiled($php, ['acc' => $acc]);    // temp file (worker-safe)
$result = $engine->evalTranspiled($php, ['acc' => $acc]);   // eval (leaks in long-running workers)

// Or save to a file for OPcache:
$engine->saveTranspiled($php, '/tmp/script.php');
$__globals = ['acc' => $acc, 'multiplier' => 2];
$result = include '/tmp/script.php';
```

Caching
-------

[](#caching)

The `Engine` instance maintains LRU caches at every stage of the pipeline. Reuse the same instance for best performance:

```
$engine = new Engine();

// Repeated eval() calls with the same source skip recompilation
$engine->eval($script, ['x' => 1]);
$engine->eval($script, ['x' => 2]); // bytecode served from cache

// transpile() and runTranspiled() also cache automatically
$php = $engine->transpile($script, ['x']); // cached after first call
$engine->runTranspiled($php, ['x' => 1]); // temp file reused + OPcache'd
$engine->runTranspiled($php, ['x' => 2]); // same cached file
```

Cache layerScopeMax entriesParse (AST)Shared by `compile()` and `transpile()`12Compiled bytecode`eval()`32Transpiled PHP source`transpile()`32Transpiled temp files`runTranspiled()`16Temp files are written to `sys_get_temp_dir()` and precompiled with `opcache_compile_file()` when available. The file cache evicts the least-recently-used entry and cleans up the corresponding file on disk.

Security model
--------------

[](#security-model)

Scripts execute in a fully sandboxed environment:

- **No filesystem access** — no `require`, `import`, `fs`, or file I/O of any kind
- **No network access** — no `fetch`, `XMLHttpRequest`, or sockets
- **No PHP internals** — no `eval`, `exec`, `system`, or access to PHP's global scope
- **No ambient globals** — no `process`, `globalThis`, `window`, or `document`
- **Explicit data boundary** — scripts can only see globals you pass in via the `$globals` parameter
- **Pure computation** — the only side effects are mutations to objects/arrays you explicitly provide

The attack surface is limited to CPU and memory consumption. For untrusted input, combine with PHP's `set_time_limit()` / `memory_limit` to cap resource usage.

C extension
-----------

[](#c-extension)

The optional `scriptlite` C extension replaces the PHP bytecode compiler and VM with a native implementation using computed-goto dispatch, tagged unions, and zero-copy string interning. The extension embeds the parser runtime, so it works standalone without the Composer autoloader — only the `.so` file is needed.

When the extension is loaded, `Engine` delegates to `ScriptLiteExt\Engine` transparently — no code changes required:

```
$engine = new Engine();      // uses ScriptLiteExt\Engine when available
$engine = new Engine(true);  // same as default
$engine = new Engine(false); // force PHP VM/transpiler, ignore extension
```

The extension registers its classes under the `ScriptLiteExt\` namespace (`Engine`, `Compiler`, `VirtualMachine`, `CompiledScript`) to avoid conflicts with the userland `ScriptLite\` namespace. Legacy `ScriptLiteNative\` aliases are provided for backward compatibility.

### Install via PHP PIE

[](#install-via-php-pie)

The extension is available as a standalone package at [aheinze/ScriptLiteExt](https://github.com/aheinze/ScriptLiteExt):

```
pie install aheinze/scriptlite-ext
```

### Building from source

[](#building-from-source)

Requires PHP 8.3+ development headers (`php-dev` / `php-devel`), `libpcre2-dev`, and a C11 compiler.

```
# Using composer script
composer build:ext

# Or manually
cd ext/scriptlite
phpize
./configure --enable-scriptlite
make -j$(nproc)
make test            # run .phpt tests
```

This produces `modules/scriptlite.so`. To load it:

```
# One-off
php -dextension=$(pwd)/ext/scriptlite/modules/scriptlite.so your_script.php

# Persistent — add to your php.ini or a conf.d file
echo "extension=/path/to/scriptlite.so" | sudo tee /etc/php.d/50-scriptlite.ini
```

> The `.so` is tied to the PHP minor version it was built against (e.g. 8.3 vs 8.4). Rebuild when switching PHP versions.

### Docker

[](#docker)

Add the build to your Dockerfile:

```
FROM php:8.4-cli

RUN apt-get update && apt-get install -y libpcre2-dev

COPY ext/scriptlite /tmp/scriptlite
RUN cd /tmp/scriptlite \
    && phpize \
    && ./configure \
    && make -j$(nproc) \
    && make install \
    && docker-php-ext-enable scriptlite \
    && rm -rf /tmp/scriptlite
```

### Verifying the extension is loaded

[](#verifying-the-extension-is-loaded)

```
php -m | grep scriptlite
# or
php -r "var_dump(class_exists('ScriptLiteExt\Engine', false));"
```

Architecture
------------

[](#architecture)

```
ECMAScript source
  │
  ├── Lexer ──▶ Token stream
  │
  ├── Parser (Pratt) ──▶ AST
  │
  ├─┬── C Compiler ──▶ Bytecode ──▶ C VM (computed goto)      ← extension (standalone)
  │ │
  │ ├── PHP Compiler ──▶ Bytecode ──▶ PHP VM (stack machine)   ← pure PHP fallback
  │ │
  │ └── PhpTranspiler ──▶ PHP source ──▶ eval (OPcache/JIT)   ← fastest pure PHP

```

The C extension embeds the parser runtime, so when loaded it handles the full pipeline (lex → parse → compile → execute) without the Composer autoloader. The userland `Engine` class delegates to `ScriptLiteExt\Engine` when available.

DirectoryPurpose`src/Lexer/`Zero-copy tokenizer, regex literal support`src/Ast/`AST node types + Pratt parser`src/Compiler/`Single-pass AST → bytecode compiler (PHP)`src/Vm/`Stack-based bytecode VM (PHP)`src/Runtime/`Runtime objects (JsArray, JsObject, JsClosure, JsRegex, JsDate, Environment)`src/Transpiler/`AST → PHP source code transpiler with type inference`ext/scriptlite/`Native C compiler + VM (optional extension)### VM opcodes

[](#vm-opcodes)

The VM uses 62 int-backed enum opcodes organized by category: stack ops, arithmetic, comparison, bitwise, variables (including register-optimized `GetReg`/`SetReg`), control flow, functions, exception handling, scope, and property access. The `match()` on int-backed enums compiles to a jump table under OPcache/JIT.

Non-captured local variables (`var` declarations and parameters) are allocated to an integer-indexed register file at compile time, bypassing the Environment hash table for ~13x faster variable access on hot paths. Variables captured by inner closures remain in the Environment scope chain to preserve correct closure semantics.

### Transpiler

[](#transpiler)

The transpiler maps ECMAScript constructs directly to PHP equivalents:

- Objects → PHP associative arrays
- Arrays → PHP indexed arrays
- Functions → PHP closures with `use (&$captured)` for scope capture
- Constructors → closures that build and return arrays
- Methods → inlined PHP built-in calls (`array_map`, `explode`, `preg_replace`, etc.)

Tests
-----

[](#tests)

```
composer test           # all phases (PHP-only + extension + .phpt)
composer test:php       # PHPUnit without extension
composer test:ext       # PHPUnit with C extension loaded
```

`composer test` runs `run-tests.php` which executes three phases:

1. PHPUnit in pure PHP-library mode (no extension)
2. PHPUnit with the C extension loaded
3. Extension `.phpt` tests in `ext/scriptlite/tests`

907 tests, ~2230 assertions across all three backends. Extension-gated tests are skipped when the `.so` is not loaded.

Benchmark
---------

[](#benchmark)

```
composer bench          # without extension
composer bench:ext      # with C extension
```

Runs 10 workloads (fibonacci, sieve, quicksort, string ops, closures, objects/vectors, recursive tree, matrix multiplication, functional pipeline, regex).

### C extension vs PHP VM vs Transpiler

[](#c-extension-vs-php-vm-vs-transpiler)

BenchmarkPHP VMTranspilerC ExtensionC/VMC/Trfibonacci(25)2190 ms54 ms10.8 ms**202x**5.0xsieve(5000)145 ms1.5 ms0.91 ms**159x**1.6xmatrix(3x3x50)32.8 ms0.27 ms0.20 ms**163x**1.3xclosures(5k)72.1 ms1.9 ms0.41 ms**175x**4.6xpipeline(500)14.2 ms0.79 ms0.12 ms**118x**6.6xtree(depth=10)71.8 ms2.97 ms0.85 ms**84x**3.5xquicksort(200)48.5 ms1.8 ms0.42 ms**117x**4.4xobjects+vectors25.0 ms2.0 ms0.30 ms**83x**6.8xstring ops3.6 ms0.15 ms0.09 ms**40x**1.7xregex(200iter)5.9 ms0.77 ms0.50 ms**12x**1.5x**Total****2608 ms****66 ms****14.6 ms****178x****4.5x**### Execution modes (combined workload)

[](#execution-modes-combined-workload)

ModeTimevs Native PHPPHP VM (compile + execute)~77 ms~100xPHP VM (pre-compiled)~76 ms~99xPHP VM (unserialize + execute)~76 ms~99xTranspiler (eval)~2.6 ms~3.4xTranspiler (cached file)~2.6 ms~3.4xNative PHP (same algorithms)~0.76 ms1xMemory per run: VM 596 KB, Transpiler 271 KB, Native PHP 171 KB

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance87

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

73d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/c54f23273e8779be75f3b133c8f5d6823818b43134aceb96f51dca8d98a11e12?d=identicon)[aheinze](/maintainers/aheinze)

---

Top Contributors

[![aheinze](https://avatars.githubusercontent.com/u/321047?v=4)](https://github.com/aheinze "aheinze (29 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/aheinze-scriptlite/health.svg)

```
[![Health](https://phpackages.com/badges/aheinze-scriptlite/health.svg)](https://phpackages.com/packages/aheinze-scriptlite)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
