php-ci/phpstan: pass php-version to PHPStan instead of only to the runtime - #54
Open
mattgoud wants to merge 1 commit into
Open
php-ci/phpstan: pass php-version to PHPStan instead of only to the runtime#54mattgoud wants to merge 1 commit into
mattgoud wants to merge 1 commit into
Conversation
The action fed its php-version input to shivammathur/setup-php and to nothing else. PHPStan never received it, so it took the PHP language level it analyses with from the analysed project's composer platform declaration, which meant every entry of a module matrix analysed the same level whatever PHP binary it ran on. Derive the level from the input and emit it as its own neon include. The include is placed before the module config so a module that deliberately pins phpVersion keeps the final word: neon resolves a key from the last include that defines it. The config assembly is reworked into an includes list so the injected file reaches all three paths, with or without a module config, merging enabled or disabled.
jolelievre
approved these changes
Sep 9, 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.
phpVersionfrom the action'sphp-versioninput and emit it as a neon include, so the input actually reaches PHPStan instead of only configuring the runtime.php-versionvalues. The job log must print✓ Analysing at PHP language level <id>matching the input, and the generated config must listphpstan-php-version.neon. Before this change the two runs produce byte-identical findings.The problem
php-versionwent toshivammathur/setup-phpand nowhere else. PHPStan never received it, so it took the language level it parses and analyses with from the analysed project's composer platform declaration. The matrix dimension has therefore never influenced the analysis: every entry analysed the same level, on a different binary.Measured on a PrestaShop 9.2.0 core with
ps_emailsubscription, reproducing the action's own steps at level 5. Running on the PHP 8.3.6 binary, PHPStan still reported:That error only exists below PHP 8.0: the class uses constructor property promotion,
readonlyand union types, so PHPStan could not parse it and treated it as unknown. The module declaresconfig.platform.php: 7.1.0, and that is the level that was used.That mismatch is not academic. It is why a cast helper was added to three native modules to silence the unknown-class error, and in
ps_wirepaymentthat helper carried a native return type and produced a fatal error on the configuration page of every 9.x shop with all fourteen static jobs green. See PrestaShop/ps_wirepayment#103.The change
8.1becomes80100, written tophpstan-php-version.neonand added to the includes list.Placement is deliberate. Neon resolves a key from the last include that defines it, so the file goes before the module config and the module keeps the final word. Measured, using the presence of the unknown-class error as the oracle:
So a module that deliberately pins
phpVersionkeeps working, and no extra opt-out input is needed. That answers the open question in #53.The config assembly is reworked into an includes list so the injected file reaches all three paths: no module config, module config merged, and module config with
phpstan-config-merge: falsewhere the module already includesps-module-extension.neonitself.Verified
Replaying the patched shell against a real module, oracle being the unknown-class error count:
php-version: 8.1, module has no pin80100php-version: 7.2, module has no pin70200php-version: 8.5, module has no pin80500php-version: 8.1, module pins7010080100injectedphpstan-configat all80100ps-module-extension.neonthenphpstan-php-version.neonThe second row is the point of the change: before it, that run was indistinguishable from the first.
Expect new findings, and that is the fix working
Modules are currently analysed at their composer platform level, typically 7.1 or 7.2.5, while their 9.x jobs claim to run 8.1 to 8.5. Once the level is real, core classes that were unparseable become parseable, PHPStan gains type information it did not have, and some modules will surface findings that were silently skipped. Those jobs were green on a false premise.
Worth landing with that in mind rather than as a silent change. If a staged rollout is preferred, an input defaulting to the current behaviour would do it, but that keeps the wrong default in place, so I have not proposed it.
Not included: the version range
PHPStan supports
phpVersionas a{min, max}range, which would cover the whole supported interval in one run and would allow shrinking the matrices. It is not usable across the board yet:The action uses the PHPStan shipped by the analysed core when one is present:
^1.9.3for8.2.x,9.0.3,9.1.5and9.2.x,^2.2only fordevelop. Worth revisiting once the 9.x branches move to PHPStan 2.Related
develop.