Tipi rigorosi, ovunque
Spec: ISO 32000-2, §7.5.5 ISO 32000-2 §7.5.5 Evidence: Code-backed PHPStan: Level 10, no src baseline
In sintesi
Sezione intitolata “In sintesi”NextPDF esegue PHPStan a Level 10 sul sorgente del motore senza alcuna baseline di soppressione. Questa pagina spiega perché «nessuna baseline» sia una decisione di progettazione anziché un dettaglio di tooling, e che cosa quel rigore offra in concreto a un flusso il cui compito essenziale è non gestire mai i dati in modo errato.
Perché è importante
Sezione intitolata “Perché è importante”Nella maggior parte delle applicazioni, la tipizzazione rigorosa è igiene del codice. In un motore PDF è più vicina a un meccanismo di correttezza. Il formato non perdona. Ci si aspetta che un reader individui i contenuti leggendo il file dalla fine, attraverso il trailer e la tabella di riferimenti incrociati; per questo gli offset di byte di un writer devono essere esatti. Si consideri un tipo che si allarga in sordina a mixed, un int che diventa silenziosamente una string, oppure un valore nullable dereferenziato senza controllo. Ciascuno di questi casi può produrre un file che si apre senza problemi in un visualizzatore e non supera la validazione in un altro, settimane dopo, senza alcuno stack trace che riporti alla causa.
In questo dominio i guasti costosi sono quelli silenziosi. La tipizzazione rigorosa, unita a un analizzatore rigoroso, è il modo in cui il motore trasforma una classe di guasti silenziosi a runtime in fallimenti espliciti in fase di build.
In breve
Sezione intitolata “In breve”- Il sorgente del motore è analizzato a PHPStan Level 10 — il livello più rigoroso — verificato in
phpstan.neon.dist. - Non esiste alcuna baseline di soppressione sul sorgente. La configurazione impone l’analisi del sorgente a zero errori. Una regressione fa fallire la build anziché essere assorbita in un file di ignore in continua crescita.
- Le poche voci
ignoreErrorsesistenti sono ristrette, circoscritte per identificatore e per percorso e giustificate individualmente nella configurazione (confini di dipendenza debole tra package e giunzioni di test con target di reflection) — non costituiscono una baseline di massa. - Un profilo strict separato esegue
level: maxe vieta qualsiasi nuova voce di ignore, così il nuovo codice è tenuto a un vincolo ancora più stretto. - L’effetto voluto è una pressione progettuale: il codice che non può essere espresso in modo onesto tramite i tipi non passa, quindi viene riprogettato anziché soppresso.
Come NextPDF lo affronta
Sezione intitolata “Come NextPDF lo affronta”La differenza tra «usiamo un analizzatore rigoroso» e «usiamo un analizzatore rigoroso senza baseline» è il punto essenziale, quindi vale la pena essere precisi.
Una baseline registra ogni violazione esistente e indica all’analizzatore di ignorare esattamente quelle. È un modo pragmatico per adottare l’analisi statica su una codebase legacy, ma ha un costo. La baseline diventa un registro silenzioso di debito che il sistema dei tipi ha accettato di non esaminare. Nuove violazioni dello stesso tipo possono insinuarsi accanto a quelle ereditate. La promessa dell’analizzatore si indebolisce, passando da «questo codice è pulito sui tipi» a «questo codice non è peggio di prima».
NextPDF non accetta quel compromesso per il sorgente del motore. La configurazione impone l’analisi del sorgente a zero errori e attiva reportUnmatchedIgnoredErrors, così persino una soppressione obsoleta — una che non corrisponde più a nulla — fa fallire la build. Le voci di ignore ristrette che rimangono sono circoscritte a uno specifico identificatore di errore e a un file. Ognuna riporta una spiegazione inline del perché il confine sia intenzionale (per esempio, il core che programma verso un’interfaccia Pro/Enterprise da cui deliberatamente non dipende in modo concreto). Un revisore può leggerle una per una e valutarle. Non esiste alcun elenco opaco di cui perdere il filo.
Il flusso che mantiene verificabile questa disciplina:
- Change proposed New or modified engine code.
- Level 10 analysis Strictest PHPStan level over src/, treatPhpDocTypesAsCertain on.
- Zero-error gate No source baseline; unmatched ignores also fail.
- Strict profile level: max; no new ignore entries permitted.
- Redesign, not suppress If it cannot be expressed honestly, the design changes.
treatPhpDocTypesAsCertain fa parte di questo impianto. Le annotazioni PHPDoc sono trattate come fonte di verità, quindi un @param list<T> o un @return non-empty-string non è un commento che l’analizzatore può ignorare con discrezione. È una promessa verificata. Annotazione e tipo a runtime sono costretti ad allinearsi.
Cosa dicono le evidenze
Sezione intitolata “Cosa dicono le evidenze”Questa pagina è Evidence: Code-backed . La configurazione è l’evidenza:
phpstan.neon.distimpostalevel: 10,phpVersion: 80400, analizzasrce non contiene alcuna chiavebaseline:— non esiste alcunphpstan-baseline.neonper l’analisi del sorgente.- Lo stesso file imposta
treatPhpDocTypesAsCertain: trueereportUnmatchedIgnoredErrors: true, con una nota inline secondo cui l’analisi del sorgente L10 è bloccata a zero errori e qualsiasi regressione deve far fallire la CI. - Gli
ignoreErrorsrimanenti sono ciascuno circoscritto peridentifiere spesso perpath, con commenti che spiegano la motivazione della dipendenza debole e del target di reflection — non sono una baseline generata in massa. phpstan-strict.neon.disteredita quella configurazione, porta il livello amaxe congela l’elenco degli ignore, così nessuna nuova voce può essere aggiunta sotto il profilo strict.
Il versante normativo è diretto. Il motore deve produrre file che un reader possa navigare a partire dal trailer e dalla tabella di riferimenti incrociati secondo Spec: ISO 32000-2, §7.5.5 ISO 32000-2 §7.5.5 . Gli offset di byte esatti sono un problema di tipizzazione prima di essere un problema di serializzazione. Un offset è un intero che non deve mai diventare silenziosamente qualcos’altro. Un flusso che è pulito sui tipi a Level 10 ha già eliminato gran parte dei modi in cui l’aritmetica può andare storta in sordina.
Esempio pratico
Sezione intitolata “Esempio pratico”La tipizzazione rigorosa è più visibile quando una regola di dominio è codificata come tipo anziché come controllo a runtime. Il discriminatore di conformità risponde alle domande a livello di specifica con un match esaustivo, così un caso non gestito è un errore di tipo, non un PDF errato:
declare(strict_types=1);
enum ConformanceMode: string{ case Plain = 'plain'; case PdfUa2 = 'pdfua2'; case PdfA4 = 'pdfa4';
/** @return 2|3|4|null */ public function pdfaPart(): ?int { return match ($this) { self::PdfA4 => 4, default => null, }; }}Il @return 2|3|4|null non è semplice documentazione. Sotto
treatPhpDocTypesAsCertain viene verificato. Un chiamante che presume che il risultato sia sempre un int viene avvisato in fase di analisi, prima ancora che venga scritto un singolo byte di un numero di parte PDF/A non conforme.
Equivoco comune
Sezione intitolata “Equivoco comune”La trappola è leggere «nessuna baseline» come «il codice casualmente non contiene violazioni». È il contrario. L’assenza di una baseline è la causa, non un esito fortunato. Poiché non c’è alcun posto dove parcheggiare una violazione, il codice che ne produrrebbe una deve essere scritto in modo diverso. Level 10 senza baseline sul sorgente è un vincolo che plasma la progettazione, non una valutazione che la descrive a posteriori.
Un secondo equivoco è pensare che la manciata di voci ignoreErrors sia una baseline con un altro nome. Non lo sono. Una baseline è generata in massa e opaca. Queste voci sono scritte individualmente, circoscritte per identificatore, spiegate e protette da reportUnmatchedIgnoredErrors, così non possono diventare obsolete senza essere notate.
Limiti e confini
Sezione intitolata “Limiti e confini”Questa pagina riguarda l’analisi del sorgente del motore. La test suite è analizzata in uno scope e con una configurazione separati e deliberatamente distinti; «nessuna baseline» qui è un’affermazione su src/, non un’asserzione che ogni analisi ausiliaria nel repository sia priva di baseline. PHPStan dimostra la solidità dei tipi, non la correttezza comportamentale. Non sostituisce la piramide di test, elimina soltanto una categoria di guasti che altrimenti i test dovrebbero inseguire. Il livello esatto, i flag e l’insieme di ignore sono accurati alla data di revisione di questa pagina. Le fonti autorevoli sono sempre phpstan.neon.dist e phpstan-strict.neon.dist nel repository core.
L’edizione non cambia questa disciplina. Ogni edizione è costruita dallo stesso sorgente Level 10:
| Edition | Availability |
|---|---|
| Core | Il sorgente Core è analizzato a Level 10 senza baseline sul sorgente. |
| Pro | Pro è costruito sulla stessa disciplina del sorgente Level 10. |
| Enterprise | Enterprise è costruito sulla stessa disciplina del sorgente Level 10. |
Documentazione correlata
Sezione intitolata “Documentazione correlata”- I fondamenti di PHP 8.4 — le funzionalità del linguaggio su cui si basa il sistema dei tipi.
- Gli errori come funzionalità — che cosa accade ai guasti che la tipizzazione rigorosa fa emergere.
- Il modello a pipeline — l’architettura che questa disciplina protegge.
Glossario
Sezione intitolata “Glossario”- PHPStan Level 10 — il livello di analisi più rigoroso, che tratta i valori non tipizzati e debolmente tipizzati come errori anziché come avvisi.
- Baseline — un record generato delle violazioni esistenti che si chiede all’analizzatore di ignorare. NextPDF non ne usa alcuna per il sorgente del motore.
treatPhpDocTypesAsCertain— un’impostazione di PHPStan che tratta le annotazioni di tipo PHPDoc come fatti verificati, non come note consultive.reportUnmatchedIgnoredErrors— un’impostazione che fa fallire la build quando una voce di ignore non corrisponde più a nulla, evitando soppressioni obsolete.- Pressione progettuale — l’effetto di un vincolo che obbliga a scrivere il codice in un determinato modo, in contrapposizione a un controllo che si limita a misurarlo.