Getting Started
Install
composer require phasis/phasisPhasis needs PHP 8.2 or later, plus the mbstring and bcmath extensions — both ship enabled on every mainstream PHP build (Homebrew, apt, RHEL, the official php:cli Docker image, shared-hosting providers). bcmath powers BigInt arithmetic and integer-precision number handling; without it BigInt(...) and large-integer TypedArray operations cannot run.
ext-intl is optional. Phasis works without it, but the entire Intl.* family (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) and the non-ISO Temporal calendars (hebrew, islamic, japanese, persian, etc.) need it. Install it whenever the host application exposes JS that touches locale-aware formatting.
No exec, no FFI, no Node.js, no native extensions beyond the standard PHP build.
Run a script from the CLI
./vendor/bin/phasis -e '1 + 2 * 3'
# 7
./vendor/bin/phasis -e '[1, 2, 3].map(x => x * x).join(",")'
# 1,4,9
./vendor/bin/phasis path/to/script.jsSee the CLI reference for the full command set.
Embed in PHP
<?php
require __DIR__ . '/vendor/autoload.php';
use Phasis\Engine;
$engine = new Engine();
// Evaluate an expression
$result = $engine->eval('1 + 2 * 3');
echo $result;
// 7
// Execute a script file
$engine->execFile('path/to/script.js');
// Bridge a PHP value into the JS global scope
$engine->setGlobal('config', ['debug' => true, 'version' => '1.0']);
$engine->eval('console.log(config.version)');
// 1.0The Engine instance keeps its global state between calls, so subsequent eval() invocations see anything you defined earlier.
Cold-start is cheap: only the spec-load-bearing core (Object, Function, Array, Promise, Symbol, BigInt, RegExp, the error types) is installed up front. Map, TypedArray, Temporal, Intl, URL, fetch, the full Web Platform / Fetch Packs, and everything else materialize transparently on first read. Pass new Engine(eager: true) to install every built-in at construction — useful for conformance tooling that introspects descriptors before reading them; rarely needed for normal embedding.
Call JS functions from PHP
$engine->eval('function add(a, b) { return a + b; }');
echo $engine->call('add', 2, 3);
// 5call() looks up the function by name on the global object and invokes it with the supplied PHP arguments (converted automatically to JS values).
Expose a PHP callable as a JS function
$engine->setGlobal('fetchData', function (string $url): array {
return json_decode(file_get_contents($url), true);
});
$engine->eval('const users = fetchData("https://api.example.com/users")');PHP closures are wrapped as JS functions. Their arguments are converted from JS to PHP on call, and their return values are converted back from PHP to JS.
Share PHP objects with JS
class Counter {
public int $value = 0;
public function increment(): void { $this->value++; }
}
$counter = new Counter();
$engine->setGlobal('counter', $counter);
$engine->eval('counter.increment(); counter.increment();');
echo $counter->value;
// 2The PHP object is referenced — not copied — so mutations from JS land back in the original PHP value.
Resource limits
$engine = new Engine();
$engine->setLimit('maxLoopIterations', 100_000);The loop-iteration limit is the only runtime-tunable cap. Hitting it throws an InternalError which propagates to the PHP caller as a RuntimeError you can catch. The bytecode VM has its own 4 096-frame call-stack ceiling and an 8 192-frame transpiled-closure ceiling baked in — both throw "Maximum call stack size exceeded" when crossed. See API: setLimit for the full surface.
REPL
./vendor/bin/phasis --repl
> 1 + 2
3
> const greet = name => `Hello, ${name}`
> greet('Phasis')
'Hello, Phasis'Next steps
- API reference — full
Phasis\Enginesurface. - Interop — value conversion rules, host functions, shared objects.
- Compatibility — what's implemented, test262 coverage.
- Advanced — architecture, bytecode VM, benchmarks, oracle testing.
Phasis
Pure PHP JavaScript engine. Full ES2024+ at 100% test262, plus Web Platform Pack and Fetch Pack at 100% WPT — fetch, Streams, Blob, FormData, URL, AbortSignal.
Overview
How PHP and JavaScript values cross the boundary in Phasis — value conversion rules, exposing PHP callables as JS functions, and sharing mutable objects without serialization.