
# 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](https://github.com/inline0/phasis/issues/18)). 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](https://phasis.inline0.dev/lv/docs/interop/shared-objects).

## 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](https://github.com/inline0/phasis/issues/21)) |

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.
