Arvojen muunnos
Jokainen kutsu, joka ylittää rajan PHP↔JS — setGlobal, call tai kohteesta JS kutsuttu isäntäfunktio — kulkee muuntimen kautta:
- PHP → JS, kun isäntäarvot siirtyvät moottoriin (
setGlobal(),call()-argumentit ja isäntäfunktioiden paluuarvot). - JS → PHP, kun JS-arvot siirtyvät takaisin ulos. Käytössä on kaksi hieman eri säännöillä toimivaa polkua:
eval()- jacall()-tulokset sekä argumentit, jotka JS välittää isäntäfunktiolle.
Muunnos kopioi arvot. Primitiivit muunnetaan primitiiveiksi, taulukot ja oliot kopioidaan uusiin rakenteisiin, ja PHP-kutsuttavat kääritään JS-funktioiksi. Mitään ei jaeta viitteenä kutsuttavia itseään lukuun ottamatta.
PHP → JS
| PHP-arvo | JS-tulos |
|---|---|
null |
null |
true / false |
true / false |
int |
number (IEEE 754 -kaksoistarkkuusluku; kokonaisluvut yli 2^53 menettävät tarkkuutta) |
float |
number (NaN, ±Infinity ja ±0 säilyvät) |
string |
string (UTF-8 PHP-tavut tulkitaan UTF-16-koodiyksiköiksi), myös silloin, kun nimi viittaa PHP-funktioon |
list array (avaimet 0, 1, 2, … järjestyksessä) |
uusi Array, alkiot muunnetaan rekursiivisesti |
mikä tahansa muu array |
uusi tavallinen Object, arvot muunnetaan rekursiivisesti |
Closure, kutsuttava olio tai kutsuttava taulukko, kuten [$object, 'method'] |
function, joka kutsuu PHP-kutsuttavaa |
mikä tahansa muu olio, Generator tai resurssi |
undefined |
Sekalaisten avainten taulukoista (kohteen PHP [0 => 'a', 'name' => 'b']) tulee tavallisia olioita, koska kohteen JS taulukoiden indeksien on muodostettava yhtenäinen kokonaislukujono.
$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
Koska taulukot kopioidaan, kohteessa JS tehdyt muutokset jäävät kohteeseen JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
PHP-oliot, jotka eivät ole kutsuttavia, eivät muunnu; ne saapuvat muodossa undefined (issue #18). Muunna ne ensin taulukoiksi esimerkiksi get_object_vars()-määrityksellä tai tarjoa niiden metodit sulkeumina. Katso Jaetut oliot.
JS → PHP: eval()- ja call()-tulokset
| JS-arvo | PHP-tulos |
|---|---|
undefined, null |
null |
boolean |
bool |
number (kokonaisluku, int-alueella) |
int (-0 muuttuu arvoksi 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (muu) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
list array, alkiot muunnetaan rekursiivisesti |
TypedArray |
list array numeroita |
mikä tahansa muu olio (tavalliset oliot, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
assosiatiivinen array, joka sisältää olion omat dataominaisuudet; getterit ja funktioarvoiset ominaisuudet ohitetaan |
Viimeinen rivi tarkoittaa, että oliot, joiden tila on sisäisissä paikoissa, muuntuvat tyhjiksi taulukoiksi: new Date(0), new Map([["a", 1]]) ja Promise.resolve(5) palauttavat kaikki arvon []. Error palauttaa omat message- ja stack-arvonsa. Itseensä viittaavan olion sisäiset viittaukset muunnetaan arvoon 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)'); // []
Muunna arvo kohteessa JS, kun tarvitset jonkin näistä arvoista kohteessa 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: isäntäfunktion argumentit
Argumentit, jotka JS välittää PHP-isäntäfunktiolle, noudattavat samoja primitiivien, taulukoiden ja tavallisten olioiden sääntöjä, seuraavin poikkeuksin:
| JS-arvo | PHP-argumentti |
|---|---|
TypedArray, ArrayBuffer, DataView |
moottorin oma JsValue-olio, muuntamattomana |
| olio | assosiatiivinen array, joka sisältää kaikki olion omat ominaisuudet, myös getterit (joita kutsutaan) ja funktioarvoiset ominaisuudet |
| nuolifunktio | array (yleensä tyhjä) |
function, joka on määritetty avainsanalla function, tai mikä tahansa itseensä viittaava olio |
ei muunnosta: kopiointi ei pääty, ja PHP-muistista loppuu (issue #21) |
Isäntäfunktiossa ei vielä voi vastaanottaa JS-takaisinkutsua. Välitä rajan yli dataa, älä funktioita.
Numerot ja tarkkuus
JavaScript-numerot ovat IEEE 754-kaksoistarkkuuslukuja. PHP-kokonaisluvut ovat alustakohtaisia (64-bittisiä kaikissa nykyaikaisissa koontiversioissa). Muunnos säilyttää kokonaisluvut täsmälleen arvoon ±2^53 asti; suuremmat PHP-kokonaisluvut muuttuvat arvoon number ja menettävät tarkkuutta yli 16 desimaalin jälkeen. Käytä kohteessa JS toimintoa BigInt, jos tarvitset täsmällistä laskentaa sitä suuremmilla kokonaisluvuilla, ja välitä tulos ulos merkkijonona.
Merkkijonot
PHP-merkkijonot ovat tavutaulukoita; JS-merkkijonot ovat UTF-16-koodiyksikköjonoja. Moottori muuntaa ne tulkitsemalla PHP-tavut UTF-8-muodossa ja koodaamalla ne uudelleen sisäisesti UTF-16-muotoon. Tämä on häviötöntä kelvolliselle UTF-8-syötteelle. Virheelliset UTF-8-tavut välitetään sellaisinaan sen sijaan, että ne korvattaisiin merkillä U+FFFD.
String.length, indeksoitu käyttö (s[0]) ja viipalointi laskevat kaikki UTF-16-koodiyksiköt ECMAScript-määrityksen mukaisesti — eivät koodipisteitä tai tavuja. Siksi merkkijonoissa, joissa on astral-merkkejä, kuten '🍕', on .length === 2 (U+1F355:n kaksi surrogaattipuolikasta).
Edestakaisen muunnoksen vakaus
Pelkkä data säilyy edestakaisessa muunnoksessa: PHP-arvo, joka koostuu skalaareista, listoista ja assosiatiivisista taulukoista ja joka välitetään sisään setGlobal()-kautta ja palautetaan eval()- tai call()-kautta, tuottaa saman rakenteen, paitsi että kokonaislukuarvoiset liukuluvut palautuvat muodossa int (1.0 palauttaa arvon 1).
Kaikki muu yllä olevissa taulukoissa, joka muunnetaan muotoon null, undefined tai [], ei säily edestakaisessa muunnoksessa.