Repository navigation
chore(lint): run PHPStan and Rector isolated from the machine's php.ini - #259
Conversation
b5446f9 to
4ffd330
Compare
|
First push failed Both tools use Fixed by replacing the machine's |
4ffd330 to
b623581
Compare
bin/lint.sh started both tools with the normal `php`, so every php.ini on the machine was loaded into the process that does the analysis. This repository also builds a PHP extension (php-ext/, the legacy `zvec`), and a contributor can have a copy of it installed and enabled in php.ini — the same reason the test suite needs `php -n` (#188). What the tools see then depends on the machine instead of on the working tree, which is how a Rector finding in #246 showed up in CI but not locally. AGENTS.md mentioned `php -n` for the tests only. The tools now run with `PHPRC` pointing at an ini that is generated per run, which replaces the machine's php.ini for the process bin/lint.sh starts and for the ones PHPStan starts for itself: `PHPRC` is an environment variable and is inherited, while `-n` is not — PHPStan restarts with `PHP_BINARY -d ...` and starts parallel workers, both of which load the machine's php.ini again (verified with `ps` while it runs). `php -n` on its own is not usable here: PHPStan needs the Phar class and Rector needs `PhpToken` from the tokenizer extension, and on the CI image those come from conf.d, which `-n` drops — the first push of this branch failed `lint` with `Class "Phar" not found` and `Class "PhpToken" not found`. conf.d is still scanned instead, so extensions installed that way stay available. What the replaced php.ini took with it is put back explicitly: `extension_dir` (on a Homebrew/pecl setup it is set in php.ini while the conf.d entries name their extension without a path, so every one of them would otherwise fail to load), `memory_limit=-1` instead of the built-in 128M, and FFI (`-d extension=ffi` plus `extension=ffi` in the generated ini) only where the current PHP has none in that same environment — phpstan.neon ignores undefined FFI methods and properties, not a missing FFI class, so without FFI the analysis would report errors on src/. The CI lint job now asks setup-php for FFI, so the analysis has it however the runner provides it. No step, level or rule changed, and PHP-CS-Fixer is untouched: it works on the token stream and never resolves the project's classes. Verified locally with the legacy zvec extension loaded: bin/lint.sh and bin/lint.sh --fix pass without a warning, the analysis no longer resolves the extension's classes, and Phar, tokenizer, FFI and xdebug are all still available. tests/test_docs_consistency.phpt passes with the CHANGELOG entry. shellcheck clean.
b623581 to
013d561
Compare
|
For the record: the first full run of this branch had The same class of failure happened on |
Closes #258.
What changed
bin/lint.shstarted PHPStan and Rector with the normalphp, so everyphp.inion themachine was loaded into the process that does the analysis. This repository also builds a
PHP extension (
php-ext/, the legacyzvec), and a contributor can have a copy of itinstalled and enabled in
php.ini— the same reason the test suite needs-n(#188).What the tools see then depends on the machine instead of on the working tree.
AGENTS.mdmentioned
php -nfor the tests only.This changes what the lint gate runs. Both tools now run with
PHPRCpointing at anini that is generated per run and holds nothing, so the machine's
php.iniis not read.No step, level or rule changed:
phpstanandrectorsteps, and therectorcall in the--fixbranch, gothrough one
php_analysishelper;memory_limit=-1replaces the built-in 128M, because the replacedphp.iniis wherethe real limit usually is;
extension_diris carried into the generated ini: on a Homebrew/pecl setup it is set inphp.iniwhile the conf.d entries name their extension without a path, so without it everyextension installed that way would fail to load (round 3 of the review caught exactly
that, and the resulting xdebug warning);
extension=ffiin the generated ini — every one of those processes loads it, so it is notalso passed as
-d, or PHP would load it twice.phpstan.neonignores undefined FFImethods and properties, not a missing FFI class, so without FFI the analysis would report
errors on
src/;lintjob askssetup-phpfor FFI, so the analysis has it however the runnerprovides it;
classes.
Why not
php -nphp -nwas the first attempt and it does not work here, for two separate reasons.Pharclass and Rector needsPhpTokenfrom the tokenizer extension, which theubuntu-24.04setup-php imageprovides through conf.d inis. The first push of this branch failed
lintwithClass "Phar" not foundandClass "PhpToken" not found— see the comment below.PHP_BINARY -d memory_limit=… -d phpstan.restarted=1 …and starts parallel workers;neither carries
-n, so those processes loaded the machine'sphp.iniagain. Verifiedwith
ps -Awhilephp -n vendor/bin/phpstan analyseruns.PHPRCis an environment variable and is inherited, so it replacesphp.iniin therestart and in the workers too. A probe file calling a method only the loaded extension
could know confirms it: PHPStan resolves the class without the isolation and does not
resolve it with it.
conf.d is still scanned, so an extension installed that way (tokenizer on CI, FFI,
xdebug) stays available — which is why the generated ini carries the machine's
extension_dirover: on a Homebrew/pecl setup that is itself set inphp.ini. Anextension=zvec.soline inphp.ini— the #188 case of amake installplus a manual line — no longer reaches the tools; one in conf.d stillwould. That residual gap is stated in
AGENTS.md.What this does not claim
The extension does not currently shadow
src/:php-ext/registers its 15 classes in theglobal namespace (
ZVecSchema,ZVecDoc, …) whilesrc/is namespacedCrazyGoat\ZVec\, so there is no name collision. The real shadowing in #188 is a runtimeone —
src/ZVec.php:9returns early whenextension_loaded('zvec'), so the barrel filenever loads the FFI-only classes — and that is what breaks the test suite. PHPStan and
Rector parse
src/and never run that guard.So this is not a fix for an observable wrong result today; it removes the machine
dependency that made the #246 finding (a Rector change in CI that the local run did not
report) possible, and it produces the same verdict either way on this machine. That is
also why no regression test comes with it: there is no assertion that would fail before
the change and pass after it.
findings.mdrecords the rest, including thatcomposer.lockis gitignored here, so thelocal tool versions float and have to be ruled out before blaming an extension.
Verified
bin/lint.shandbin/lint.sh --fixpass on PHP 8.5.10 with the legacyzvecextensionloaded, and
--fixchanges no file.zvecnot loaded and PHPStan does not resolve the extension'sclasses, while
Phar,tokenizer, FFI and xdebug are available andmemory_limitis-1; no startup warning is printed.tests/test_docs_consistency.phptpasses with the CHANGELOG entry.shellcheckandbash -nclean..phptsuite cannot run in a fresh worktree (it needssdk/andffi/build/, built bybin/worktree-setup.sh); the change touches nosrc/,ffi/ortests/file.