Pāriet uz saturu

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

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

Tā kā masīvi tiek kopēti, JS veiktās izmaiņas paliek JS:

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

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

Ja kāda no šīm vērtībām nepieciešama PHP, pārveidojiet to JS iekšienē:

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: 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.