Értékkonverzió
Minden hívás, amely átlépi a PHP↔JS határt — setGlobal, call, vagy a JS környezetből meghívott gazdafüggvény — konverteren halad át:
- PHP → JS, amikor gazdaoldali értékek kerülnek a motorba (
setGlobal(),call()argumentumai, gazdafüggvények visszatérési értékei). - JS → PHP, amikor JS értékek kerülnek vissza. Két út van, kissé eltérő szabályokkal: a
eval()éscall()eredményei, valamint az argumentumok, amelyeket a JS ad át egy gazdafüggvénynek.
A konverzió másolatot készít. A primitívek primitívekké alakulnak, a tömbök és objektumok új struktúrákba másolódnak, az PHP hívható elemek pedig JS függvényként kapnak burkolatot. A hívható elemeken kívül semmi sem osztozik referencián keresztül.
PHP → JS
| PHP érték | JS eredmény |
|---|---|
null |
null |
true / false |
true / false |
int |
number (IEEE 754 double; a 2^53 feletti egész számok elveszítik pontosságukat) |
float |
number (a NaN, ±Infinity és ±0 megmarad) |
string |
string (az UTF-8 PHP bájtokat UTF-16 kódegységként értelmezi), akkor is, ha PHP függvényt nevez meg |
list array (a 0, 1, 2, … kulcsok sorrendben) |
új Array, az elemek rekurzív konverziójával |
bármely más array |
új, egyszerű Object, az értékek rekurzív konverziójával |
Closure, meghívható objektum, például a [$object, 'method'] hívható tömb |
function, amely meghívja a PHP hívható elemet |
bármely más objektum, Generator, erőforrás |
undefined |
A vegyes kulcsú tömbök (a PHP [0 => 'a', 'name' => 'b'] típusai) egyszerű objektumokká válnak, mivel az JS tömbjeinek indexei csak folytonos egész számok lehetnek.
$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
Mivel a tömbök másolatként jönnek létre, a JS módosításai a JS környezetében maradnak:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
A nem hívható PHP objektumok nem konvertálhatók; undefined formájában érkeznek meg (18. számú issue). Alakítsa őket előbb tömbbé, például a get_object_vars() segítségével, vagy tegye elérhetővé a metódusaikat closure-ökként. Lásd: Megosztott objektumok.
JS → PHP: eval() és call() eredmények
| JS érték | PHP eredmény |
|---|---|
undefined, null |
null |
boolean |
bool |
number (egész, az int-tartományon belül) |
int (a -0 0-vé válik) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (egyéb) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
list array, az elemek rekurzív konverziójával |
TypedArray |
list array számokból |
bármely más objektum (egyszerű objektumok, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
asszociatív array a saját adat-tulajdonságaiból, a hozzáférőket és függvényértékű tulajdonságokat kihagyva |
Az utolsó sor azt jelenti, hogy a belső slotokban tárolt állapotú objektumok üres tömbbé alakulnak: a new Date(0), a new Map([["a", 1]]) és a Promise.resolve(5) egyaránt []-t ad vissza. Egy Error a saját message és stack értékeit adja vissza. Az önmagára hivatkozó objektum a belső hivatkozásait null-vé alakítja.
$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)'); // []
Ha ezek közül valamelyik értékre van szüksége PHP-ben, végezze el a konverziót a JS környezetében:
$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: gazdafüggvény argumentumai
A JS által egy PHP gazdafüggvénynek átadott argumentumoknál ugyanazok a szabályok érvényesek a primitívekre, tömbökre és egyszerű objektumokra, a következő eltérésekkel:
| JS érték | PHP argumentum |
|---|---|
TypedArray, ArrayBuffer, DataView |
a motor saját JsValue objektuma, konvertálás nélkül |
| object | asszociatív array az összes saját tulajdonságával, a hozzáférőket (amelyek getterei lefutnak) és a függvényértékű tulajdonságokat is beleértve |
| arrow function | egy array (általában üres) |
function a function kulcsszóval deklarálva, vagy bármely önmagára hivatkozó objektum |
nincs konverzió: a másolás nem ér véget, és az PHP kifogy a memóriából (21. számú issue) |
Gazdafüggvényben még nem fogadható JS visszahívás. Ezen a határon függvények helyett adatot adjon át.
Számok és pontosság
A JavaScript számai IEEE 754 double értékek. A PHP intjei a platform natív típusai (minden modern összeállításban 64 bitesek). A konverzió ±2^53-ig pontosan megőrzi az egész számokat; az ennél nagyobb PHP int értékek number-vé alakulnak, és 16 tizedesjegy fölött pontatlanná válnak. Ha ennél nagyobb egész számokkal kell pontosan számolnia, használja a BigInt-t a JS környezetében, az eredményt pedig karakterláncként adja át.
Karakterláncok
A PHP karakterláncai bájttömbök; a JS karakterláncai UTF-16 kódegység-sorozatok. A motor úgy teremti meg a kapcsolatot közöttük, hogy a PHP bájtokat UTF-8-ként értelmezi, majd belsőleg UTF-16-ba kódolja újra. Ez érvényes UTF-8 bemenet esetén veszteségmentes. Az érvénytelen UTF-8 bájtok változatlanul kerülnek továbbításra, nem helyettesíti őket U+FFFD karakterrel.
A String.length, az indexelt hozzáférés (s[0]) és a szeletelés egyaránt UTF-16 kódegységeket számol az ECMAScript-specifikáció szerint — nem kódpontokat és nem bájtokat. Ezért az olyan, kiterjesztett síkbeli karaktert tartalmazó karakterláncok, mint a '🍕', .length === 2-t tartalmaznak (az U+1F355 két helyettesítő felét).
Körfordulós stabilitás
Az egyszerű adatok oda-vissza konvertálás után is megmaradnak: egy skalárokból, listákból és asszociatív tömbökből álló PHP érték, amely a setGlobal() felől kerül be, majd a eval() vagy call() felől tér vissza, ugyanazt a struktúrát adja, kivéve, hogy az egész értékű lebegőpontos számok int-ként térnek vissza (a 1.0 1-t ad vissza).
A fenti táblázatokban szereplő összes többi, null, undefined vagy [] értékre leképezett típus nem alakítható oda-vissza veszteség nélkül.