Conversione dei valori
Ogni chiamata che attraversa il confine PHP↔JS — setGlobal, call o una funzione host invocata da JS — passa attraverso un convertitore:
- PHP → JS quando i valori host entrano nel motore (
setGlobal(), argomenticall(), valori restituiti dalle funzioni host). - JS → PHP quando i valori JS escono. Ci sono due percorsi con regole leggermente diverse: i risultati di
eval()ecall(), e gli argomenti che JS passa a una funzione host.
La conversione copia i valori. Le primitive restano primitive, array e oggetti vengono copiati in nuove strutture e le funzioni richiamabili PHP sono racchiuse in funzioni JS. Nulla viene condiviso per riferimento, fatta eccezione per le funzioni richiamabili stesse.
PHP → JS
| Valore PHP | Risultato JS |
|---|---|
null |
null |
true / false |
true / false |
int |
number (double IEEE 754; gli interi oltre 2^53 perdono precisione) |
float |
number (NaN, ±Infinity, ±0 preservati) |
string |
string (byte UTF-8 PHP interpretati come unità di codice UTF-16), anche quando nomina una funzione PHP |
array elenco (chiavi 0, 1, 2, … in ordine) |
nuovo Array, elementi convertiti ricorsivamente |
qualsiasi altro array |
nuovo Object semplice, valori convertiti ricorsivamente |
Closure, oggetto invocabile, array richiamabile come [$object, 'method'] |
function che invoca la funzione richiamabile PHP |
qualsiasi altro oggetto, Generator, risorsa |
undefined |
Gli array con chiavi miste (PHP [0 => 'a', 'name' => 'b']) diventano oggetti semplici perché gli array JS devono avere indici interi contigui.
$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
Poiché gli array vengono copiati, le modifiche di JS rimangono in JS:
$list = [1, 2, 3];
$engine->setGlobal('list', $list);
$engine->eval('list.push(4)');
count($list); // 3
$engine->eval('list.length'); // 4
Gli oggetti PHP non richiamabili non vengono convertiti: arrivano come undefined (problema n. 18). Convertili prima in array, per esempio con get_object_vars(), oppure esponi i loro metodi tramite closure. Vedi Oggetti condivisi.
JS → PHP: risultati eval() e call()
| Valore JS | Risultato PHP |
|---|---|
undefined, null |
null |
boolean |
bool |
number (intero, nell'intervallo int) |
int (-0 diventa 0) |
NaN, Infinity, -Infinity |
NAN, INF, -INF |
number (altro) |
float |
string |
string (UTF-8) |
bigint, symbol |
null |
function |
null |
Array |
elenco array, elementi convertiti ricorsivamente |
TypedArray |
elenco array di numeri |
qualsiasi altro oggetto (oggetti semplici, Error, Date, Map, Set, Promise, ArrayBuffer, …) |
array associativo delle sue proprietà dati, escluse quelle accessorie e quelle con valori funzione |
L'ultima riga indica che gli oggetti il cui stato risiede in slot interni vengono convertiti in un array vuoto: new Date(0), new Map([["a", 1]]) e Promise.resolve(5) restituiscono tutti []. Un Error restituisce il proprio message e stack. Un oggetto che fa riferimento a sé stesso converte i propri riferimenti interni in null.
$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)'); // []
Esegui la conversione all'interno di JS quando ti serve uno di questi valori in 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: argomenti delle funzioni host
Gli argomenti che JS passa a una funzione host PHP seguono le stesse regole per primitive, array e oggetti semplici, con queste differenze:
| Valore JS | Argomento PHP |
|---|---|
TypedArray, ArrayBuffer, DataView |
oggetto JsValue del motore, non convertito |
| oggetto | array associativo di tutte le sue proprietà, incluse quelle accessorie (i cui getter vengono eseguiti) e quelle con valori funzione |
| funzione freccia | un array (di solito vuoto) |
function dichiarata con la parola chiave function, o qualsiasi oggetto che fa riferimento a sé stesso |
nessuna conversione: la copia non termina mai e PHP esaurisce la memoria (problema n. 21) |
Al momento non è possibile ricevere una callback JS in una funzione host. Passa dati, non funzioni, attraverso questo confine.
Numeri e precisione
I numeri JavaScript sono double IEEE 754. Gli interi PHP sono nativi della piattaforma (a 64 bit in ogni build moderna). La conversione conserva esattamente gli interi fino a ±2^53; gli interi PHP più grandi diventano number e perdono precisione oltre le 16 cifre decimali. Usa BigInt all'interno di JS se ti servono calcoli esatti con interi più grandi e passa il risultato come stringa.
Stringhe
Le stringhe PHP sono array di byte; le stringhe JS sono sequenze di unità di codice UTF-16. Il motore le converte interpretando i byte PHP come UTF-8 e ricodificandoli internamente in UTF-16. La conversione non comporta perdite per gli input UTF-8 validi. I byte che non sono UTF-8 valido vengono mantenuti invariati, anziché essere sostituiti con U+FFFD.
String.length, l'accesso tramite indice (s[0]) e lo slicing contano tutti le unità di codice UTF-16 secondo le specifiche ECMAScript, non i punti di codice né i byte. Le stringhe contenenti caratteri astrali come '🍕' hanno quindi .length === 2 (le due metà surrogate di U+1F355).
Stabilità del percorso di andata e ritorno
I dati semplici sopravvivono al percorso di andata e ritorno: un valore PHP composto da scalari, elenchi e array associativi, che entra tramite setGlobal() e torna tramite eval() o call(), produce la stessa struttura, salvo che i float interi tornano come int (1.0 restituisce 1).
Tutti gli altri valori nelle tabelle sopra che vengono convertiti in null, undefined o [] non sopravvivono al percorso di andata e ritorno.