Preskočiť na obsah

Konverzia hodnôt

Každé volanie cez hranicu PHP↔JS — setGlobal, call alebo hostiteľská funkcia zavolaná z JS — prechádza konvertorom:

  • PHP → JS, keď hodnoty hostiteľa vstupujú do enginu (setGlobal(), argumenty call(), návratové hodnoty hostiteľských funkcií).
  • JS → PHP, keď hodnoty JS smerujú von. Existujú dve cesty s mierne odlišnými pravidlami: výsledky z eval() a call() a argumenty, ktoré JS odovzdáva hostiteľskej funkcii.

Konverzia vytvára kópie. Primitívne hodnoty sa mapujú na primitívne hodnoty, polia a objekty sa kopírujú do nových štruktúr a volateľné hodnoty PHP sa obaľujú ako funkcie JS. Nič sa nezdieľa prostredníctvom referencie, okrem samotných volateľných hodnôt.

PHP → JS

Hodnota PHP Výsledok JS
null null
true / false true / false
int number (dvojitá presnosť IEEE 754; celé čísla nad 2^53 strácajú presnosť)
float number (NaN, ±Infinity, ±0 sa zachovávajú)
string string (bajty UTF-8 PHP sa interpretujú ako 16-bitové jednotky kódu UTF-16), aj keď označuje funkciu PHP
zoznam array (kľúče 0, 1, 2, … v poradí) nové Array, prvky sa konvertujú rekurzívne
ľubovoľné iné array nový obyčajný Object, hodnoty sa konvertujú rekurzívne
Closure, volateľný objekt, volateľné pole, napríklad [$object, 'method'] function, ktorá volá volateľnú hodnotu PHP
ľubovoľný iný objekt, Generator, zdroj undefined

Polia so zmiešanými kľúčmi (PHP, [0 => 'a', 'name' => 'b']) sa menia na obyčajné objekty, pretože polia v JS musia mať súvislý rozsah celočíselných indexov.

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

Keďže sa polia kopírujú, zmeny v JS zostanú v JS:

PHP
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list);                 // 3
$engine->eval('list.length'); // 4

Objekty PHP, ktoré nemožno volať, sa nekonvertujú; dorazia ako undefined (problém č. 18). Najskôr ich preveďte na polia, napríklad pomocou get_object_vars(), alebo ich metódy sprístupnite ako uzávery. Pozrite si Zdieľané objekty.

JS → PHP: výsledky eval() a call()

Hodnota JS Výsledok PHP
undefined, null null
boolean bool
number (celé číslo v rozsahu int) int (-0 sa zmení na 0)
NaN, Infinity, -Infinity NAN, INF, -INF
number (iné) float
string string (UTF-8)
bigint, symbol null
function null
Array zoznam array, prvky sa konvertujú rekurzívne
TypedArray zoznam array čísel
ľubovoľný iný objekt (obyčajné objekty, Error, Date, Map, Set, Promise, ArrayBuffer, …) asociatívne array jeho vlastných dátových vlastností; prístupové vlastnosti a vlastnosti s funkčnými hodnotami sa vynechajú

Posledný riadok znamená, že objekty so stavom uloženým v interných slotoch sa konvertujú na prázdne pole: new Date(0), new Map([["a", 1]]) a Promise.resolve(5) vracajú []. Objekt Error vracia vlastné message a stack. Objekt, ktorý odkazuje sám na seba, konvertuje svoje vnútorné odkazy na 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)');              // []

Keď potrebujete niektorú z týchto hodnôt v PHP, konvertujte ju priamo v JS:

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: argumenty hostiteľských funkcií

Argumenty, ktoré JS odovzdáva hostiteľskej funkcii PHP, sa pri primitívnych hodnotách, poliach a obyčajných objektoch riadia rovnakými pravidlami, s týmito rozdielmi:

Hodnota JS Argument PHP
TypedArray, ArrayBuffer, DataView vlastný objekt enginu JsValue, bez konverzie
objekt asociatívne array všetkých jeho vlastných vlastností vrátane prístupových vlastností (ktorých gettery sa spustia) a vlastností s funkčnými hodnotami
funkcia so šípkou array (zvyčajne prázdny)
function deklarovaná pomocou kľúčového slova function alebo ľubovoľný objekt odkazujúci sám na seba bez konverzie: kopírovanie sa nikdy neskončí a PHP minie pamäť (problém č. 21)

Hostiteľská funkcia zatiaľ nedokáže prijať spätné volanie JS. Cez túto hranicu odovzdávajte dáta, nie funkcie.

Čísla a presnosť

Čísla JavaScript sú dvojité čísla IEEE 754. Celočíselné hodnoty PHP zodpovedajú platforme (64-bitové v každom modernom zostavení). Konverzia zachová celé čísla až do ±2^53 presne; väčšie celé čísla PHP sa zmenia na number a pri viac ako 16 desatinných číslicach strácajú presnosť. Ak potrebujete presne počítať s väčšími celými číslami, použite v JS BigInt a výsledok odovzdajte ako reťazec.

Reťazce

Reťazce PHP sú polia bajtov; reťazce JS sú postupnosti 16-bitových jednotiek kódu UTF-16. Engine ich prepája tak, že bajty PHP interpretuje ako UTF-8 a následne ich interne prekóduje do UTF-16. Pri platnom vstupe UTF-8 sa nestratia žiadne údaje. Bajty, ktoré netvoria platné UTF-8, sa prenášajú bez zmeny namiesto toho, aby sa nahradili znakom U+FFFD.

String.length, prístup podľa indexu (s[0]) aj výrezy počítajú jednotky kódu UTF-16 podľa špecifikácie ECMAScriptu — nie kódové body ani bajty. Reťazce obsahujúce znaky mimo základnej viacjazyčnej roviny, ako napríklad '🍕', preto majú .length === 2 (dve náhradné polovice znaku U+1F355).

Stabilita pri spätnom prevode

Jednoduché dáta sa pri spätnom prevode zachovajú: hodnota PHP pozostávajúca zo skalárov, zoznamov a asociatívnych polí, ktorá vstúpi cez setGlobal() a vráti sa cez eval() alebo call(), vytvorí rovnakú štruktúru, s výnimkou celočíselných čísel s desatinnou časťou, ktoré sa vrátia ako int (1.0 vracia 1).

Všetko ostatné z vyššie uvedených tabuliek, čo sa mapuje na null, undefined alebo [], sa pri spätnom prevode nezachová.