Lewati ke konten

Memulai

Instalasi

Shell
composer require phasis/phasis

Phasis memerlukan PHP 8.2 atau lebih baru dengan tiga ekstensi, semuanya diwajibkan oleh composer.json:

  • mbstring — penanganan string.
  • bcmath — aritmetika BigInt dan penanganan angka presisi bilangan bulat.
  • curl — transportasi default di balik fetch().

mbstring dan curl aktif di sebagian besar build. bcmath sering kali tidak: image resmi php Docker (php:8.x-cli, php:8.x-fpm) tidak menyertakannya, dan beberapa distribusi mengemasnya secara terpisah (php-bcmath). Tanpanya, Composer menolak menginstal Phasis. Dalam Dockerfile yang berbasis image resmi, tambahkan:

Dockerfile
RUN docker-php-ext-install bcmath

Jalankan php -m untuk melihat ekstensi yang tersedia di build Anda.

ext-intl bersifat opsional. Phasis berfungsi tanpanya, tetapi seluruh keluarga Intl.* (Collator, NumberFormat, DateTimeFormat, PluralRules, Locale, DisplayNames, ListFormat, RelativeTimeFormat, Segmenter, DurationFormat) dan kalender Temporal non-ISO (hebrew, islamic, japanese, persian, dll.) memerlukannya. Instal ekstensi ini jika aplikasi host menyediakan JS yang menggunakan pemformatan peka lokal. ext-openssl juga opsional: enkripsi AES-CBC, AES-CTR, dan AES-GCM crypto.subtle memerlukannya, sedangkan digest dan HMAC dapat berfungsi tanpanya.

Tanpa exec, tanpa FFI, tanpa Node.js, tanpa ekstensi native khusus.

Jalankan skrip dari CLI

Shell
./vendor/bin/phasis -e 'console.log(1 + 2 * 3)'
# 7

./vendor/bin/phasis -e 'console.log([1, 2, 3].map(x => x * x).join(","))'
# 1,4,9

./vendor/bin/phasis path/to/script.js

CLI mencetak apa yang ditulis skrip menggunakan console.log; ia tidak mencetak nilai ekspresi terakhir.

Lihat referensi CLI untuk rangkaian perintah lengkap.

Sematkan di PHP

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

// console output is buffered on the engine, not printed
echo $engine->getConsoleOutput();
// 1.0

Instans Engine mempertahankan status globalnya di antara pemanggilan, sehingga pemanggilan eval() berikutnya dapat menggunakan apa pun yang Anda definisikan sebelumnya.

console.log dan saudaranya menambahkan keluaran ke buffer pada mesin alih-alih menulis ke stdout. Baca dengan getConsoleOutput() (satu string, baris digabung dengan \n) atau getConsoleLines() (daftar baris), lalu kosongkan dengan clearConsole().

Mulai dingin tidak mahal: hanya inti yang diperlukan untuk memuat spesifikasi (Object, Function, Array, Promise, Symbol, BigInt, RegExp, tipe error) yang dipasang di awal. Map, TypedArray, Temporal, Intl, URL, fetch, seluruh Paket Web Platform / Fetch, dan semua lainnya tersedia secara transparan saat pertama kali dibaca. Berikan new Engine(eager: true) untuk memasang semua bawaan saat konstruksi — berguna untuk perkakas kesesuaian yang memeriksa deskriptor sebelum membacanya; jarang diperlukan untuk penyematan biasa.

Panggil fungsi JS dari PHP

PHP
$engine->eval('function add(a, b) { return a + b; }');
echo $engine->call('add', 2, 3);
// 5

call() mencari fungsi berdasarkan nama pada objek global dan memanggilnya dengan argumen PHP yang diberikan (secara otomatis dikonversi menjadi nilai JS).

Tampilkan callable PHP sebagai fungsi JS

PHP
$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")');

Closure PHP dibungkus sebagai fungsi JS. Argumennya dikonversi dari JS ke PHP saat dipanggil, dan nilai kembalian dikonversi kembali dari PHP ke JS.

Objek PHP

Objek PHP belum dapat dibagikan dengan JavaScript. setGlobal() dengan objek PHP biasa mengikat undefined (dilacak di masalah #18). Teruskan data sebagai array, yang disalin ke JS, dan tampilkan perilaku sebagai closure:

PHP
class Counter {
    public int $value = 0;
    public function increment(): void { $this->value++; }
}

$counter = new Counter();
$engine->setGlobal('increment', fn () => $counter->increment());

$engine->eval('increment(); increment();');

echo $counter->value;
// 2

Lihat Objek bersama untuk detail dan solusi lainnya.

Batas sumber daya

PHP
$engine = new Engine();
$engine->setLimit('maxLoopIterations', 100_000);

maxLoopIterations (default 100,000) adalah satu-satunya batas yang dapat diatur saat runtime, dan saat ini hanya berlaku untuk kode yang dijalankan interpreter penjelajah pohon. Loop yang dijalankan VM bytecode (badan fungsi dan sebagian besar kode tingkat teratas) tidak dihitung, jadi batas saja tidak menghentikan while (true) {} (masalah #19). Saat batas tercapai, error dapat ditangkap di JS; jika tidak ditangkap, error mencapai PHP sebagai Phasis\Exceptions\InternalError, subkelas dari Phasis\Exceptions\RuntimeError.

Mesin juga memiliki batas tetap tumpukan pemanggilan 4,096 frame (serta pelindung 8,192 frame untuk closure yang ditranspilasi); jika terlampaui, akan melempar “Maximum call stack size exceeded”, sekali lagi sebagai InternalError. Tidak ada batas waktu nyata, jadi jalankan kode yang tidak tepercaya di bawah PHP max_execution_time sendiri atau dalam proses terpisah. Lihat API: setLimit.

Langkah berikutnya

  • Referensi API — seluruh permukaan Phasis\Engine.
  • Interoperabilitas — aturan konversi nilai, fungsi host, objek PHP.
  • Kompatibilitas — apa yang diimplementasikan, cakupan test262.
  • Parser dan Pemformat — mengurai dan memformat JavaScript tanpa menjalankannya.
  • Lanjutan — arsitektur, VM bytecode, tolok ukur, pengujian oracle.