Preskoči na vsebino

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(), argumenti call(), vrnjene vrednosti gostiteljskih funkcij).
  • JS → PHP, ko vrednosti JS prehajajo nazaj. Obstajata dve poti z nekoliko različnimi pravili: rezultati eval() in call() 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.

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

Ker se polja kopirajo, spremembe v JS ostanejo v JS:

PHP
$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.

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)');              // []

Vrednost pretvorite znotraj JS, kadar jo potrebujete v PHP:

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.