Konversi nilai
Setiap pemanggilan yang melintasi batas PHP↔JS — setGlobal, call, fungsi host yang dipanggil dari JS — melewati konverter:
- PHP → JS saat nilai host masuk ke engine (
setGlobal(), argumencall(), nilai kembalian fungsi host). - JS → PHP saat nilai JS keluar. Ada dua jalur dengan aturan yang sedikit berbeda: hasil dari
eval()dancall(), serta argumen yang diberikan JS ke fungsi host.
Konversi menyalin data. Primitif dipetakan ke primitif, array dan objek disalin ke struktur baru, dan callable PHP dibungkus sebagai fungsi JS. Tidak ada yang digunakan bersama melalui referensi selain callable itu sendiri.
PHP → JS
| Nilai PHP | Hasil JS |
|---|---|
null |
null |
true / false |
true / false |
int |
number (double IEEE 754; int di atas 2^53 kehilangan presisi) |
float |
number (NaN, ±Infinity, ±0 dipertahankan) |
string |
string (byte PHP UTF-8 ditafsirkan sebagai unit kode UTF-16), termasuk jika namanya sama dengan fungsi PHP |
daftar array (kunci 0, 1, 2, … secara berurutan) |
Array baru, elemennya dikonversi secara rekursif |
array lainnya |
Object biasa yang baru, nilainya dikonversi secara rekursif |
Closure, objek yang dapat dipanggil, array callable seperti [$object, 'method'] |
function yang memanggil callable PHP |
objek lainnya, Generator, resource |
undefined |
Array dengan kunci campuran (PHP milik [0 => 'a', 'name' => 'b']) menjadi objek biasa karena array di JS harus memiliki indeks integer yang berurutan tanpa celah.
$engine->setGlobal('list', [1, 2, 3]); // → JS Array
$engine->setGlobal('map', ['a' => 1, 'b' => 2]); // → JS Object
$engine->setGlobal('mixed', [0 => 'x', 'k' => 'v']); // → JS Object
Karena array disalin, perubahan yang dibuat JS tetap berada di JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
Objek PHP yang bukan callable tidak dapat dikonversi; objek tersebut diterima sebagai undefined (masalah #18). Konversikan terlebih dahulu menjadi array, misalnya dengan get_object_vars(), atau ekspos metodenya sebagai closure. Lihat Objek bersama.
JS → PHP: hasil eval() dan call()
| Nilai JS | Hasil PHP |
|---|---|
undefined, null |
null |
boolean |
bool |
number (bilangan bulat, dalam rentang int) |
int (-0 menjadi 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (lainnya) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
daftar array, elemennya dikonversi secara rekursif |
TypedArray |
daftar array berisi angka |
objek lainnya (objek biasa, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
array asosiatif berisi properti datanya sendiri, accessor dan properti bernilai fungsi dilewati |
Baris terakhir berarti objek yang statusnya berada di slot internal dikonversi menjadi array kosong: new Date(0), new Map([["a", 1]]), dan Promise.resolve(5) semuanya mengembalikan []. Error mengembalikan message dan stack miliknya sendiri. Objek yang mereferensikan dirinya sendiri mengonversi referensi internalnya menjadi null.
$engine->eval('[1, 2, 3]'); // [1, 2, 3]
$engine->eval('({a: 1, b: 2})'); // ['a' => 1, 'b' => 2]
$engine->eval('new Uint8Array([1, 2, 3])'); // [1, 2, 3]
$engine->eval('1234567890123456789n'); // null
$engine->eval('new Date(0)'); // []
Lakukan konversi di dalam JS jika Anda memerlukan salah satu nilai tersebut di PHP:
$engine->eval('(1234567890123456789n).toString()'); // "1234567890123456789"
$engine->eval('new Date(0).toISOString()'); // "1970-01-01T00:00:00.000Z"
$engine->eval('Object.fromEntries(new Map([["a", 1]]))'); // ['a' => 1]
JS → PHP: argumen fungsi host
Argumen yang diberikan JS ke fungsi host PHP mengikuti aturan yang sama untuk primitif, array, dan objek biasa, dengan perbedaan berikut:
| Nilai JS | Argumen PHP |
|---|---|
TypedArray, ArrayBuffer, DataView |
objek JsValue bawaan engine, tanpa konversi |
| objek | array asosiatif berisi semua propertinya sendiri, termasuk accessor (getter-nya dijalankan) dan properti bernilai fungsi |
| fungsi panah | sebuah array (biasanya kosong) |
function yang dideklarasikan dengan kata kunci function, atau objek apa pun yang mereferensikan dirinya sendiri |
tanpa konversi: penyalinan tidak pernah berakhir dan PHP kehabisan memori (masalah #21) |
Belum ada cara untuk menerima callback JS dalam fungsi host. Kirim data, bukan fungsi, melalui batas ini.
Angka dan presisi
Angka JavaScript adalah double IEEE 754. Integer PHP menggunakan representasi asli platform (64-bit di semua build modern). Konversi mempertahankan integer hingga ±2^53 secara tepat; integer PHP yang lebih besar menjadi number dan kehilangan presisi setelah 16 digit desimal. Gunakan BigInt di dalam JS jika Anda memerlukan aritmetika presisi untuk integer yang lebih besar, lalu teruskan hasilnya sebagai string.
String
String PHP adalah array byte; string JS adalah rangkaian unit kode UTF-16. Engine menjembatani keduanya dengan menafsirkan byte PHP sebagai UTF-8 dan mengodekannya ulang ke UTF-16 secara internal. Proses ini tidak menghilangkan data jika input UTF-8 valid. Byte yang bukan UTF-8 valid diteruskan tanpa perubahan, bukan diganti dengan U+FFFD.
String.length, akses terindeks (s[0]), dan pemotongan semuanya menghitung unit kode UTF-16 sesuai spesifikasi ECMAScript — bukan titik kode atau byte. Karena itu, string yang berisi karakter astral seperti '🍕' memiliki .length === 2 (dua bagian surrogate dari U+1F355).
Stabilitas bolak-balik
Data biasa tetap sama setelah bolak-balik: nilai PHP yang terdiri dari skalar, daftar, dan array asosiatif, yang masuk melalui setGlobal() lalu kembali melalui eval() atau call(), menghasilkan struktur yang sama, kecuali float integral yang kembali sebagai int (1.0 mengembalikan 1).
Selain itu, nilai dalam tabel di atas yang dipetakan ke null, undefined atau [] tidak akan kembali seperti semula.