
# Μετατροπή τιμών

Κάθε κλήση που διασχίζει το όριο PHP↔JS — `setGlobal`, `call`, μια συνάρτηση φιλοξενίας που καλείται από το JS — περνά από έναν μετατροπέα:

- **PHP → JS** όταν οι τιμές φιλοξενίας εισέρχονται στη μηχανή (`setGlobal()`, ορίσματα `call()`, τιμές επιστροφής συναρτήσεων φιλοξενίας).
- **JS → PHP** όταν οι τιμές JS επιστρέφουν προς τα έξω. Υπάρχουν δύο διαδρομές με ελαφρώς διαφορετικούς κανόνες: τα αποτελέσματα των `eval()` και `call()`, καθώς και τα ορίσματα που περνά το JS σε μια συνάρτηση φιλοξενίας.

Η μετατροπή δημιουργεί αντίγραφα. Οι πρωτογενείς τιμές αντιστοιχίζονται σε πρωτογενείς τιμές, οι πίνακες και τα αντικείμενα αντιγράφονται σε νέες δομές και οι κλήσιμες τιμές PHP τυλίγονται ως συναρτήσεις JS. Τίποτα δεν μοιράζεται με αναφορά, εκτός από τις ίδιες τις κλήσιμες τιμές.

## PHP → JS

| Τιμή PHP | Αποτέλεσμα JS |
|---|---|
| `null` | `null` |
| `true` / `false` | `true` / `false` |
| `int` | `number` (διπλής ακρίβειας IEEE 754· οι ακέραιοι πέρα από το 2^53 χάνουν ακρίβεια) |
| `float` | `number` (διατηρούνται NaN, ±Infinity, ±0) |
| `string` | `string` (τα byte UTF-8 PHP ερμηνεύονται ως μονάδες κώδικα UTF-16), ακόμη κι όταν αποτελεί το όνομα μιας συνάρτησης PHP |
| πίνακας λίστας `array` (κλειδιά 0, 1, 2, … με τη σειρά) | νέος `Array`, τα στοιχεία μετατρέπονται αναδρομικά |
| οποιοδήποτε άλλο `array` | νέο απλό `Object`, οι τιμές μετατρέπονται αναδρομικά |
| `Closure`, αντικείμενο με δυνατότητα κλήσης, κλήσιμος πίνακας όπως το `[$object, 'method']` | `function` που καλεί την κλήσιμη τιμή PHP |
| οποιοδήποτε άλλο αντικείμενο, `Generator`, πόρος | `undefined` |

Οι πίνακες με μικτά κλειδιά (τα `[0 => 'a', 'name' => 'b']` του PHP) γίνονται απλά αντικείμενα, επειδή οι πίνακες στο JS πρέπει να έχουν συνεχόμενες ακέραιες θέσεις ευρετηρίου.

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

Επειδή οι πίνακες αντιγράφονται, οι αλλαγές στο JS παραμένουν στο JS:

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

