Generato con assistenza AI (Claude Fable 5). Data: 2026-06-13. Sorgente di riferimento: PHP 8.5.7,
/Volumes/Extreme Pro/Claude/php-8.5.7. Questo documento descrive il comportamento osservabile che la reimplementazione Rust deve riprodurre. Non descrive l'architettura interna di Zend se non dove necessario a chiarire la semantica.
Il valore PHP è una union taggata a 16 byte: union zend_value a 8 byte
(Zend/zend_types.h:335-353) + type tag e campi ausiliari (zend_types.h:355-380).
Type tag rilevanti per Tier 1 (zend_types.h:620-631): IS_UNDEF=0, IS_NULL, IS_FALSE, IS_TRUE, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRAY (più IS_REFERENCE, differito).
Semantica osservabile:
- IS_UNDEF è distinto da IS_NULL: leggere una variabile mai assegnata produce
Warning: Undefined variable $xe il valore NULL; la distinzione esiste solo nel motore. IS_FALSE/IS_TRUEsono due tag separati nel C; per noi èBool(bool)— non osservabile.- I tipi heap (string, array) sono refcounted con copy-on-write: l'assegnazione
$b = $acondivide il payload; la scrittura su un valore condiviso lo separa prima (SEPARATE_ARRAY/zend_string_separate). Osservabile: modificare$bnon tocca$a, e modificare l'array iterato by-value durante unforeachnon altera l'iterazione.
Zend/zend_types.h:393-398: { gc; hash h; size_t len; char val[] }.
- Le stringhe PHP sono sequenze di byte arbitrarie, non UTF-8.
strlenconta byte. Vincolo Rust:[u8], maiString. - Hash lazy: calcolato alla prima richiesta, 0 = non calcolato (
Zend/zend_string.h:130-133). Non osservabile nell'output → qualunque hash interno è corretto. - Interning dei literal: ottimizzazione, non semantica.
Zend/zend_types.h:408-432. L'array PHP è un ordered hash con chiavi int|string.
Comportamenti osservabili da riprodurre:
- Ordine di iterazione = ordine di inserimento, anche dopo
unset(foreach, var_dump, ecc.). La distinzione interna packed/mixed (Zend/zend_hash.c:345) è invisibile. - Canonicalizzazione chiavi (
Zend/zend_hash.c:3300,_zend_handle_numeric_str_ex): una chiave stringa diventa chiave int se: segno-opzionale, poi solo cifre, niente leading zero ("08"resta stringa;"0"da solo è int 0), lunghezza entroMAX_LENGTH_OF_LONG(Zend/zend_long.h:112= 20 su 64-bit), niente overflow (controlli espliciti per positivo e negativo),"-0"resta stringa. Inoltre (dalla conversione zval→chiave):1.5→ 1 con Deprecated,true→ 1,false→ 0,null→"". - next free element: l'append
$a[] = vusa max(indici int mai inseriti)+1; non decresce dopo unset dell'ultimo elemento. - Internal pointer (
reset/next/current/key/end): differito a quando i builtin relativi entreranno in scope.
_is_numeric_string_ex, Zend/zend_operators.c:3620-3750:
- Grammatica:
[ \t\n\r\v\f]* [+-]? ( digits [. digits]? | . digits ) ( [eE] [+-]? digits )?con trailing whitespace ammesso solo in modalitàallow_errors. "0x1A"NON è numerica (niente hex).".5"e"5."sono double.- Interi troppo lunghi (≥
MAX_LENGTH_OF_LONGcifre) → double. - Tre classi osservabili: numerica (intera o double), leading-numeric
(
"5abc": prefisso numerico + trailing data), non numerica.
- to bool (
Zend/zend_operators.c:687-756): falsy =null,false,0,0.0/-0.0,"","0"(solo esattamente"0":"0.0"e"00"sono truthy),[]. Tutto il resto truthy. - double → int (
Zend/zend_operators.h:126-172): troncamento verso zero se nel range; NaN/Inf/out-of-range → comportamento d'errore (deprecation/0) — verificare nei .phpt. - to string:
null→"",false→"",true→"1", int → decimale, double → §8. - Cast esplicito
(int)"abc"= 0 senza errore; aritmetica implicita su stringhe è in §7.
===(Zend/zend_operators.c:2474-2510): tipi diversi → false; double con semantica IEEE (0.0 === -0.0true,NAN === NANfalse); stringhe byte-a-byte; array: stessa sequenza ordinata di (chiave, valore) con valori ricorsivamente identici — l'ordine conta.==,<,<=(zend_compare,Zend/zend_operators.c:2306-2470): tabella per coppia di tipi. Punti salienti PHP 8:int|double vs string: se la stringa è numerica → confronto numerico (compare_long_to_string,Zend/zend_operators.c:3278); se NON numerica → il numero viene convertito a stringa e si confronta come stringhe (cambio PHP 8:0 == "abc"è false).string vs stringentrambe numeriche → confronto numerico (zendi_smart_streq,Zend/zend_operators.c:3373-3418); altrimenti byte-compare.null == ""true;null == falsetrue; bool vs qualunque → entrambi a bool.>/>=sono compilati come</<=a operandi scambiati.- NAN in confronto ordinato → mai minore/uguale.
- add/sub/mul (
add_function,Zend/zend_operators.c:1200; fast pathZend/zend_operators.h:704-806): int+int con overflow → double (controllo sign-bit); promozione int→double se un operando è double. - array + array: union, le chiavi del primo operando vincono.
- Operando stringa: numerica → numero; leading-numeric → numero con
Warning: A non-numeric value encountered; non numerica →TypeError: Unsupported operand types: string + int(PHP 8). - div: risultato int solo se divisione esatta tra int, altrimenti double;
divisione per zero →
DivisionByZeroError(uncaught → Fatal error, exit 255). - mod: operandi a int;
% 0→ DivisionByZeroError. - concat
.(concat_function,Zend/zend_operators.c:2017): entrambi a stringa (null→"", false→"", true→"1", int/double→stringa); array → TypeError ("Array to string conversion" Warning per echo di array:"Array"). - ++/-- (
increment_function,Zend/zend_operators.c:2712):++su null → 1;--su null → resta null;++/--su bool → no-op; su stringa numerica → aritmetica;++su stringa alfanumerica → incremento Perl-style con carry ("a"→"b","z"→"aa","a9"→"b0","Az"→"Ba"); dettagli di deprecation 8.3+ da verificare sui .phpt.
Due modalità distinte:
- echo / conversione a stringa: precision=14 cifre significative, trailing zero
rimossi, notazione esponenziale per magnitudini estreme (
1.0E+15: il punto decimale è sempre presente nell'esponenziale).0.1+0.2→"0.3". - var_dump / serialize / var_export: serialize_precision=-1 → shortest roundtrip
(
ext/standard/var.c:329,%.*HconPG(serialize_precision)).0.1+0.2→float(0.30000000000000004). - Speciali:
INF→INF,-INF,NAN;-0.0→-0.
ext/standard/var.c:
- scalari (
:317-332):bool(false),bool(true),NULL,int(42),float(1.5),string(3) "abc"(lunghezza in byte). - array (
:156, :45):array(2) {\n [0]=>\n int(1)\n ["k"]=>\n ...\n}— indentazione 2 spazi per livello, chiavi int[0]=>, chiavi stringa["k"]=>. - Nesting oltre profondità →
*RECURSION*(non raggiungibile senza riferimenti in Tier 1).
- Errori non fatali a display_errors on (
main/main.c:1493):\n+Warning: <msg> in <file> on line <line>+\n(stesso formato perDeprecated:,Notice:,Fatal error:). - Uncaught exception (
Zend/zend_exceptions.c:756):Fatal error: Uncaught <Class>: <msg> in <file>:<line>+\nStack trace:\n#0 {main}\n thrown in <file> on line <line>. - Messaggi chiave Tier 1:
Undefined variable $x(Warning),Undefined array key "k"/Undefined array key 0(Warning),Too few arguments to function f(), 1 passed in %s on line %d and exactly 2 expected(ArgumentCountError). - Exit code: fatal error → 255 (
Zend/zend.c:1625);exit(n)→ n; default 0.
| Caso | Comportamento atteso |
|---|---|
PHP_INT_MAX + 1 |
double 9.2233720368547758E+18 |
"10" == "1e1" |
true (entrambe numeriche) |
"abc" == 0 |
false (PHP 8) |
[] == false, [0] == true |
true, true |
$a[1.5] = x |
chiave 1, Deprecated: Implicit conversion |
$a["08"] vs $a["8"] |
chiavi DIVERSE (stringa vs int) |
unset($a[ultimo]); $a[]=v |
riusa indice successivo al max storico |
intdiv overflow, 0/0, 1%0 |
DivisionByZeroError |
"z"++ |
"aa"; "9"++ → int 10 |
echo di true/null |
1 / stringa vuota |
- Solo il codice tra
<?php ... ?>è eseguito; il testo fuori dai tag è emesso verbatim (incluso l'HTML before/after — Tier 1: gestire testo prima/dopo i tag). - Le funzioni utente sono hoisted: chiamabili prima della dichiarazione testuale (se la dichiarazione è top-level e incondizionata).
echoaccetta più argomenti separati da virgola;printritorna 1.- Output unbuffered su stdout per il CLI (l'ordine output/warning è interleaved).
Un lettore (umano o LLM) che parta da questo documento + i .phpt di Zend/tests deve
poter implementare il Tier 1 senza leggere altro C, ricorrendo al sorgente solo per i
casi ambigui che i test evidenzieranno.