Ga naar inhoud

Strikte typering, overal

Spec: ISO 32000-2, §7.5.5 Evidence: Code-backed PHPStan: Level 10, no src baseline

NextPDF draait PHPStan op Level 10 over de engine-broncode zonder enige onderdrukkingsbaseline. Deze pagina legt uit waarom “geen baseline” een ontwerpbeslissing is en geen tooldetail, en wat die striktheid daadwerkelijk oplevert voor een pijplijn die gegevens niet stilzwijgend verkeerd mag verwerken.

In de meeste toepassingen is strikte typering vooral hygiëne. In een PDF-engine ligt het dichter bij een correctheidsmechanisme. Het formaat is meedogenloos. Van een lezer wordt verwacht dat die inhoud vindt door het bestand vanaf het einde te lezen, via de trailer en de cross-reference table; daardoor moeten de byte-offsets aan de schrijverskant exact zijn. Denk aan een type dat stilzwijgend verbreedt naar mixed, een int die stilzwijgend een string wordt, of een nullable waarde die ongecontroleerd wordt gedereferentieerd. Elk van die gevallen kan een bestand opleveren dat in de ene viewer probleemloos opent en in een andere niet door de validatie komt, weken later, zonder stack trace die naar de oorzaak verwijst.

De duurste fouten in dit domein zijn de stilzwijgende. Strikte typering plus een strikte analyzer is de manier waarop de engine een categorie stilzwijgende runtimefouten omzet in zichtbare fouten tijdens de build.

  • De engine-broncode wordt geanalyseerd op PHPStan Level 10 — het strengste niveau — geverifieerd in phpstan.neon.dist.
  • Er is geen onderdrukkingsbaseline voor de broncode. De configuratie vergrendelt de broncode-analyse op nul fouten. Een regressie laat de build mislukken in plaats van in een groeiend negeerbestand te belanden.
  • De paar ignoreErrors-vermeldingen die bestaan zijn nauw afgebakend op identifier en pad, en afzonderlijk gemotiveerd in de configuratie (grenzen voor zachte afhankelijkheden tussen packages en reflection-target-testnaden) — geen bulk-baseline.
  • Een apart strikt profiel draait level: max en verbiedt elke nieuwe negeervermelding, zodat nieuwe code aan een nog strengere norm wordt gehouden.
  • Het beoogde effect is ontwerpdruk: code die niet type-eerlijk kan worden uitgedrukt, komt niet door en wordt daarom opnieuw ontworpen in plaats van onderdrukt.

Het verschil tussen “we gebruiken een strikte analyzer” en “we gebruiken een strikte analyzer zonder baseline” is precies waar het om draait, dus het is de moeite waard om nauwkeurig te zijn.

Een baseline legt elke bestaande overtreding vast en draagt de analyzer op om precies die te negeren. Het is een pragmatische manier om statische analyse in te voeren op een legacy-codebase, maar het heeft een prijs. De baseline wordt een stil schuldgrootboek waar het typesysteem niet meer naar kijkt. Nieuwe overtredingen van dezelfde soort kunnen ongemerkt naast bestaande binnensluipen. De belofte van de analyzer verzwakt van “deze code is type-schoon” naar “deze code is niet slechter dan die was.”

NextPDF maakt die afweging niet voor de engine-broncode. De configuratie vergrendelt de broncode-analyse op nul fouten en schakelt reportUnmatchedIgnoredErrors in, zodat zelfs een verouderde onderdrukking — een die nergens meer op aansluit — de build laat mislukken. De beperkte negeervermeldingen die overblijven zijn afgebakend tot een specifieke fout-identifier en een specifiek bestand. Elk ervan bevat een inline-toelichting waarom de grens opzettelijk is (bijvoorbeeld de core die programmeert tegen een Pro/Enterprise-interface waarvan ze bewust niet concreet afhankelijk wil zijn). Een reviewer kan ze stuk voor stuk lezen en beoordelen. Er is geen ondoorzichtige lijst waardoor je het overzicht kwijtraakt.

De flow die dit eerlijk houdt:

  1. Change proposed New or modified engine code.
  2. Level 10 analysis Strictest PHPStan level over src/, treatPhpDocTypesAsCertain on.
  3. Zero-error gate No source baseline; unmatched ignores also fail.
  4. Strict profile level: max; no new ignore entries permitted.
  5. Redesign, not suppress If it cannot be expressed honestly, the design changes.
Hoe een wijziging de engine-broncode bereikt: een type-oneerlijke wijziging kan de poort niet passeren en wordt daarom opnieuw ontworpen in plaats van onderdrukt.

treatPhpDocTypesAsCertain maakt hier deel van uit. PHPDoc-annotaties worden behandeld als bron van waarheid, dus een @param list<T> of @return non-empty-string is geen commentaar dat de analyzer beleefd negeert. Het is een gecontroleerde belofte. Annotatie en runtimetype moeten overeenkomen.

