Make WordPress Core


Ignore:
Timestamp:
08/05/2026 07:16:57 AM (2 months ago)
Author:
westonruter
Message:

Build/Test Tools: Raise the PHPStan rule level to 1.

Level 1 adds detection of possibly undefined variables, and of unknown magic methods and properties on classes with __call and __get. The 494 errors this surfaces in existing code are recorded in baselines rather than being fixed here, so that new code is held to level 1 straight away while the existing reports are worked through separately. No files under src are changed.

The tests/phpstan/baseline.php file is replaced by one baseline per error identifier under tests/phpstan/baselines, so that the remaining work on each kind of error is visible as a single file that should shrink to nothing and then be deleted. Every entry is scoped to the file which the error occurs in and carries an exact occurrence count, so that a new occurrence of an already baselined error is reported rather than absorbed. The consequence is that fixing a baselined error means regenerating its baseline in the same change, because the count no longer matches.

PHPStan's own --generate-baseline captures every error a run reports, with no way to restrict it to one identifier, so tests/phpstan/generate-baselines.php is added to write the files instead, exposed as composer phpstan:baselines and as npm run typecheck:php:baselines. A run also deletes any baseline whose identifier no longer reports anything, and rewrites the list of baselines in phpstan.neon.dist. The ignoreErrors in that file now has a comment explaining how it is distinct from a baseline: an entry there is a decision that the code is right as written, whereas a baseline entry is work still to be done. The constants that add_theme_support() defines are declared in the configuration so that the errors around them are resolved rather than recorded, and tests/phpstan/README.md is updated throughout.

Three problems in the static analysis GHA workflow are fixed as well. Fixing a baselined error makes PHPStan report an unmatched ignore, which surfaced only as an annotation reading like a complaint about a correct fix; the job now detects any ignore.* report and fails with an explanation of what to run. An analysis that did not finish passed as a green run, because the status of the pipeline was that of cs2pr rather than of PHPStan; that status is now recovered and a run that did not finish fails. The path filter deciding whether the workflow runs named only the old baseline file, so a pull request that merely regenerated the baselines would not have run the analysis that checks them.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/11151.
Follow-up to r61699.

Props westonruter, sabernhardt, apermo, johnjamesjacoby, adamsilverstein, justlevine.
See #61175.
Fixes #64680.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpstan/README.md

    r62939 r63019  
    3939composer run phpstan -- -vvv --debug
    4040```
     41
     42Note the `--` in each of those. Composer needs it in order to pass the flags on to PHPStan rather than reading them as its own, and without it they are discarded silently. The npm script supplies it, which is why only one is needed there.
    4143
    4244For available flags, see https://phpstan.org/user-guide/command-line-usage.
    … …  
    9294- Adding the error pattern to the `ignoreErrors` section of the `phpstan.neon.dist` configuration file. This should be used to handle conflicts with WordPress Coding Standards or similar project decisions, or to allowlist legacy code that is not worth refactoring solely to satisfy the tests.
    9395
    94 - Adding an error to the "tech debt" baseline. This should be used for code that needs to be addressed eventually - by fixing, refactoring, or ignoring via one of the above methods - but is not worth addressing right now.
     96- Adding an error to a "tech debt" baseline. This should be used for code that needs to be addressed eventually - by fixing, refactoring, or ignoring via one of the above methods - but is not worth addressing right now.
    9597
    9698        Baselines are a useful triage tool for handling PHPStan errors in legacy code, as they allow us to enforce stricter code quality checks on new code, while gradually chipping away at the existing issues over time. **Avoid adding PHPStan errors from new code whenever possible, and use baselines as a last resort.**
    9799
    98         The baseline file is located at `tests/phpstan/baseline.php` and generated by running PHPStan with the `--generate-baseline` flag:
     100### How the baselines are organized
    99101
    100         ```bash
    101         npm run typecheck:php -- --generate-baseline=tests/phpstan/baseline.php
     102The baselines live in [`baselines/`](baselines), one file per error identifier, such as `variable.undefined.neon`. Splitting them this way keeps each kind of error visible as a single file that should shrink to nothing and then be deleted, rather than as part of one large file in which every kind is mixed together.
    102103
    103         # or, with Composer directly:
    104         composer run phpstan -- --generate-baseline=tests/phpstan/baseline.php
    105         ```
     104Every entry is scoped to the file the error occurs in and carries an exact occurrence count:
    106105
    107         This will regenerate the baseline file with any new errors added to the existing ones. You can then commit the updated baseline file.
     106```neon
     107-
     108        message: '#^Variable \$wpdb might not be defined\.$#'
     109        identifier: variable.undefined
     110        count: 4
     111        path: ../../../src/wp-trackback.php
     112```
     113
     114Both the path and the count matter. A new occurrence of an already baselined error does not match the entry, even in a file that is already listed, and is reported as a new error. That is the point of recording them this way: the baselines describe exactly what exists today, so nothing new slips in behind them.
     115
     116The consequence is that **fixing a baselined error means regenerating its baseline as part of the same change**, because the count no longer matches. A count that no longer matches is reported as an `ignore.count` error, which PHPStan does not allow to be ignored or baselined.
     117
     118### Regenerating the baselines
     119
     120The baselines are generated, and should not be edited by hand. Regenerate them with:
     121
     122```bash
     123npm run typecheck:php:baselines
     124```
     125
     126which will run the generator in the Docker container.
     127
     128As with the analysis itself, flags are passed by adding `--` followed by the flags themselves:
     129
     130```bash
     131# a single identifier:
     132npm run typecheck:php:baselines -- --identifier=variable.undefined
     133
     134# several, either comma separated or by repeating the option:
     135npm run typecheck:php:baselines -- --identifier=variable.undefined,isset.variable
     136npm run typecheck:php:baselines -- --identifier=isset.variable --identifier=empty.variable
     137
     138# print every error as one baseline, writing nothing:
     139npm run typecheck:php:baselines -- --combined
     140
     141# the remaining options:
     142npm run typecheck:php:baselines -- --help
     143```
     144
     145If you are not using the Docker environment, you can run the generator via Composer directly:
     146
     147```bash
     148composer phpstan:baselines
     149
     150composer phpstan:baselines -- --identifier=variable.undefined
     151composer phpstan:baselines -- --combined
     152composer phpstan:baselines -- --help
     153```
     154
     155Note the `--` in each of those. Composer needs it in order to pass the flags on to the script rather than reading them as its own, and without it they are discarded silently, so `composer phpstan:baselines --identifier=variable.undefined` regenerates every baseline rather than that one. The npm script supplies it, which is why only one is needed there.
     156
     157A run also deletes any baseline whose identifier no longer reports anything, and rewrites the list of them between the `# phpstan:baselines` markers in the `includes` of [`phpstan.neon.dist`](../../phpstan.neon.dist) to match. Adding a newly split out baseline, and retiring one that has reached zero, therefore need no edit of the configuration.
     158
     159PHPStan's own `--generate-baseline` is deliberately not used directly. It captures every error a run reports, with no way to restrict it to one identifier, so it cannot refresh a single baseline without sweeping every other kind of error into it.
    108160
    109161## Performance and troubleshooting
Note: See TracChangeset for help on using the changeset viewer.