Skip to content

php-ci/phpstan: pass php-version to PHPStan instead of only to the runtime - #54

Open
mattgoud wants to merge 1 commit into
PrestaShop:masterfrom
mattgoud:fix/phpstan-action-php-version
Open

php-ci/phpstan: pass php-version to PHPStan instead of only to the runtime#54
mattgoud wants to merge 1 commit into
PrestaShop:masterfrom
mattgoud:fix/phpstan-action-php-version

Conversation

@mattgoud

@mattgoud mattgoud commented Sep 9, 2026

Copy link
Copy Markdown
Contributor
Questions Answers
Description? Derive the PHPStan phpVersion from the action's php-version input and emit it as a neon include, so the input actually reaches PHPStan instead of only configuring the runtime.
Type? bug fix
BC breaks? no for the API, but see Expect new findings below
Deprecations? no
Fixed ticket? Fixes #53
How to test? Run any module workflow using this action twice with different php-version values. The job log must print ✓ Analysing at PHP language level <id> matching the input, and the generated config must list phpstan-php-version.neon. Before this change the two runs produce byte-identical findings.
Sponsor company @PrestaShopCorp

The problem

php-version went to shivammathur/setup-php and 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:

Call to method getLanguages() on an unknown class
PrestaShop\PrestaShop\Core\Context\LegacyControllerContext.

That error only exists below PHP 8.0: the class uses constructor property promotion, readonly and union types, so PHPStan could not parse it and treated it as unknown. The module declares config.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_wirepayment that 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.1 becomes 80100, written to phpstan-php-version.neon and 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:

Where the injected value sits Which value wins
inline in the generated config the injection, overriding the module
include before the module config the module
include after the module config the injection

So a module that deliberately pins phpVersion keeps 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: false where the module already includes ps-module-extension.neon itself.

Verified

Replaying the patched shell against a real module, oracle being the unknown-class error count:

Inputs Level used Result
php-version: 8.1, module has no pin 80100 0 errors
php-version: 7.2, module has no pin 70200 3 errors
php-version: 8.5, module has no pin 80500 0 errors
php-version: 8.1, module pins 70100 80100 injected 3 errors, the module pin wins
no phpstan-config at all 80100 config lists ps-module-extension.neon then phpstan-php-version.neon

The 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 phpVersion as 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:

phpVersion: {min: 80100, max: 80500}
  -> Invalid configuration: 'parameters › phpVersion' expects to be int|null, array given

The action uses the PHPStan shipped by the analysed core when one is present: ^1.9.3 for 8.2.x, 9.0.3, 9.1.5 and 9.2.x, ^2.2 only for develop. Worth revisiting once the 9.x branches move to PHPStan 2.

Related

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.
@github-project-automation github-project-automation Bot moved this to Ready for review in PR Dashboard Sep 9, 2026
@ps-jarvis ps-jarvis added the Waiting for QA Status: Action required, Waiting for test feedback label Sep 9, 2026
@ps-jarvis ps-jarvis moved this from Ready for review to To be tested in PR Dashboard Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Waiting for QA Status: Action required, Waiting for test feedback

Projects

Status: To be tested

Development

Successfully merging this pull request may close these issues.

php-ci/phpstan action: php-version never reaches PHPStan, so the matrix analyses every entry at the composer platform level

3 participants