
# Conversia valorilor

Fiecare apel care traversează limita PHP↔JS — `setGlobal`, `call`, o funcție gazdă apelată din JS — trece printr-un convertor:

- **PHP → JS** când valorile gazdei ajung în motor (`setGlobal()`, argumentele lui `call()`, valorile returnate de funcțiile gazdă).
- **JS → PHP** când valorile JS sunt trimise înapoi. Există două căi cu reguli ușor diferite: rezultatele lui `eval()` și `call()`, respectiv argumentele pe care JS le transmite unei funcții gazdă.

Conversia copiază valorile. Primitivele se mapează la primitive, tablourile și obiectele sunt copiate în structuri noi, iar funcțiile apelabile PHP sunt încapsulate ca funcții JS. Nimic nu este partajat prin referință, cu excepția funcțiilor apelabile în sine.

## PHP → JS

| Valoare PHP | Rezultat JS |
|---|---|
| `null` | `null` |
| `true` / `false` | `true` / `false` |
| `int` | `number` (double IEEE 754; întregii mai mari de 2^53 își pierd precizia) |
| `float` | `number` (NaN, ±Infinity, ±0 păstrate) |
| `string` | `string` (octeții UTF-8 PHP interpretați ca unități de cod UTF-16), chiar și când denumește o funcție PHP |
| listă `array` (cheile 0, 1, 2, … în ordine) | `Array` nou, elementele convertite recursiv |
| orice alt `array` | `Object` simplu nou, valorile convertite recursiv |
| `Closure`, obiect apelabil, tablou apelabil precum `[$object, 'method']` | `function` care apelează funcția apelabilă PHP |
| orice alt obiect, `Generator`, resursă | `undefined` |

Tablourile cu chei mixte (PHP, `[0 => 'a', 'name' => 'b']`) devin obiecte simple, deoarece tablourile din JS trebuie să aibă o secvență continuă de indici întregi.

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

Deoarece tablourile sunt copiate, modificările aduse în JS rămân în JS:

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

Obiectele PHP care nu sunt apelabile nu se convertesc; ajung ca `undefined` ([problema #18](https://github.com/inline0/phasis/issues/18)). Convertiți-le mai întâi în tablouri, de exemplu cu `get_object_vars()`, sau expuneți-le metodele ca funcții anonime. Consultați [Obiecte partajate](https://phasis.inline0.dev/ro/docs/interop/shared-objects).

## JS → PHP: rezultate `eval()` și `call()`

| Valoare JS | Rezultat PHP |
|---|---|
| `undefined`, `null` | `null` |
| `boolean` | `bool` |
| `number` (integral, în intervalul int) | `int` (`-0` devine `0`) |
| `NaN`, `Infinity`, `-Infinity` | `NAN`, `INF`, `-INF` |
| `number` (altul) | `float` |
| `string` | `string` (UTF-8) |
| `bigint`, `symbol` | `null` |
| `function` | `null` |
| `Array` | listă `array`, elementele convertite recursiv |
| `TypedArray` | listă `array` de numere |
| orice alt obiect (obiecte simple, `Error`, `Date`, `Map`, `Set`, `Promise`, `ArrayBuffer`, …) | `array` asociativ, cu propriile proprietăți de date, omițând accesorii și proprietățile cu valori funcție |

Ultimul rând înseamnă că obiectele a căror stare se află în sloturi interne se convertesc într-un tablou gol: `new Date(0)`, `new Map([["a", 1]])` și `Promise.resolve(5)` returnează toate `[]`. Un `Error` returnează propriile `message` și `stack`. Un obiect care face referire la sine își convertește referințele interne la `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)');              // []
```

Convertiți în interiorul lui JS când aveți nevoie de una dintre aceste valori în 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: argumentele funcțiilor gazdă

Argumentele pe care JS le transmite unei funcții gazdă PHP urmează aceleași reguli pentru primitive, tablouri și obiecte simple, cu următoarele diferențe:

| Valoare JS | Argument PHP |
|---|---|
| `TypedArray`, `ArrayBuffer`, `DataView` | propriul obiect `JsValue` al motorului, neconvertit |
| obiect | `array` asociativ cu toate proprietățile proprii, inclusiv accesorii (ale căror funcții getter sunt executate) și proprietăți cu valori funcție |
| funcție săgeată | un `array` (de obicei gol) |
| `function` declarat cu cuvântul-cheie `function` sau orice obiect care face referire la sine | fără conversie: copia nu se termină niciodată, iar PHP rămâne fără memorie ([problema #21](https://github.com/inline0/phasis/issues/21)) |

Momentan nu există nicio modalitate de a primi un apel invers JS într-o funcție gazdă. Treceți date, nu funcții, peste această limită.

## Numere și precizie

Numerele JavaScript sunt valori double IEEE 754. Întregii PHP sunt nativi platformei (64 de biți în fiecare versiune modernă). Conversia păstrează exact numerele întregi până la ±2^53; întregii PHP mai mari devin `number` și își pierd precizia după 16 cifre zecimale. Folosiți `BigInt` în JS dacă aveți nevoie de calcule exacte cu întregi mai mari și transmiteți rezultatul ca șir de caractere.

## Șiruri de caractere

Șirurile PHP sunt tablouri de octeți; șirurile JS sunt secvențe de unități de cod UTF-16. Motorul face conversia interpretând octeții PHP ca UTF-8 și recodificându-i intern în UTF-16. Conversia nu pierde date pentru intrări UTF-8 valide. Octeții care nu sunt UTF-8 valid sunt transmiși neschimbați, în loc să fie înlocuiți cu U+FFFD.

`String.length`, accesul după index (`s[0]`) și extragerea de subșiruri numără unitățile de cod UTF-16, conform specificației ECMAScript — nu punctele de cod și nici octeții. Prin urmare, șirurile care conțin caractere astrale precum `'🍕'` au `.length === 2` (cele două jumătăți surrogate ale lui U+1F355).

## Stabilitatea conversiei dus-întors

Datele simple supraviețuiesc unei conversii dus-întors: o valoare PHP formată din scalari, liste și tablouri asociative, transmisă prin `setGlobal()` și returnată prin `eval()` sau `call()`, produce aceeași structură, cu excepția valorilor float întregi, care revin ca `int` (`1.0` returnează `1`).

Nimic altceva din tabelele de mai sus care se mapează la `null`, `undefined` sau `[]` nu poate fi convertit dus-întors.
