Esperimento di traduzione PHP 8.5.7 (C) → Rust guidato dal comportamento osservabile, non dall'architettura interna dello Zend Engine.
Principio guida: il contratto da preservare non è il design di Zend (1999–2004) ma l'output osservabile di PHP. L'oracle esiste già: i ~21.500 test
.phptdel sorgente ufficiale. Qualunque runtime che produce lo stesso output è PHP. Questo trasforma il lavoro da traduzione del C a reimplementazione guidata dalla spec, dove il C si legge solo per inchiodare la semantica.
Metodologia: skill legacy-port, adattata (Strategia A adapter per il front-end +
full port semantico del solo zend_operators.c).
Steps 0–61 completati · 934 test verdi · clippy pulito · differential 37.835 casi a 0 mismatch.
Step 61 ha completato i suggerimenti della code-review esterna: (E) diff unificato nel
phpt-runner (--list-fails mostra un line-diff EXPECTF-aware invece di due blob troncati); (B)
flag PHP_RUST_TRACE che su stderr dumpa l'HIR (=hir/body) e/o traccia ogni statement
eseguito indentato per profondità di chiamata (=exec/all), per il triage lowering-vs-eval; e la
modularizzazione di lower.rs (3.783 → lower/{mod,stmt,class,expr}.rs, mod.rs 1.412
righe, −63%, zero cambi di comportamento); e (C) 7 test unitari oracle-independent su
php-types::ops (l'anima type-juggling, prima senza test inline). Scartata solo la macro di
binding builtin (rischiosa).
Step 60 ha modularizzato eval.rs (era un monolite da 6.965 righe, segnalato da una
code-review esterna): spezzato in eval/{mod,expr,stmt,calls,class,builtins}.rs, ognuno un
blocco impl Evaluator. Refactor puramente meccanico, zero cambi di comportamento, 927 test
verdi a ogni sotto-step; mod.rs ora 1.913 righe (−72%). (lower.rs, 3.783 righe, candidato per
lo stesso trattamento in futuro.)
Step 59 ha (a) implementato il CLI phpr (era uno stub fn main(){}): ora è un php
drop-in che esegue uno script e scrive lo stream CLI-faithful con exit code fedele, utile anche
come differential vs l'oracle; (b) chiuso 13 dei 29 fail sprintf/printf residui con un batch di
fedeltà (modificatore l, specifier sconosciuti→ValueError, errori catchable col tipo giusto,
conteggio "N arguments are required", threading dei warning di coercion, pad char in
left-justify). Sweep strings 229→242/393 (61.6%). I residui non sono bug del motore
(output byte-identico all'oracle) ma di runner-EXPECTF su binario e fopen(__FILE__) non
materializzato dall'harness (l'interleaving dei warning dell'evaluator è stato risolto allo
step 66). Infra: target-dir di cargo spostato su disco interno (il volume esterno rompeva le
build incrementali).
Step 58 ha chiuso il motore sprintf: (a) fix del crash capacity overflow su width/precision
oltre INT_MAX (un %9999…f abortiva l'intero run in-process) → ora ValueError; (b) sintassi
* (width/precision da argomento, PHP 8.4) con binding posizionale e validazione fedele; (c)
conversioni %g/%G/%h/%H (port di php_gcvt: fixed/scientific shortest-form, byte-exact
vs oracle su 24×9 casi). sprintf_star.phpt ora passa; lo sweep strings completa in-process
(229/393). Il valore è trasversale: %g e * sono comuni in tutto il corpus, e ogni run
in-process è ora robusto.
Step 57 ha aggiunto il secondo batch di funzioni stringa pure (strrpos/stripos/strripos,
strspn/strcspn, strtr byte-map + array, chunk_split, strip_tags, quotemeta,
levenshtein): sulla copia pulita di ext/standard/tests/strings il pass-rate sul runnable
sale a 58.0% (228/393) con --isolate (il run in-process aborta su un crash pre-esistente
di sprintf con la sintassi *, vedi diary/04-divergences.md). Il corpus ha fatto trovare e
fixare 1 bug di fedeltà (strtr("", $map) non deve emettere il Warning chiave-vuota).
Step 56 ha aggiunto un batch di funzioni stringa pure (bin2hex/hex2bin, addslashes/
stripslashes, substr_replace, nl2br, wordwrap, htmlspecialchars/htmlentities
- decode,
vsprintf/vprintf): sulla copia pulita diext/standard/tests/stringsil pass-rate sul runnable è 51% (143/280) alla prima sweep.
Step 55 ha aggiunto un batch di builtin stream/file read (file, readfile, fpassthru,
stream_get_contents, stream_copy_to_stream, ftruncate) + getenv/putenv +
disk_free_space/disk_total_space: sulla copia pulita di ext/standard/tests/file i
pass salgono 71 → 86 (skip −26).
Step 54 ha aggiunto due engine di parsing: scanf (sscanf/fscanf, con modo
return-array e modo by-reference) e CSV (str_getcsv/fgetcsv/fputcsv), eliminando
il bucket di skip "missing builtin: fscanf/fgetcsv/fputcsv" su ext/standard/tests/file.
Step 52 ha aggiunto il sottosistema filesystem (predicati file_exists/is_*/filetype,
famiglia stat/lstat/fstat + accessor, mutatori unlink/mkdir/rename/copy/touch/
symlink/chmod/…, scandir/glob/tempnam/tmpfile): sul corpus ext/standard/tests/file
i pass salgono 2 → 63. Step 53 ha aggiunto strstr/strrchr/stristr,
get_resource_type, la famiglia opendir/readdir/closedir/rewinddir e
fprintf/vfprintf, e ha corretto un panic latente (dir handle in un builtin di stream).
Hardening tooling (non-funzionale): depth-guard nell'evaluator (
MAX_CALL_DEPTH, converte la ricorsione runaway in unErrorcatchable invece di un SIGABRT del processo) + modalitàphpt-runner --isolate(ogni test in un sotto-processo: un crash è contenuto come un FAIL, non aborta il batch). Oracle ricompilato con--enable-mbstring→ sblocca la validazione dimb_*.
| Step | Contenuto | Stato |
|---|---|---|
| 0 | Scaffolding workspace + diary + Phase 0 reconnaissance | ✅ |
| 1 | php-types: PhpStr, Zval, PhpArray |
✅ |
| 2 | Operatori + conversioni (zend_operators.c) + differential 37.835 casi, 0 mismatch |
✅ |
| 3 | Bridge mago → HIR | ✅ |
| 4 | Evaluator (echo, variabili, controllo di flusso) | ✅ |
| 5 | Builtins nucleo + var_dump |
✅ |
| 7 | Array end-to-end + foreach / switch / match |
✅ |
| 6 | phpt-runner (capability scan + import testsuite) — 6172 file, 98.6% dei runnable |
✅ |
| 8 | Funzioni utente | ✅ |
| 9 | Rendering diagnostici (warning/fatal su stdout) | ✅ |
| 10 | Espansione builtin (count, array_*, implode/explode, substr/strpos/str_replace, sprintf/printf, abs/max/min, print_r) — baseline 126 → 135 pass | ✅ |
| 11 | Reference semantics — $b = &$a (11a), parametri f(&$x) (11b), builtin by-ref array_push/sort/array_pop/array_shift (11c), element-ref + foreach as &$v via Zval::Ref (11d) |
✅ |
| 12 | global $x + $GLOBALS['literal'] (frame overlay globale/locale) |
✅ |
| 13 | Return-by-reference function &f() |
✅ |
| 14 | Type-hint enforcement scalare weak (+ TypeError byte-esatto) |
✅ |
| 15 | Variabili static (persistenza cross-call) |
✅ |
| 16 | declare(strict_types=1) |
✅ |
| 17 | Espansione builtin per frequenza (case/build/trim/math/array — ~24 funzioni) | ✅ |
| 18 | Closures & callables — use, arrow fn, first-class f(...), array_map/filter/usort, costanti engine |
✅ |
| 19 | OOP/classi — new, $this, ereditarietà, visibility, static + LSB, interfacce, abstract, instanceof, __toString, closure bind, var_dump/print_r oggetti |
✅ |
| 20 | Eccezioni — try/catch/finally/throw + gerarchia Throwable (prelude PHP) |
✅ |
| 21 | Traits (flatten-at-lowering, insteadof/as, collisioni → Fatal) |
✅ |
| 22 | Magic methods — __get/__set/__isset/__unset/__call/__callStatic/__invoke |
✅ |
| 23 | Enum (pure + backed) — case singleton, from/tryFrom/cases, UnitEnum/BackedEnum |
✅ |
| 24 | Stringable auto-interface + __destruct (shutdown LIFO + sweep refcount-zero immediato) |
✅ |
| 25 | Interpolazione stringhe — "$x", "$a[k]", "$o->p", "{$expr}" |
✅ |
| 26 | json_encode / json_decode (assoc array + stdClass, flag PRETTY/UNESCAPED_*) |
✅ |
| 27 | Regex preg_* — match/match_all/replace/replace_callback/split/quote (crate regex) |
✅ |
| 28 | Stack-trace frame reali — getTrace/getTraceAsString + render uncaught con frame |
✅ |
| 29 | Builtin puri string+array + cast (object) |
✅ |
| 30 | Heredoc / nowdoc (lower_document) |
✅ |
| 31 | preg_* named groups + flag PREG_* (OFFSET_CAPTURE, SET_ORDER, UNMATCHED_AS_NULL, SPLIT_*) |
✅ |
| 32 | Array by-ref family — array_splice + array_walk |
✅ |
| 33 | Array key/assoc set-ops (array_diff_key/assoc, array_intersect_key/assoc) + array_column |
✅ |
| 34 | DateTime/date() — date/gmdate/mktime/checkdate/strtotime (subset) + OOP DateTime/DateTimeImmutable/DateInterval (format/modify/add/sub/diff/createFromFormat), scope UTC (crate time) |
✅ |
| 35 | API procedurale date — date_create/date_format/date_diff/date_add/date_sub/date_modify/date_*_set/date_create_from_format/date_interval_format/date_interval_create_from_date_string (funzioni globali del prelude) + getdate/localtime (builtin puri). Infra: il prelude ora trasporta anche le funzioni globali |
✅ |
| 36 | preg backref/lookaround — auto-fallback regex→fancy-regex (enum Engine): backref, lookaround, atomic/possessive, (?R)/conditional/\K/\G. Scope-out: subroutine (?1), control verb (*SKIP), callout. Hardening 36-3: backtrack_limit + stop-on-error (niente hang/panic su pattern patologici). Corpus ext/pcre 38→41 pass |
✅ |
| 37 | flag modificatori PCRE U (ungreedy, swap_greed+(?U)), A (anchored, wrap \A(?:…)), X (no-op PCRE2), D/$ (default $ zero-width prima di \n finale via lookahead (?=\n?\z)→fancy; D=\z stretto). Corpus ext/pcre 41→44 pass |
✅ |
| 38 | argomenti nominati f(c: 3, a: 1) per funzioni/costruttori/metodi/static (riordino, default saltati, named→by-ref, errori catchable, posizionale-dopo-nominato compile-fatal) + parametri variadic f(...$rest). nullsafe ?-> già dallo step 19. Follow-up: spread ...$arr, named→variadic |
✅ |
| 39 | generatori yield — esecuzione sospendibile via coroutine stackful corosensei (D-GEN-1). yield/yield $k=>$v/yield;/yield from (array+sub-generatore), send(), return+getReturn(), Iterator (current/key/next/valid/rewind), foreach su Generator, instanceof Generator/Iterator/Traversable, var_dump. Closure-generator. Corpus Zend/tests/generators 59/110. Scope-out (D-GEN-4): throw(), eccezioni/finally attraverso yield, yield by-ref |
✅ |
| 40 | argument unpacking / spread f(...$arr) per Call/New/MethodCall/StaticCall: chiavi int→posizionali (valore chiave ignorato), chiavi string→nominati, spread→variadic (re-keyed), Traversable/generator, TypeError su non-iterabile, compile-fatal posizionale-dopo-spread / spread-dopo-nominato. named→variadic (...$rest raccoglie i nominati senza match con chiave string, esplicita e da spread). Scope-out D-40.1: precedenza messaggio su input doppiamente-invalido |
✅ |
| 41 | mbstring batch 1 (UTF-8 code-point) — mb_strlen/mb_substr/mb_str_split, case (mb_strtoupper/mb_strtolower/mb_convert_case/mb_ucfirst/mb_lcfirst, full Unicode via std), ricerca (mb_strpos/stripos/strrpos/strripos/mb_strstr/stristr/strrchr/strrichr/mb_substr_count), mb_ord/mb_chr/mb_str_pad/mb_trim/ltrim/rtrim/mb_check_encoding. Builtin puri. Scope-out: encoding non-UTF-8 (serve encoding_rs), mb_ereg*, mb_convert_encoding/detect/strwidth |
✅ |
| 42 | mbstring batch 2A (encoding + width) — mb_convert_encoding/mb_detect_encoding via encoding_rs (UTF-8↔ISO-8859-1/Windows-1252/SJIS/EUC-JP/UTF-16; true Latin-1 e UTF-16 hand-rolled, substitute ?); mb_strwidth/mb_strimwidth/mb_strcut via tabella EAW portata da libmbfl. Builtin puri. Scope-out: mb_ereg*/mb_split (oniguruma → step 43), mb_list_encodings, width su encoding ≠ UTF-8 |
✅ |
| 43 | mbstring batch 2B (regex mb_ereg*) — adapter su oniguruma reale (crate onig): mb_ereg/mb_eregi ($regs by-ref), mb_ereg_replace/mb_eregi_replace/mb_ereg_replace_callback, mb_split, mb_ereg_match, mb_regex_encoding/set_options, e famiglia stateful mb_ereg_search_*. Default Ruby syntax + opzioni pr (classi POSIX, named group, backref). Primo step con stato persistente sull'Evaluator + higher-order builtins. Scope-out: encoding ≠ UTF-8 |
✅ |
| 44 | phpt-runner --EXTENSIONS-- relax + import corpus mbstring (Phase 4c) — gating selettivo (allowlist core/standard/mbstring/pcre/json/date) sblocca 163 test mbstring-only; run ext/mbstring/tests = 30 pass / 37 fail / 350 skip. 3 bug classe A fixati (offset out-of-range su mb_str(r)(i)pos, lista encoding vuota su mb_detect_encoding/mb_convert_encoding). 37 fail residui = scope-out dichiarati; 2 D-NEW (array input in mb_convert_encoding; titlecase digrammi in MB_CASE_TITLE) |
✅ |
| 45 | goto + label — ultima feature di control-flow. Flow::Goto + exec_stmts con indice (salto same-block / propagazione out-of-block, incl. uscita da loop/try+finally); validazione compile-time (undefined / dup label, into-loop/switch, into-finally) via stack di barriere. Corpus Zend/tests/*goto* = 5 pass / 5 skip (non-goto) / 0 fail. Scope-out D-45.1: salto dentro un blocco trasparente (raro, mai nel corpus). +2 fix di fedeltà al phpt-runner (strip backtrace con fatal_error_backtraces=Off; nome script = path .php reale) |
✅ |
| 46 | print + exit/die — costrutti di linguaggio. print = espressione (emette, ritorna 1); exit/die si propagano via Err(PhpError::Exit(u8)) (uncatchable, NON girano i finally), nuovo Outcome.exit_code. Coercion `string |
int $status: int/bool/float/null → exit code, string/__toString→ messaggio, array/oggetto non-stringabile →TypeError. Sblocca finally_goto_005+ testZend/tests/exit`. Scope-out D-46.1: Deprecated notice di coercion non emessi |
| 47 | var_export + reflection — var_export (port di php_var_export_ex: indent esatto, float con .0, stringhe single-quote + NUL via . "\0" ., (object) array/__set_state, modalità return, warning su ref circolari); get_class_methods/get_object_vars scope-aware (visibilità da cur_class, ereditarietà child→parent, metodi d'interfaccia via nuovo ClassDecl.abstract_methods). +14 test. Scope-out D-47.1/2: visibilità abstract protected, aliasing reference di get_object_vars |
✅ |
| 48 | micro-step runner breakdown + dynamic class refs + @ — (a) il phpt-runner riporta il costrutto specifico non supportato (expr:*/stmt:*) e i builtin mancanti (top 20), in-process e --isolate; (b) ClassRef::Dynamic per new $cls/$cls::m()/$cls::CONST/$obj::m()/$x instanceof $cls (stringa o oggetto → class id; non-forwarding); (c) operatore @ via suppress_depth (no-op di flush_diags + truncate dei diag; throwable NON soppressi). +9 test. Scope-out D-48.1 |
✅ |
| 49 | constant expressions — define()/const, costanti magiche (__LINE__/__FILE__/__DIR__/…), costanti named risolte in lowering; + hardening del phpt-runner |
✅ |
| 50 | serialize() / unserialize() — formato byte-exact (scalari, array, oggetti O:, ref), round-trip vs oracle |
✅ |
| 51 | fopen + sottosistema filesystem-stream — tipo Zval::Resource, php://memory/temp/std*, file reali r/w/a/x/c+; fread/fwrite/fgets/fseek/ftell/feof/fclose + file_get_contents/file_put_contents, byte-exact |
✅ |
| 52 | predicati/operazioni filesystem — file_exists/is_*/filetype, famiglia stat/lstat/fstat + accessor, mutatori unlink/mkdir/rename/copy/touch/symlink/chmod, scandir/glob/tempnam/tmpfile. ext/standard/tests/file 2 → 63 pass |
✅ |
| 53 | lever cheap dir file — strstr/strrchr/stristr, get_resource_type, famiglia opendir/readdir/closedir/rewinddir, fprintf/vfprintf + fix panic dir-handle |
✅ |
| 54 | engine di parsing — scanf (sscanf/fscanf, return-array + by-ref) e CSV (str_getcsv/fgetcsv/fputcsv) |
✅ |
| 55 | batch stream/file read (file/readfile/fpassthru/stream_get_contents/stream_copy_to_stream/ftruncate) + getenv/putenv + disk_*_space. file 71 → 86 pass |
✅ |
| 56 | batch funzioni stringa (bin2hex/hex2bin, addslashes/stripslashes, substr_replace, nl2br, wordwrap, htmlspecialchars/htmlentities+decode, vsprintf/vprintf). strings 51% runnable |
✅ |
| 57 | batch stringhe #2 (strrpos/stripos/strripos, strspn/strcspn, strtr byte-map+array, chunk_split, strip_tags, quotemeta, levenshtein) + fix strtr("",$map). strings 58% |
✅ |
| 58 | chiusura motore sprintf — fix crash capacity overflow (width/precision > INT_MAX → ValueError); sintassi * (PHP 8.4, da argomento, posizionale); %g/%G/%h/%H (port di php_gcvt, byte-exact su 24×9 casi). sprintf_star.phpt passa |
✅ |
| 59 | CLI phpr (era uno stub: ora esegue uno script, stream CLI-faithful, exit code fedele) + batch fedeltà sprintf/printf (modificatore l, specifier ignota/mancante → ValueError, errori catchable col tipo giusto, conteggio "N arguments required", warning Array-to-string, pad char left-justify). strings 242/393 (61.6%). Infra: target-dir cargo fuori dal volume esterno |
✅ |
| 60 | modularizzazione eval.rs (6.965 righe) → eval/{mod,expr,stmt,calls,class,builtins}.rs, ognuno un impl Evaluator. Refactor meccanico, zero cambi di comportamento; mod.rs −72% |
✅ |
| 61 | DevEx tooling (code-review esterna): diff unificato EXPECTF-aware nel phpt-runner (E), flag PHP_RUST_TRACE = dump HIR + trace d'esecuzione su stderr (B), 7 test unitari oracle-independent su php-types::ops (C); + split lower.rs (3.783) → lower/{mod,stmt,class,expr}.rs (−63%) |
✅ |
| 62 | famiglia hash/encoding (base64_encode/base64_decode, md5, sha1, crc32, hash) in encoding.rs — base64 port byte-exact di php_base64_decode_impl (strict/lenient, padding), digest via RustCrypto (md-5/sha1/sha2), CRC-32 zlib via crc32fast. base64/md5/sha1/crc32 .phpt tutti verdi |
✅ |
| 63 | pack/unpack in pack.rs — port fedele di ext/standard/pack.c (host little-endian): tutti i codici aAZ hH cC sSnv iI lLNV qQJP fgG deE xX@, ValueError/Warning fedeli, chiavi nominali in unpack. pack_*/unpack_* .phpt verdi, byte-identico all'oracle su sweep ampio (inclusi codici 64-bit) |
✅ |
| 64 | crypt in crypto.rs su pwhash (DES/BSDi/MD5/SHA-256/512/bcrypt) + dispatch e convenzione *0/*1 di php_crypt, costanti CRYPT_*, pre-check anti-hang su rounds=N. Dir crypt/ 4/4 + crypt/sha256/sha512/des verdi, byte-identico all'oracle; 3 D-NEW ($2x$, bcrypt 8-bit, salt md5 non-standard — limiti pwhash, casi deprecati) |
✅ |
| 65 | strtok (stateful → evaluator-dispatched): campo strtok_state sull'Evaluator, port fedele di PHP_FUNCTION(strtok). Forma a 2 arg (re)inizializza il cursore, 1 arg lo riprende, cleared a fine stringa. Tutti gli strtok_* .phpt verdi, byte-identico all'oracle |
✅ |
| 66 | fix interleaving diag/output dei builtin: in dispatch_value_builtin i diagnostici sollevati da un builtin che scrive su stdout (printf/vprintf) sono ora resi su rendered prima del suo output — il warning nasce durante la formattazione, prima della scrittura (es. Array to string conversion precede il risultato di printf, come PHP). Byte-identico all'oracle su casi multipli |
✅ |
| 67 | harness: materializza lo script su disco prima di eseguirlo (come run-tests.php) → __FILE__/fopen(__FILE__)/include __FILE__ risolvono contro un file reale. Guardato: crea solo se assente, rimuove solo ciò che ha creato (mai sovrascrive file companion). Toglie i warning fopen spuri da decine di test |
✅ |
| 68 | %s di sprintf/printf onora __toString: la famiglia sprintf/printf/vsprintf/vprintf/fprintf/vfprintf è ora evaluator-dispatched — gli argomenti oggetto sono risolti via __toString (o lanciano il fatale corretto se assente) prima del motore di formato puro, ricorsivamente negli array dei v*. strings 290→292 pass; 1 D-NEW (%d/%f su oggetto-con-__toString) |
✅ |
Lo step 6 è stato eseguito dopo lo step 7 (deciso con l'utente: gli array rendono il phpt-runner molto più utile, quintuplicando i test in-scope).
Risultato chiave (step 2): il porting di zend_operators.c — type juggling,
confronti PHP 8, formattazione float, increment Perl-style, bitwise su stringhe — è
verificato byte-per-byte contro un binario PHP 8.5.7 compilato dal sorgente, su
37.835 casi (47 valori × 47 × 17 operatori binari + 6 unari), diagnostica inclusa.
Risultato chiave (step 6): il phpt-runner esegue un capability scan della
testsuite ufficiale (tests/ + Zend/tests/, 6172 file): fa girare i test in-scope
e categorizza i fuori-scope come SKIP motivati (l'unico FAIL è una divergenza di
output reale). Baseline attuale: 71 pass / 1 fail / 6100 skip = 98.6% dei runnable.
L'import ha scoperto e fatto fixare 2 bug reali (?? su offset di stringa #69889,
literale intero gigante → INF #74947) e 1 divergenza ereditata da mago (\u{}).
| Sottosistema Zend | LOC C | Sostituto Rust | LOC Rust |
|---|---|---|---|
VM generata (zend_vm_execute.h) + zend_execute.c |
~146.000 | evaluator tree-walk su HIR | 3–5K |
zend_compile.c (AST→opcodes) |
12.400 | lowering AST→HIR | 1–2K |
| lexer re2c + parser Bison + AST | ~25.000 | dipendenza mago + bridge |
~500 |
zend_alloc / zend_gc / TSRM / Optimizer / opcache / win32 |
~88.000 | ownership, Rc+COW, Send/Sync, processo residente |
~0 |
zend_operators.c (type juggling) |
3.900 | full port fedele (l'anima di PHP) | ~1.500 |
~280K LOC del core → ~8–10K LOC Rust stimati.
php-rust/crates/
php-types Zval / PhpStr / PhpArray / Object + operatori (zero dep interne)
php-runtime HIR, lowering da mago, evaluator tree-walk (OOP, eccezioni,
enum, closure, __destruct, interpolazione; json_decode +
preg_* intercettati; stack-trace). lowering in
lower/{mod,stmt,class,expr}.rs; evaluator in
eval/{mod,expr,stmt,calls,class,builtins}.rs
php-builtins registry ~243 builtin (var_dump/print_r, array_*, string,
sprintf/printf, math, json_encode, file/stream, mbstring,
hash/encoding: base64/md5/sha1/crc32/hash, pack/unpack, crypt,
strtok, …)
php-cli binario `phpr` (CLI: esegue uno script, stream CLI-faithful, exit code)
phpt-runner runner .phpt + capability scan + diff unificato (bin + lib)
diary/ 00-reconnaissance … 99-conclusions + metrics
cd php-rust
cargo test # unit + integration
# differential vs oracle (richiede un binario php):
# build dal sorgente: ./configure --disable-all --enable-cli && make
PHP_ORACLE=/path/to/php cargo test -p php-types --test differentialIl differential si auto-salta con un messaggio se l'oracle non è disponibile; i
test unitari in php-types::ops coprono lo stesso nucleo type-juggling
senza bisogno dell'oracle.
phpr è un php drop-in: esegue uno script e scrive lo stream CLI-faithful
(output + diagnostics + fatal inline) con exit code fedele (exit/die, 255
su fatal, altrimenti 0). Utile anche come differential contro l'oracle.
cargo run -p php-cli -- script.phpPer capire se un .phpt fallisce in lowering o in evaluation, su stderr
(non inquina lo stdout confrontato), valido per phpr e per il runner:
PHP_RUST_TRACE=hir phpr s.php # dump dell'HIR abbassato (Program intero)
PHP_RUST_TRACE=body phpr s.php # solo la lista di statement top-level
PHP_RUST_TRACE=exec phpr s.php # traccia ogni statement eseguito, indentato per call-depth
PHP_RUST_TRACE=all phpr s.php # HIR + trace d'esecuzioneEsegue i .phpt ufficiali attraverso l'evaluator, con capability scan e
classificazione PASS/FAIL/SKIP (i fuori-scope sono SKIP motivati):
cargo run -p phpt-runner -- /path/to/php-src/tests /path/to/php-src/Zend/tests
cargo run -p phpt-runner -- --isolate <path> # ogni test in un sotto-processo (un crash = un FAIL)
cargo run -p phpt-runner -- --list-fails <path> # diff unificato (riga + contesto) per ogni failIl deliverable principale dell'esperimento è il diario metodologico in diary/,
non solo il codice: decisioni (02-mapping-table.md), log per step
(03-translation-log.md), divergenze trovate, conclusioni.
Generato con assistenza AI (Claude Fable 5 / Opus 4.8). Codice e commenti tecnici in inglese, diario in italiano.