Deze pagina is Evidence: Code-backed . De configuratie is het bewijs:

  • phpstan.neon.dist stelt level: 10 en phpVersion: 80400 in, analyseert src en bevat geen baseline:-sleutel — er is geen phpstan-baseline.neon voor de broncode-analyse.
  • Hetzelfde bestand stelt treatPhpDocTypesAsCertain: true en reportUnmatchedIgnoredErrors: true in, met een inline-opmerking dat de L10-broncode-analyse op nul fouten is vergrendeld en dat elke regressie CI moet laten mislukken.
  • De overige ignoreErrors zijn elk afgebakend per identifier en vaak per path, met commentaar dat de motivatie voor de zachte afhankelijkheidsgrens en het reflection-target toelicht — het is geen in bulk gegenereerde baseline.
  • phpstan-strict.neon.dist erft die configuratie, verhoogt het niveau naar max en bevriest de negeerlijst zodat onder het strikte profiel geen nieuwe vermelding mag worden toegevoegd.

Vanuit de standaarden is de koppeling direct. De engine moet bestanden produceren waarin een lezer vanaf de trailer en de cross-reference table kan navigeren volgens Spec: ISO 32000-2, §7.5.5 . Exacte byte-offsets zijn een typeprobleem voordat ze een serialisatieprobleem zijn. Een offset is een integer die nooit stilzwijgend iets anders mag worden. Een pijplijn die op Level 10 type-schoon is, heeft al de meeste manieren weggenomen waarop rekenwerk stilletjes mis kan gaan.

Strikte typering is het meest zichtbaar waar een domeinregel als type wordt gecodeerd in plaats van als runtimecontrole. De conformiteitsdiscriminator beantwoordt vragen op specificatieniveau met een uitputtende match, zodat een niet-afgehandeld geval een typefout is en geen verkeerde PDF:

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,
};
}
}

De @return 2|3|4|null is geen documentatie. Onder treatPhpDocTypesAsCertain wordt deze gecontroleerd. Een aanroeper die aanneemt dat het resultaat altijd een int is, krijgt dat tijdens de analyse te horen, nog voordat ook maar één byte van een niet-conform PDF/A-onderdeelnummer wordt geschreven.

De valkuil is om “geen baseline” te lezen als “de code heeft toevallig geen overtredingen.” Dat is omgekeerd. De afwezigheid van een baseline is de oorzaak, geen toevallige uitkomst. Omdat er nergens een overtreding kan worden geparkeerd, moet code die er een zou veroorzaken anders worden geschreven. Level 10 zonder source-baseline is een beperking die het ontwerp vormgeeft, geen rapport achteraf dat het beschrijft.

Een tweede misverstand: dat de handvol ignoreErrors-vermeldingen een baseline onder een andere naam zijn. Dat zijn ze niet. Een baseline wordt in bulk gegenereerd en is ondoorzichtig. Deze vermeldingen zijn afzonderlijk geschreven, op identifier afgebakend, toegelicht en beschermd door reportUnmatchedIgnoredErrors, zodat ze niet ongemerkt kunnen verouderen.

Deze pagina gaat over de analyse van de engine-broncode. De testsuite wordt geanalyseerd binnen een afzonderlijke, bewust gescheiden scope en configuratie; “geen baseline” is hier een uitspraak over src/, geen bewering dat elke aanvullende analyse in de repository baseline-vrij is. PHPStan bewijst typecorrectheid, geen gedragscorrectheid. Het vervangt de testpiramide niet, maar verwijdert alleen een categorie fouten die tests anders zouden moeten opsporen. Het exacte niveau, de flags en de negeerset kloppen op de reviewdatum van deze pagina. De gezaghebbende bron is altijd phpstan.neon.dist en phpstan-strict.neon.dist in de core-repository.

De editie verandert deze discipline niet. Elke editie wordt gebouwd vanuit dezelfde Level 10-broncode:

Level 10 source analysis — edition availability
Edition Availability
Core De Core-broncode wordt geanalyseerd op Level 10 zonder source-baseline.
Pro Pro volgt dezelfde Level 10-discipline voor de broncode.
Enterprise Enterprise volgt dezelfde Level 10-discipline voor de broncode.
  • PHPStan Level 10 — het strengste analyseniveau, dat niet-getypeerde en losjes getypeerde waarden als fouten behandelt in plaats van als waarschuwingen.
  • Baseline — een gegenereerd register van bestaande overtredingen die de analyzer opdracht krijgt te negeren. NextPDF gebruikt er geen voor de engine-broncode.
  • treatPhpDocTypesAsCertain — een PHPStan-instelling die PHPDoc-type-annotaties behandelt als gecontroleerde feiten, niet als adviserend commentaar.
  • reportUnmatchedIgnoredErrors — een instelling die de build laat mislukken wanneer een negeervermelding nergens meer op aansluit, waardoor verouderde onderdrukkingen worden voorkomen.
  • Ontwerpdruk — het effect van een beperking die afdwingt dat code op een bepaalde manier wordt geschreven, in plaats van een controle die dat alleen meet.