chore(phpstan): make the shared base PHPStan-2 ready (unblocks 9 fleet dependabot PRs) - #517
Merged
Merged
Conversation
Nine dependabot PRs bumping phpstan/phpstan 1.12.33 -> 2.x are red across the fleet, all on the same two migration-level classes rather than on real defects. Measured on decidesk with phpstan 2.2.8: 60 errors, of which 17 phpDoc.parseError @SuppressWarnings((PHPMD.UnusedFormalParameter) …) 21 alreadyNarrowedType / defensive checks against a type known only identical.alwaysTrue from a docblock Both are addressed in the shared base so the fix lands once, not eighteen times: treatPhpDocTypesAsCertain: false restores 1.x behaviour. The guards it flags cross an app boundary (OpenRegister contracts) where the docblock is the only contract; deleting them to satisfy a stricter reading of an unenforced type would remove real runtime protection. ignoreErrors phpDoc.parseError @SuppressWarnings is PHPMD's annotation and is read by the phpmd gate. 2.x parses it as its own tag and fails on PHPMD's syntax. decidesk: 60 -> 22 errors. The residual 22 are genuine per-app debt (throws.unusedType, parameterByRef.type, argument.type) and are NOT masked here. Verified on BOTH majors, since 17 of 18 apps still run 1.12.33: phpstan 2.2.8 60 -> 22 errors phpstan 1.12.33 exit 0, '[OK] No errors', no configuration error and still 'Found 1 error' on a deliberately broken file — so it is analysing, not silently skipping (the failure mode this file's own header documents).
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Nine dependabot PRs bumping
phpstan/phpstan1.12.33 → 2.x are red across the fleet — decidesk#526, doriath#259, hermiq#363, larpingapp#342, openbuild#236, pipelinq#1171, portaliq#158, scholiq#430, shillinq#889. None of them fail on a real defect. They fail on two migration-level behaviour changes, so the fix belongs in the shared base once rather than in eighteen app configs.Measured on decidesk with phpstan 2.2.8 — 60 errors:
phpDoc.parseError@SuppressWarnings((PHPMD.UnusedFormalParameter) …)function.alreadyNarrowedTypeidentical.alwaysTruethrows.unusedTypeWhat this changes
treatPhpDocTypesAsCertain: false— 2.x narrows types declared only in a docblock, then reports every defensive check against them as already-decided. The flagged guards cross an app boundary (OpenRegister contracts) where the docblock is the only contract there is. Deleting them to satisfy a stricter reading of an unenforced type would remove real runtime protection, so the setting restores 1.x behaviour instead. No effect on 1.x, which does not narrow from PHPDoc at all.ignoreErrors: phpDoc.parseErrorfor@SuppressWarnings— that tag is PHPMD's, and the phpmd gate reads it. 2.x began parsing it as its own tag and fails on PHPMD's rule syntax (Unexpected token ".UnusedFormalParameter)", expected ")"). This is PHPStan commenting on a neighbour's syntax, not a defect.Result on decidesk: 60 → 22 errors. The residual 22 are genuine per-app debt (
throws.unusedType,parameterByRef.type,argument.type) and are deliberately not masked.Verification on both majors
17 of 18 apps still run 1.12.33, so a 2.x-only config key here would break the fleet. Tested against decidesk:
[OK] No errors, no configuration errorAnd a positive control on 1.12.33 — a deliberately broken file still produced
Found 1 error, confirming it is analysing rather than silently skipping. That "analysed ZERO files" failure mode is the one this file's own header documents from v1.7.0/v1.7.1, so it was checked explicitly rather than inferred from a green exit code.Not merged
Gates ship from
.github@mainto all 18 apps the moment this lands, so I have left it for a human. It is not covered by the app-leveldevelopmentmerge authorisation.