Τα αντικείμενα PHP που δεν είναι κλήσιμα δεν μετατρέπονται· φτάνουν ως `undefined` ([issue #18](https://github.com/inline0/phasis/issues/18)). Μετατρέψτε τα πρώτα σε πίνακες, για παράδειγμα με το `get_object_vars()`, ή εκθέστε τις μεθόδους τους ως συναρτήσεις κλεισίματος. Δείτε τα [Κοινόχρηστα αντικείμενα](https://phasis.inline0.dev/el/docs/interop/shared-objects).

## JS → PHP: αποτελέσματα `eval()` και `call()`

| Τιμή JS | Αποτέλεσμα PHP |
|---|---|
| `undefined`, `null` | `null` |
| `boolean` | `bool` |
| `number` (ακέραιος, εντός ορίων int) | `int` (το `-0` γίνεται `0`) |
| `NaN`, `Infinity`, `-Infinity` | `NAN`, `INF`, `-INF` |
| `number` (άλλο) | `float` |
| `string` | `string` (UTF-8) |
| `bigint`, `symbol` | `null` |
| `function` | `null` |
| `Array` | πίνακας `array`, τα στοιχεία μετατρέπονται αναδρομικά |
| `TypedArray` | πίνακας `array` αριθμών |
| οποιοδήποτε άλλο αντικείμενο (απλά αντικείμενα, `Error`, `Date`, `Map`, `Set`, `Promise`, `ArrayBuffer`, …) | συσχετιστικός `array` των δικών του ιδιοτήτων δεδομένων, παραλείποντας τους προσπελαστές και τις ιδιότητες με τιμή συνάρτησης |

Η τελευταία γραμμή σημαίνει ότι τα αντικείμενα των οποίων η κατάσταση βρίσκεται σε εσωτερικές υποδοχές μετατρέπονται σε κενό πίνακα: τα `new Date(0)`, `new Map([["a", 1]])` και `Promise.resolve(5)` επιστρέφουν όλα `[]`. Ένα `Error` επιστρέφει τα δικά του `message` και `stack`. Ένα αντικείμενο που αναφέρεται στον εαυτό του μετατρέπει τις εσωτερικές αναφορές του σε `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)');              // []
```

Μετατρέψτε τις τιμές μέσα στο JS όταν τις χρειάζεστε στο 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: ορίσματα συναρτήσεων φιλοξενίας

Τα ορίσματα που περνά το JS σε μια συνάρτηση φιλοξενίας PHP ακολουθούν τους ίδιους κανόνες για πρωτογενείς τιμές, πίνακες και απλά αντικείμενα, με τις εξής διαφορές:

| Τιμή JS | Όρισμα PHP |
|---|---|
| `TypedArray`, `ArrayBuffer`, `DataView` | το εγγενές αντικείμενο `JsValue` της μηχανής, χωρίς μετατροπή |
| αντικείμενο | συσχετιστικός `array` όλων των δικών του ιδιοτήτων, μαζί με τους προσπελαστές (εκτελούνται οι μέθοδοι λήψης τους) και τις ιδιότητες με τιμή συνάρτησης |
| συνάρτηση βέλους | ένα `array` (συνήθως κενό) |
| `function` που έχει δηλωθεί με τη λέξη-κλειδί `function`, ή οποιοδήποτε αντικείμενο αναφέρεται στον εαυτό του | καμία μετατροπή: η αντιγραφή δεν τερματίζεται ποτέ και το PHP εξαντλεί τη μνήμη ([issue #21](https://github.com/inline0/phasis/issues/21)) |

Προς το παρόν δεν υπάρχει τρόπος να λάβετε μια επανάκληση JS σε συνάρτηση φιλοξενίας. Περάστε δεδομένα, όχι συναρτήσεις, μέσα από αυτό το όριο.

## Αριθμοί και ακρίβεια

Οι αριθμοί JavaScript είναι διπλής ακρίβειας IEEE 754. Οι ακέραιοι PHP είναι εγγενείς στην πλατφόρμα (64 bit σε κάθε σύγχρονη έκδοση). Η μετατροπή διατηρεί ακριβώς τους ακεραίους έως ±2^53· οι μεγαλύτεροι ακέραιοι PHP γίνονται `number` και χάνουν ακρίβεια πέρα από τα 16 δεκαδικά ψηφία. Χρησιμοποιήστε το `BigInt` μέσα στο JS αν χρειάζεστε ακριβείς πράξεις με μεγαλύτερους ακεραίους και περάστε το αποτέλεσμα ως συμβολοσειρά.

## Συμβολοσειρές

Οι συμβολοσειρές PHP είναι πίνακες byte· οι συμβολοσειρές JS είναι ακολουθίες μονάδων κώδικα UTF-16. Η μηχανή τις γεφυρώνει ερμηνεύοντας τα byte PHP ως UTF-8 και κωδικοποιώντας τα ξανά εσωτερικά σε UTF-16. Αυτό γίνεται χωρίς απώλειες για έγκυρη είσοδο UTF-8. Τα byte που δεν αποτελούν έγκυρο UTF-8 περνούν αυτούσια, αντί να αντικαθίστανται από U+FFFD.

Η `String.length`, η πρόσβαση με δείκτη (`s[0]`) και η κατάτμηση μετρούν μονάδες κώδικα UTF-16 σύμφωνα με την προδιαγραφή ECMAScript — όχι σημεία κώδικα ούτε byte. Επομένως, οι συμβολοσειρές που περιέχουν χαρακτήρες εκτός βασικού πολυγλωσσικού επιπέδου, όπως το `'🍕'`, έχουν `.length === 2` (τα δύο μισά υποκατάστασης του U+1F355).

## Σταθερότητα μετά την επιστροφή

Τα απλά δεδομένα διατηρούνται σε μια πλήρη διαδρομή μετάβασης και επιστροφής: μια τιμή PHP που αποτελείται από βαθμωτές τιμές, λίστες και συσχετιστικούς πίνακες, όταν περνά μέσω του `setGlobal()` και επιστρέφει μέσω του `eval()` ή του `call()`, παράγει την ίδια δομή, με εξαίρεση ότι οι ακέραιες τιμές κινητής υποδιαστολής επιστρέφουν ως `int` (το `1.0` επιστρέφει `1`).

Οτιδήποτε άλλο στους παραπάνω πίνακες που αντιστοιχίζεται σε `null`, `undefined` ή `[]` δεν διατηρείται σε μια πλήρη διαδρομή μετάβασης και επιστροφής.
