Siirry sisältöön

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()- ja call()-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.

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

Koska taulukot kopioidaan, kohteessa JS tehdyt muutokset jäävät kohteeseen JS:

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

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

Muunna arvo kohteessa JS, kun tarvitset jonkin näistä arvoista kohteessa 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: 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.