
# Reikšmių konvertavimas

Kiekvienas kvietimas, peržengiantis PHP↔JS ribą, — `setGlobal`, `call` arba iš JS iškviečiama pagrindinio kompiuterio funkcija, — pereina per konverterį:

- **PHP → JS**, kai pagrindinio kompiuterio reikšmės patenka į variklį (`setGlobal()`, `call()` argumentai, pagrindinio kompiuterio funkcijų grąžinamos reikšmės).
- **JS → PHP**, kai JS reikšmės perduodamos atgal. Yra du keliai su šiek tiek skirtingomis taisyklėmis: `eval()` ir `call()` rezultatai bei argumentai, kuriuos JS perduoda pagrindinio kompiuterio funkcijai.

Konvertuojant sukuriamos kopijos. Primityviosios reikšmės susiejamos su primityviosiomis, masyvai ir objektai nukopijuojami į naujas struktūras, o PHP kviečiamieji objektai apgaubiami kaip JS funkcijos. Niekuo nebendrinama pagal nuorodą, išskyrus pačius kviečiamuosius objektus.

## PHP → JS

| PHP reikšmė | JS rezultatas |
|---|---|
| `null` | `null` |
| `true` / `false` | `true` / `false` |
| `int` | `number` (IEEE 754 double; sveikieji skaičiai, didesni nei 2^53, praranda tikslumą) |
| `float` | `number` (NaN, ±Infinity, ±0 išsaugomi) |
| `string` | `string` (UTF-8 PHP baitai interpretuojami kaip UTF-16 kodo vienetai), net jei pavadinimas nurodo PHP funkciją |
| sąrašas `array` (raktai 0, 1, 2, … eilės tvarka) | naujas `Array`, elementai konvertuojami rekursyviai |
| bet kuris kitas `array` | naujas paprastas `Object`, reikšmės konvertuojamos rekursyviai |
| `Closure`, iškviečiamasis objektas, kviečiamasis masyvas, pavyzdžiui, `[$object, 'method']` | `function`, iškviečiantis kviečiamąjį PHP |
| bet kuris kitas objektas, `Generator`, išteklius | `undefined` |

Masyvai su mišriais raktais (PHP `[0 => 'a', 'name' => 'b']`) tampa paprastais objektais, nes JS masyvai privalo turėti ištisinę sveikųjų skaičių indeksų seką.

```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
```

Kadangi masyvai kopijuojami, JS pakeitimai lieka JS pusėje:

```php
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list);                 // 3
$engine->eval('list.length'); // 4
```

