Liigu sisu juurde

Väärtuste teisendamine

Iga kõne, mis ületab piiri PHP↔JS — setGlobal, call, host-funktsiooni kutsumine JS-st — läbib teisendaja:

  • PHP → JS, kui hosti väärtused liiguvad mootorisse (setGlobal(), call() argumendid, host-funktsioonide tagastusväärtused).
  • JS → PHP, kui JS väärtused liiguvad tagasi välja. Kasutusel on kaks veidi erinevate reeglitega rada: eval() ja call() tulemused ning argumendid, mille JS host-funktsioonile edastab.

Teisendamisel tehakse koopia. Primitiivid teisenduvad primitiivideks, massiivid ja objektid kopeeritakse uutesse struktuuridesse ning PHP-i kutsutavad objektid pakendatakse JS funktsioonideks. Viite kaudu jagatakse ainult kutsutavaid objekte.

PHP → JS

PHP väärtus JS tulemus
null null
true / false true / false
int number (IEEE 754 double; täisarvud üle 2^53 kaotavad täpsuse)
float number (NaN, ±Infinity, ±0 säilivad)
string string (UTF-8 PHP baite tõlgendatakse UTF-16 koodiühikutena), ka siis, kui see nimetab PHP funktsiooni
loendi array (võtmed 0, 1, 2, … järjekorras) uus Array, elemendid teisendatakse rekursiivselt
mis tahes muu array uus lihtne Object, väärtused teisendatakse rekursiivselt
Closure, väljakutsutav objekt, väljakutsutav massiiv, näiteks [$object, 'method'] function, mis kutsub PHP-i väljakutsutavat objekti
mis tahes muu objekt, Generator, ressurss undefined

Segavõtmetega massiivid (PHP-i [0 => 'a', 'name' => 'b']) muutuvad lihtobjektideks, sest JS massiividel peab olema katkematu täisarvuline indeksiruum.

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

Kuna massiivid kopeeritakse, jäävad JS muudatused JS-i:

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

PHP objektid, mida ei saa välja kutsuda, ei teisendu ja saabuvad kujul undefined (probleem #18). Teisenda need esmalt massiivideks, näiteks käsuga get_object_vars(), või avalikusta nende meetodid sulunditena. Vt Shared objects.

JS → PHP: tulemused eval() ja call() korral

JS väärtus PHP tulemus
undefined, null null
boolean bool
number (täisarv, int-vahemikus) int (-0 muutub väärtuseks 0)
NaN, Infinity, -Infinity NAN, INF, -INF
number (muu) float
string string (UTF-8)
bigint, symbol null
function null
Array loend array, elemendid teisendatakse rekursiivselt
TypedArray arvude loend array
mis tahes muu objekt (lihtobjektid, Error, Date, Map, Set, Promise, ArrayBuffer, …) assotsiatiivne array, mis sisaldab objekti enda andmeomadusi; gettereid ja funktsiooniväärtusega omadusi ei arvestata

Viimane rida tähendab, et objektid, mille olek asub sisemistes pesades, teisenduvad tühjaks massiiviks: new Date(0), new Map([["a", 1]]) ja Promise.resolve(5) tagastavad kõik []. Error tagastab enda message ja stack. Iseendale viitava objekti sisemised viited teisenduvad väärtuseks 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)');              // []

Kui vajad mõnda neist väärtustest PHP-s, teisenda see JS sees:

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: host-funktsiooni argumendid

Argumendid, mille JS edastab host-funktsioonile PHP, järgivad primitiivide, massiivide ja lihtobjektide puhul samu reegleid, kuid erinevad järgmiselt:

JS väärtus PHP argument
TypedArray, ArrayBuffer, DataView mootori enda JsValue objekt, teisendamata
objekt assotsiatiivne array kõigi objekti enda omadustega, sealhulgas getteritega (mis käivitatakse) ja funktsiooniväärtusega omadustega
noolefunktsioon array (tavaliselt tühi)
function, mis on deklareeritud märksõnaga function, või mis tahes iseendale viitav objekt ei teisendata: kopeerimine ei lõpe ja PHP-l saab mälu otsa (probleem #21)

Host-funktsioonis ei saa praegu vastu võtta JS tagasihelistusfunktsiooni. Edasta üle selle piiri andmeid, mitte funktsioone.

Arvud ja täpsus

JavaScript arvud on IEEE 754 topelttäpsusega arvud. PHP täisarvud kasutavad platvormi algset vormingut (kõigis tänapäevastes järkudes 64-bitine). Teisendamisel säilivad täisarvud täpselt kuni ±2^53-ni; sellest suuremad PHP täisarvud muutuvad väärtuseks number ja kaotavad täpsuse üle 16 kümnendkoha. Kui vajad suuremate täisarvudega täpset arvutamist, kasuta selleks JS-s BigInt ja edasta tulemus stringina.

Stringid

PHP stringid on baidimassiivid; JS stringid on UTF-16 koodiühikute jadad. Mootor teisendab need, tõlgendades PHP baite UTF-8-na ja kodeerides need sisemiselt uuesti UTF-16-na. Kehtiv UTF-8-sisend teisendub kadudeta. Kehtetu UTF-8-ga baidid edastatakse muutmata, selle asemel et asendada need märgiga U+FFFD.

String.length, indekseerimine (s[0]) ja lõikamine loendavad ECMAScripti spetsifikatsiooni järgi UTF-16 koodiühikuid, mitte koodipunkte ega baite. Seetõttu on astralsümboleid, näiteks '🍕', sisaldavatel stringidel .length === 2 (U+1F355 kaks asenduspaari poolt).

Edasi-tagasi teisendamise stabiilsus

Lihtandmed säilivad edasi-tagasi teisendamisel: skalaare, loendeid ja assotsiatiivseid massiive sisaldav väärtus PHP, mis siseneb setGlobal() kaudu ja tuleb tagasi eval() või call() kaudu, annab sama struktuuri, välja arvatud see, et täisarvulised ujukomaarvud tulevad tagasi kujul int (1.0 tagastab 1).

Kõik muu ülaltoodud tabelites, mis teisendub kujule null, undefined või [], ei säili edasi-tagasi teisendamisel.