Summary
.github/actions/php-ci/phpstan/action.yml passes its php-version input to shivammathur/setup-php, and nowhere else. PHPStan never receives it, so it takes the PHP language level it analyses with from the analysed project's composer platform declaration instead.
The consequence is that the php_version dimension of every module matrix has no effect on the analysis. All entries analyse the same code at the same language level, whatever PHP binary they run on.
Evidence
Measured against a PrestaShop 9.2.0 core with ps_emailsubscription, reproducing the action's own steps (ps-module-extension.neon merged with the module config, level 5).
The analysis ran on the PHP 8.3.6 binary and still reported:
Call to method getLanguages() on an unknown class
PrestaShop\PrestaShop\Core\Context\LegacyControllerContext.
That error can only occur below PHP 8.0: LegacyControllerContext uses constructor property promotion, readonly and union types, so PHPStan cannot parse it and treats it as an unknown class. The module declares config.platform.php: 7.1.0, and that is the level PHPStan used, not the 8.3 it was executing on.
Same file, same core, only the neon changed:
| Configuration |
Result |
no phpVersion |
3 errors, unknown class |
phpVersion: 80100 |
no errors |
So the level is entirely decided by the config and the composer platform, never by php-version.
Why it matters
This is not theoretical. A cast helper was added to three native modules purely to silence that unknown-class error, which is a symptom of the wrong analysis level rather than a code problem. In ps_wirepayment the helper carried a native : AdminController return type and produced a fatal error on the configuration page of every PrestaShop 9.x shop, with all fourteen static jobs green. See PrestaShop/ps_wirepayment#103.
Modules are currently working around this one by one, by pinning phpVersion in their own 9.x neon files:
Those are interim fixes for something that belongs in the shared action.
Proposal
The action already builds phpstan-temp.neon on the fly. Deriving phpVersion from the php-version input and appending it there fixes every module at once, and makes the matrix mean what it appears to mean:
# php-version "8.1" -> 80100
PHP_VERSION_ID=$(echo "${{ inputs.php-version }}" | awk -F. '{ printf "%d%02d00", $1, $2 }')
{
echo "parameters:"
echo " phpVersion: $PHP_VERSION_ID"
} >> "$TEMP_CONFIG"
Two things to settle before implementing:
- Collision with module-level pins. Several modules now set
phpVersion in their own neon, which the temp config includes alongside the injected value. Whether the include wins, the inline value wins, or neon raises a duplicate-key error needs checking, and the module pins should be removed once the action handles it.
- An opt-out. An input such as
phpstan-php-version defaulting to the php-version value, so a module can still pin a different analysis level deliberately.
Not usable yet: the version range
PHPStan supports phpVersion as a {min, max} range, which would let one run cover the whole supported interval and would allow shrinking the matrix. It is not available across the board today:
phpVersion: {min: 80100, max: 80500}
-> Invalid configuration: 'parameters › phpVersion' expects to be int|null, array given
| Core analysed |
PHPStan shipped |
8.2.x, 9.0.3, 9.1.5, 9.2.x |
^1.9.3, resolves to 1.12.x |
develop |
^2.2 |
The action takes PHPStan from the analysed core when one is present, so the range only works on the develop entry for now. Worth revisiting when the 9.x branches move to PHPStan 2.
Related
Modules cleanup epic PrestaShop/PrestaShop#41648, which is the campaign that surfaced this.
Summary
.github/actions/php-ci/phpstan/action.ymlpasses itsphp-versioninput toshivammathur/setup-php, and nowhere else. PHPStan never receives it, so it takes the PHP language level it analyses with from the analysed project's composer platform declaration instead.The consequence is that the
php_versiondimension of every module matrix has no effect on the analysis. All entries analyse the same code at the same language level, whatever PHP binary they run on.Evidence
Measured against a PrestaShop 9.2.0 core with
ps_emailsubscription, reproducing the action's own steps (ps-module-extension.neonmerged with the module config, level 5).The analysis ran on the PHP 8.3.6 binary and still reported:
That error can only occur below PHP 8.0:
LegacyControllerContextuses constructor property promotion,readonlyand union types, so PHPStan cannot parse it and treats it as an unknown class. The module declaresconfig.platform.php: 7.1.0, and that is the level PHPStan used, not the 8.3 it was executing on.Same file, same core, only the neon changed:
phpVersionphpVersion: 80100So the level is entirely decided by the config and the composer platform, never by
php-version.Why it matters
This is not theoretical. A cast helper was added to three native modules purely to silence that unknown-class error, which is a symptom of the wrong analysis level rather than a code problem. In
ps_wirepaymentthe helper carried a native: AdminControllerreturn type and produced a fatal error on the configuration page of every PrestaShop 9.x shop, with all fourteen static jobs green. See PrestaShop/ps_wirepayment#103.Modules are currently working around this one by one, by pinning
phpVersionin their own 9.x neon files:developThose are interim fixes for something that belongs in the shared action.
Proposal
The action already builds
phpstan-temp.neonon the fly. DerivingphpVersionfrom thephp-versioninput and appending it there fixes every module at once, and makes the matrix mean what it appears to mean:Two things to settle before implementing:
phpVersionin their own neon, which the temp config includes alongside the injected value. Whether the include wins, the inline value wins, or neon raises a duplicate-key error needs checking, and the module pins should be removed once the action handles it.phpstan-php-versiondefaulting to thephp-versionvalue, so a module can still pin a different analysis level deliberately.Not usable yet: the version range
PHPStan supports
phpVersionas a{min, max}range, which would let one run cover the whole supported interval and would allow shrinking the matrix. It is not available across the board today:8.2.x,9.0.3,9.1.5,9.2.x^1.9.3, resolves to 1.12.xdevelop^2.2The action takes PHPStan from the analysed core when one is present, so the range only works on the
developentry for now. Worth revisiting when the 9.x branches move to PHPStan 2.Related
Modules cleanup epic PrestaShop/PrestaShop#41648, which is the campaign that surfaced this.