Lewati ke konten

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(), argumen call(), nilai kembalian fungsi host).
  • JS → PHP saat nilai JS keluar. Ada dua jalur dengan aturan yang sedikit berbeda: hasil dari eval() dan call(), 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.

PHP
$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:

PHP
$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.

PHP
$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:

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.