Vērtību pārveide
Katrs izsaukums, kas šķērso PHP↔JS robežu — setGlobal, call, no JS izsaukta resursdatora funkcija — tiek veikts, izmantojot pārveidotāju:
- PHP → JS, kad resursdatora vērtības nonāk dzinējā (
setGlobal(),call()argumenti, resursdatora funkciju atgrieztās vērtības). - JS → PHP, kad JS vērtības tiek nodotas atpakaļ. Ir divi ceļi ar nedaudz atšķirīgiem noteikumiem:
eval()uncall()rezultāti, kā arī argumenti, ko JS nodod resursdatora funkcijai.
Pārveidojot tiek veidotas kopijas. Primitīvie tipi tiek kartēti uz primitīvajiem tipiem, masīvi un objekti tiek kopēti jaunās struktūrās, bet PHP izsaucamie objekti tiek iesaiņoti kā JS funkcijas. Nekas netiek koplietots pēc atsauces, izņemot pašus izsaucamos objektus.
PHP → JS
| PHP vērtība | JS rezultāts |
|---|---|
null |
null |
true / false |
true / false |
int |
number (IEEE 754 dubultprecizitāte; veseli skaitļi virs 2^53 zaudē precizitāti) |
float |
number (NaN, ±Infinity, ±0 tiek saglabāti) |
string |
string (UTF-8 PHP baiti tiek interpretēti kā UTF-16 koda vienības), arī tad, ja tie apzīmē PHP funkciju |
saraksta array (atslēgas 0, 1, 2, … secībā) |
jauns Array, elementi tiek pārveidoti rekursīvi |
jebkurš cits array |
jauns parasts Object, vērtības tiek pārveidotas rekursīvi |
Closure, izsaucams objekts, izsaucams masīvs, piemēram, [$object, 'method'] |
function, kas izsauc PHP izsaucamo objektu |
jebkurš cits objekts, Generator, resurss |
undefined |
Masīvi ar jaukta tipa atslēgām (PHP [0 => 'a', 'name' => 'b']) kļūst par parastiem objektiem, jo JS masīviem jābūt nepārtrauktam veselu skaitļu indeksu diapazonam.
$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
Tā kā masīvi tiek kopēti, JS veiktās izmaiņas paliek JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
PHP objekti, kas nav izsaucami, netiek pārveidoti; tie tiek saņemti kā undefined (18. problēma). Vispirms pārveidojiet tos par masīviem, piemēram, izmantojot get_object_vars(), vai atklājiet to metodes kā slēgtās funkcijas. Skatiet sadaļu Koplietojami objekti.
JS → PHP: eval() un call() rezultāti
| JS vērtība | PHP rezultāts |
|---|---|
undefined, null |
null |
boolean |
bool |
number (vesels skaitlis, int diapazonā) |
int (-0 kļūst par 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (cita) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
saraksts array, elementi tiek pārveidoti rekursīvi |
TypedArray |
skaitļu saraksts array |
jebkurš cits objekts (parasti objekti, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
asociatīvais array ar tā īpašajām datu īpašībām, izlaižot piekļuves elementus un funkciju vērtību īpašības |
Pēdējā rinda nozīmē, ka objekti, kuru stāvoklis glabājas iekšējās vietās, tiek pārveidoti par tukšu masīvu: new Date(0), new Map([["a", 1]]) un Promise.resolve(5) visi atgriež []. Error atgriež savu message un stack. Objektā ar atsauci uz sevi iekšējās atsauces tiek pārveidotas par 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)'); // []
Ja kāda no šīm vērtībām nepieciešama PHP, pārveidojiet to JS iekšienē:
$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: resursdatora funkciju argumenti
Argumentiem, ko JS nodod PHP resursdatora funkcijai, tiek piemēroti tie paši noteikumi attiecībā uz primitīvajiem tipiem, masīviem un parastiem objektiem, taču ir šādas atšķirības:
| JS vērtība | PHP arguments |
|---|---|
TypedArray, ArrayBuffer, DataView |
paša dzinēja JsValue objekts, netiek pārveidots |
| objekts | asociatīvais array ar visām tā īpašībām, tostarp piekļuves elementiem (tiek izsaukti to getter) un funkciju vērtību īpašībām |
| bultas funkcija | array (parasti tukšs) |
function, kas deklarēts ar atslēgvārdu function, vai jebkurš objekts, kas atsaucas pats uz sevi |
netiek pārveidots: kopēšana nekad nebeidzas, un PHP pietrūkst atmiņas (21. problēma) |
Pagaidām nav iespējams resursdatora funkcijā saņemt JS atzvanīšanas funkciju. Pāri šai robežai nododiet datus, nevis funkcijas.
Skaitļi un precizitāte
JavaScript skaitļi ir IEEE 754 dubultprecizitātes skaitļi. PHP int ir platformas sākotnējā formāta skaitļi (visās mūsdienu versijās 64 bitu). Pārveide precīzi saglabā veselus skaitļus līdz ±2^53; lielāki PHP int kļūst par number un zaudē precizitāti pēc 16 decimālcipariem. Ja nepieciešami precīzi aprēķini ar lielākiem veseliem skaitļiem, izmantojiet BigInt JS iekšienē un nododiet rezultātu kā virkni.
Virknes
PHP virknes ir baitu masīvi; JS virknes ir UTF-16 koda vienību secības. Dzinējs tās savieno, interpretējot PHP baitus kā UTF-8 un iekšēji pārkodējot par UTF-16. Derīgai UTF-8 ievadei šī pārveide nezaudē datus. Baiti, kas nav derīgs UTF-8, tiek nodoti tālāk nemainīti, nevis aizstāti ar U+FFFD.
String.length, indeksēta piekļuve (s[0]) un apakšvirkņu izgriešana atbilstoši ECMAScript specifikācijai skaita UTF-16 koda vienības, nevis koda punktus vai baitus. Tāpēc virknēm, kurās ir astrālās rakstzīmes, piemēram, '🍕', ir .length === 2 (abas U+1F355 surogātpuses).
Atgriezeniskās pārveidošanas stabilitāte
Vienkāršie dati saglabājas pēc atgriezeniskas pārveidošanas: PHP vērtība, kas sastāv no skalāriem, sarakstiem un asociatīvajiem masīviem, ievadīta ar setGlobal() un atgriezta ar eval() vai call(), dod to pašu struktūru, izņemot to, ka veselu skaitļu tipa peldošā komata vērtības tiek atgrieztas kā int (1.0 atgriež 1).
Viss pārējais iepriekšējās tabulās, kas tiek pārveidots par null, undefined vai [], pēc pārveidošanas atpakaļ nesaglabājas.