Pretvorba vrednosti
Vsak klic, ki prečka mejo PHP↔JS — setGlobal, call ali gostiteljska funkcija, priklicana iz JS — gre skozi pretvornik:
- PHP → JS, ko vrednosti gostitelja prehajajo v mehanizem (
setGlobal(), argumenticall(), vrnjene vrednosti gostiteljskih funkcij). - JS → PHP, ko vrednosti JS prehajajo nazaj. Obstajata dve poti z nekoliko različnimi pravili: rezultati
eval()incall()ter argumenti, ki jih JS posreduje gostiteljski funkciji.
Pretvorba kopira. Primitivi se preslikajo v primitive, polja in objekti se kopirajo v nove strukture, funkcije PHP pa se ovijejo kot funkcije JS. Vse se kopira, razen samih funkcij, ki ostanejo deljene prek sklica.
PHP → JS
| Vrednost PHP | Rezultat JS |
|---|---|
null |
null |
true / false |
true / false |
int |
number (dvojna natančnost IEEE 754; cela števila nad 2^53 izgubijo natančnost) |
float |
number (NaN, ±Infinity, ±0 se ohranijo) |
string |
string (bajti PHP UTF-8 se interpretirajo kot 16-bitne kodne enote UTF-16), tudi če poimenujejo funkcijo PHP |
seznam array (ključi 0, 1, 2, … po vrsti) |
novo Array, elementi se pretvorijo rekurzivno |
katerikoli drug array |
nov navaden Object, vrednosti se pretvorijo rekurzivno |
Closure, objekt, ki ga je mogoče priklicati, funkcijsko polje, kot je [$object, 'method'] |
function, ki kliče funkcijo PHP |
katerikoli drug objekt, Generator, vir |
undefined |
Polja z mešanimi ključi (PHP: [0 => 'a', 'name' => 'b']) postanejo navadni objekti, ker morajo imeti polja v JS zvezno območje celih števil kot indeksov.
$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
Ker se polja kopirajo, spremembe v JS ostanejo v JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
Objekti PHP, ki jih ni mogoče priklicati, se ne pretvorijo; prispejo kot undefined (težava #18). Najprej jih pretvorite v polja, na primer z get_object_vars(), ali njihove metode izpostavite kot zaprtja. Glejte Deljeni objekti.
JS → PHP: rezultati eval() in call()
| Vrednost JS | Rezultat PHP |
|---|---|
undefined, null |
null |
boolean |
bool |
number (celo število, znotraj območja int) |
int (-0 postane 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (drugo) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
seznam array, elementi se pretvorijo rekurzivno |
TypedArray |
seznam array števil |
katerikoli drug objekt (navadni objekti, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
asociativno array njegovih lastnih podatkovnih lastnosti; lastnosti z dostopniki in funkcijskimi vrednostmi se preskočijo |
Zadnja vrstica pomeni, da se objekti, katerih stanje je shranjeno v notranjih režah, pretvorijo v prazno polje: new Date(0), new Map([["a", 1]]) in Promise.resolve(5) vsi vrnejo []. Error vrne svoj lastni message in stack. Objekt, ki se sklicuje sam nase, pretvori svoje notranje sklice v 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)'); // []
Vrednost pretvorite znotraj JS, kadar jo potrebujete v 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: argumenti gostiteljskih funkcij
Argumenti, ki jih JS posreduje gostiteljski funkciji PHP, sledijo istim pravilom za primitive, polja in navadne objekte, z naslednjimi razlikami:
| Vrednost JS | Argument PHP |
|---|---|
TypedArray, ArrayBuffer, DataView |
lasten objekt JsValue mehanizma, brez pretvorbe |
| objekt | asociativno array vseh njegovih lastnih lastnosti, vključno z dostopniki (katerih pridobivalniki se izvedejo) in lastnostmi s funkcijskimi vrednostmi |
| puščična funkcija | array (običajno prazna) |
function, deklariran s ključno besedo function, ali katerikoli objekt, ki se sklicuje sam nase |
brez pretvorbe: kopiranje se nikoli ne konča in PHP zmanjka pomnilnika (težava #21) |
Za zdaj ni mogoče prejeti povratnega klica JS v gostiteljski funkciji. Čez to mejo prenašajte podatke, ne funkcij.
Števila in natančnost
Števila JavaScript so dvojne natančnosti IEEE 754. Celi števili PHP sta izvorni za platformo (64-bitni v vsaki sodobni gradnji). Pretvorba natančno ohrani cela števila do ±2^53; večja cela števila PHP postanejo number in izgubijo natančnost po 16 decimalnih mestih. Če potrebujete natančno aritmetiko s celimi števili, večjimi od te meje, uporabite BigInt znotraj JS, rezultat pa posredujte kot niz.
Nizi
Nizi PHP so polja bajtov; nizi JS so zaporedja 16-bitnih kodnih enot UTF-16. Mehanizem jih poveže tako, da bajte PHP interpretira kot UTF-8 in jih interno znova kodira v UTF-16. To je brezizgubno za veljaven vhod UTF-8. Bajti, ki niso veljaven UTF-8, se posredujejo nespremenjeni, namesto da bi jih nadomestili z U+FFFD.
String.length, indeksirani dostop (s[0]) in rezanje niza štejejo 16-bitne kodne enote UTF-16, kot določa specifikacija ECMAScript — ne kodnih točk ali bajtov. Nizi z znaki izven osnovne večjezične ravnine, kot je '🍕', imajo zato .length === 2 (dve nadomestni polovici znaka U+1F355).
Stabilnost povratnega kroga
Navadni podatki preživijo povratni krog: vrednost PHP iz skalarjev, seznamov in asociativnih polj, ki vstopi prek setGlobal() in se vrne prek eval() ali call(), ustvari enako strukturo, razen da se celoštevilska števila s plavajočo vejico vrnejo kot int (1.0 vrne 1).
Vse drugo v zgornjih tabelah, kar se preslika v null, undefined ali [], se ne ohrani pri povratnem krogu.