Värdekonvertering
Varje anrop som passerar gränsen PHP↔JS — setGlobal, call, en värdfunktion som anropas från JS — går genom en konverterare:
- PHP → JS när värden från värden flödar in i motorn (
setGlobal(), argument tillcall(), returvärden från värdfunktioner). - JS → PHP när värden i JS flödar tillbaka. Det finns två vägar med något olika regler: resultat från
eval()ochcall(), samt argumenten som JS skickar till en värdfunktion.
Konverteringen kopierar. Primitiva värden mappas till primitiva värden, arrayer och objekt kopieras till nya strukturer, och anropsbara värden i PHP omsluts som funktioner i JS. Inget delas genom referenser förutom själva de anropsbara värdena.
PHP → JS
| Värde i PHP | Resultat i JS |
|---|---|
null |
null |
true / false |
true / false |
int |
number (IEEE 754 double; heltal större än 2^53 förlorar precision) |
float |
number (NaN, ±Infinity, ±0 bevaras) |
string |
string (UTF-8 PHP-byte tolkade som UTF-16-kodenheter), även när det namnger en funktion i PHP |
listan array (nycklarna 0, 1, 2, … i ordning) |
ny Array, element konverteras rekursivt |
alla andra array |
nytt vanligt Object, värden konverteras rekursivt |
Closure, anropsbart objekt, anropsbar array som [$object, 'method'] |
function som anropar den anropsbara funktionen i PHP |
alla andra objekt, Generator, resurs |
undefined |
Arrayer med blandade nycklar (PHPs [0 => 'a', 'name' => 'b']) blir vanliga objekt eftersom arrayer i JS måste ha ett sammanhängande heltalsindex.
$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
Eftersom arrayer kopieras stannar ändringar i JS i JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
Objekt i PHP som inte är anropsbara konverteras inte; de kommer fram som undefined (problem nr 18). Konvertera dem först till arrayer, till exempel med get_object_vars(), eller exponera deras metoder som closures. Se Delade objekt.
JS → PHP: resultat från eval() och call()
| Värde i JS | Resultat i PHP |
|---|---|
undefined, null |
null |
boolean |
bool |
number (heltal inom int-intervallet) |
int (-0 blir 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (övriga) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
listan array, element konverteras rekursivt |
TypedArray |
lista array med tal |
alla andra objekt (vanliga objekt, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
associativ array med objektets egna dataegenskaper; accessorer och egenskaper med funktionsvärden hoppas över |
Den sista raden betyder att objekt vars tillstånd finns i interna slots konverteras till en tom array: new Date(0), new Map([["a", 1]]) och Promise.resolve(5) returnerar alla []. Ett Error returnerar sitt eget message och stack. Ett självrefererande objekt konverterar sina inre referenser till 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)'); // []
Konvertera inuti JS när du behöver något av dessa värden i 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: argument till värdfunktioner
Argument som JS skickar till en värdfunktion i PHP följer samma regler för primitiva värden, arrayer och vanliga objekt, med följande skillnader:
| JS värde | PHP argument |
|---|---|
TypedArray, ArrayBuffer, DataView |
motorns eget JsValue-objekt, okonverterat |
| objekt | associativ array över alla dess egna egenskaper, inklusive accessorer (vars getters körs) och egenskaper med funktionsvärden |
| pilfunktion | en array (vanligtvis tom) |
function deklarerad med nyckelordet function, eller ett objekt som refererar till sig självt |
ingen konvertering: kopieringen tar aldrig slut och PHP får slut på minne (problem nr 21) |
Det går ännu inte att ta emot en callback i JS i en värdfunktion. Skicka data, inte funktioner, över den här gränsen.
Tal och precision
Tal i JavaScript är IEEE 754-double. Heltal i PHP är plattformsbaserade (64 bitar i alla moderna byggen). Konverteringen bevarar heltal upp till ±2^53 exakt; större heltal i PHP blir number och förlorar precision efter 16 decimalsiffror. Använd BigInt inuti JS om du behöver exakt aritmetik med heltal större än så, och skicka ut resultatet som en sträng.
Strängar
Strängar i PHP är byte-arrayer; strängar i JS är sekvenser av UTF-16-kodenheter. Motorn överbryggar skillnaden genom att tolka PHP byte som UTF-8 och koda om dem till UTF-16 internt. Detta är förlustfritt för giltig UTF-8-indata. Byte som inte är giltig UTF-8 skickas vidare oförändrade i stället för att ersättas med U+FFFD.
String.length, indexerad åtkomst (s[0]) och delsträngar räknar alla UTF-16-kodenheter enligt ECMAScript-specifikationen — inte kodpunkter eller byte. Strängar som innehåller astrala tecken som '🍕' har därför .length === 2 (de två surrogathalvorna i U+1F355).
Stabilitet vid rundresa
Vanliga data överlever en rundresa: ett värde i PHP som består av skalärer, listor och associativa arrayer och som går in via setGlobal() och kommer tillbaka via eval() eller call() ger samma struktur, förutom att flyttal med heltalsvärde kommer tillbaka som int (1.0 returnerar 1).
Allt annat i tabellerna ovan som mappas till null, undefined eller [] klarar inte en rundresa.