PHP nekviečiami objektai nekonvertuojami; jie perduodami kaip `undefined` ([18 klaida](https://github.com/inline0/phasis/issues/18)). Pirmiausia konvertuokite juos į masyvus, pavyzdžiui, naudodami `get_object_vars()`, arba atskleiskite jų metodus kaip uždarinius. Žr. [Bendrinami objektai](https://phasis.inline0.dev/lt/docs/interop/shared-objects).

## JS → PHP: `eval()` ir `call()` rezultatai

| JS reikšmė | PHP rezultatas |
|---|---|
| `undefined`, `null` | `null` |
| `boolean` | `bool` |
| `number` (sveikasis skaičius, telpantis int intervale) | `int` (`-0` tampa `0`) |
| `NaN`, `Infinity`, `-Infinity` | `NAN`, `INF`, `-INF` |
| `number` (kitas) | `float` |
| `string` | `string` (UTF-8) |
| `bigint`, `symbol` | `null` |
| `function` | `null` |
| `Array` | sąrašas `array`, elementai konvertuojami rekursyviai |
| `TypedArray` | skaičių sąrašas `array` |
| bet kuris kitas objektas (paprasti objektai, `Error`, `Date`, `Map`, `Set`, `Promise`, `ArrayBuffer`, …) | asociatyvusis `array`, sudarytas iš jo paties duomenų savybių; prieigos savybės ir funkcijų reikšmes turinčios savybės praleidžiamos |

Paskutinės eilutės atveju objektai, kurių būsena saugoma vidiniuose lizduose, konvertuojami į tuščią masyvą: `new Date(0)`, `new Map([["a", 1]])` ir `Promise.resolve(5)` visi grąžina `[]`. `Error` grąžina savo `message` ir `stack`. Objektas su nuoroda į save savo vidines nuorodas konvertuoja į `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)');              // []
```

Jei kurią nors iš šių reikšmių reikia naudoti PHP, konvertuokite ją JS viduje:

```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: pagrindinio kompiuterio funkcijų argumentai

Argumentams, kuriuos JS perduoda PHP pagrindinio kompiuterio funkcijai, taikomos tos pačios primityviųjų reikšmių, masyvų ir paprastų objektų taisyklės su šiais skirtumais:

| JS reikšmė | PHP argumentas |
|---|---|
| `TypedArray`, `ArrayBuffer`, `DataView` | paties variklio `JsValue` objektas, nekonvertuotas |
| objektas | asociatyvusis `array`, apimantis visas jo savybes, įskaitant prieigos savybes (kurių gavimo funkcijos vykdomos) ir funkcijų reikšmes turinčias savybes |
| rodyklinė funkcija | `array` (paprastai tuščias) |
| `function`, deklaruotas naudojant raktažodį `function`, arba bet kuris objektas, nurodantis į save | nekonvertuojamas: kopijavimas niekada nesibaigia, o PHP pritrūksta atminties ([issue #21](https://github.com/inline0/phasis/issues/21)) |

Kol kas negalima gauti JS atgalinio iškvietimo pagrindinio kompiuterio funkcijoje. Per šią ribą perduokite duomenis, o ne funkcijas.

## Skaičiai ir tikslumas

JavaScript skaičiai yra IEEE 754 double. PHP sveikieji skaičiai priklauso nuo platformos (visose šiuolaikinėse versijose – 64 bitų). Konvertuojant tiksliai išsaugomi sveikieji skaičiai iki ±2^53; didesni PHP sveikieji skaičiai tampa `number`, todėl praranda tikslumą po 16 dešimtainių skaitmenų. Jei reikia tiksliai atlikti aritmetinius veiksmus su didesniais sveikaisiais skaičiais, naudokite `BigInt` JS viduje ir perduokite rezultatą kaip eilutę.

## Eilutės

PHP eilutės yra baitų masyvai, o JS eilutės – UTF-16 kodo vienetų sekos. Variklis jas susieja, interpretuodamas PHP baitus kaip UTF-8 ir viduje perkoduodamas į UTF-16. Jei įvestis yra tinkamas UTF-8, duomenys neprarandami. Netinkami UTF-8 baitai perduodami nepakeisti, jų nepakeičiant U+FFFD.

`String.length`, indeksavimas (`s[0]`) ir dalies iškirpimas skaičiuoja UTF-16 kodo vienetus pagal ECMAScript specifikaciją — ne kodo taškus ir ne baitus. Todėl eilutėse su astraliniais simboliais, pavyzdžiui, `'🍕'`, yra `.length === 2` (dvi pakaitinės U+1F355 dalys).

## Grįžtamojo konvertavimo stabilumas

Paprasti duomenys po konvertavimo pirmyn ir atgal išlieka: PHP reikšmė, sudaryta iš skaliarų, sąrašų ir asociatyviųjų masyvų, perduota per `setGlobal()` ir grąžinta per `eval()` arba `call()`, išlaiko tą pačią struktūrą. Išimtis – sveikųjų skaičių reikšmes turinčios slankiojo kablelio reikšmės grįžta kaip `int` (`1.0` grąžina `1`).

Visa kita pirmiau pateiktose lentelėse, kas susiejama su `null`, `undefined` arba `[]`, po konvertavimo pirmyn ir atgal neišlieka.
