Skip to content

Tests / Build Scripts: Configure PHPStan level 0 - #10419

Closed
justlevine wants to merge 59 commits into
WordPress:trunkfrom
justlevine:tests/phpstan/level-0
Closed

Tests / Build Scripts: Configure PHPStan level 0#10419
justlevine wants to merge 59 commits into
WordPress:trunkfrom
justlevine:tests/phpstan/level-0

Conversation

@justlevine

Copy link
Copy Markdown

Trac ticket: https://core.trac.wordpress.org/ticket/61175

This PR adds a PHPStan configuration for PHPStan level 0, along with tests and docs.

Based from #7619 - which remains in use to explore adopting future levels (alongside parallel remediation branches).

Proposal: https://make.wordpress.org/core/2025/07/11/proposal-phpstan-in-the-wordpress-core-development-workflow/


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

@github-actions

github-actionsBot commented Oct 25, 2025

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props justlevine, westonruter, desrosj, johnbillion.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@github-actions

Copy link
Copy Markdown

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces PHPStan level 0 static analysis configuration to WordPress Core, establishing a foundation for catching code errors without execution. The implementation includes PHPStan configuration files, bootstrap scripts, documentation, GitHub workflows for CI integration, and inline code annotations to suppress legitimate PHPStan warnings.

Changes:

  • Added PHPStan level 0 configuration with baseline support for legacy code
  • Added PHPStan annotations to source files to document legitimate suppressions
  • Integrated PHPStan into CI/CD via GitHub workflows and npm/composer scripts

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 9 comments.

Show a summary per file
FileDescription
phpstan.neon.distMain PHPStan configuration with level 0 rules and baseline inclusion
tests/phpstan/base.neonBase configuration defining paths, exclusions, and codebase-specific settings
tests/phpstan/baseline.phpEmpty baseline file for future tech debt tracking
tests/phpstan/bootstrap.phpDefines WordPress constants for PHPStan discovery
tests/phpstan/README.mdDocumentation for running and configuring PHPStan
src/wp-includes/template.phpAdded PHPStan annotations and removed obsolete comment
src/wp-includes/style-engine/class-wp-style-engine-css-rules-store.phpAdded @phpstan-consistent-constructor annotation
src/wp-includes/media.phpAdded annotation for PHP8+ GdImage class
src/wp-includes/functions.phpAdded enhanced return type documentation for wp_die()
src/wp-includes/customize/*.phpAdded @return documentation for overridden update() methods
src/wp-includes/class-wp-theme-json.phpAdded null return value to match documented return type
src/wp-includes/class-wp-scripts.phpRemoved obsolete PHPStan suppression comment
src/wp-admin/press-this.phpAdded PHPStan annotations for plugin file includes
src/wp-admin/includes/class-wp-filesystem-ssh2.phpAdded phpstan-ignore-next-line for unimplemented method
composer.jsonAdded phpstan/phpstan dependency and analyse script
package.jsonAdded test:php:stan npm script
phpcs.xml.distExcluded PHPStan files from coding standards checks
.gitignoreAdded phpstan.neon to ignore list for local overrides
.github/workflows/php-static-analysis.ymlMain workflow for running PHPStan on pushes and PRs
.github/workflows/reusable-php-static-analysis.ymlReusable workflow for PHPStan execution

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadtests/phpstan/README.md Outdated
Comment threadsrc/wp-includes/functions.php Outdated
Comment threadtests/phpstan/base.neon Outdated
Comment threadtests/phpstan/bootstrap.php Outdated
Comment threadtests/phpstan/README.md Outdated
Comment thread.github/workflows/php-static-analysis.yml Outdated
Comment threadcomposer.json Outdated
"squizlabs/php_codesniffer": "3.13.5",
"wp-coding-standards/wpcs": "~3.3.0",
"phpcompatibility/phpcompatibility-wp": "~2.1.3",
"phpstan/phpstan": "~2.1.33",

CopilotAIFeb 12, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version constraint "~2.1.33" is very specific and may cause issues. The tilde operator (~) for a three-part version like 2.1.33 means ">=2.1.33 <2.2.0". This constraint locks to a specific patch version which may not exist or may prevent receiving important bug fixes. Consider using "^2.1" (which means ">=2.1.0 <3.0.0") or "~2.1.0" (which means ">=2.1.0 <2.2.0") instead to allow flexibility for patch updates while staying within the same minor version

Suggested change
"phpstan/phpstan": "~2.1.33",
"phpstan/phpstan": "~2.1.0",

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@westonruter what are your thoughts on this one?

I definitely think we should pin at the version we commit, but I wouldn't want to Semver because contextually "nonbreaking enhancements" are breaking from an implementation POV if they create a new quality gate.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. So switching to ~2.1.0 would keep it at 2.1.x. This seems necessary because there is no composer.lock, which actually is curious since we package-lock.json. If we had a composer.lock then we'd be free to use ^2.1. But I suppose can't use it because of the different versions of PHP which may end up getting used when doing composer install.

So yeah, I guess go with ~2.1.0 and not ^2.1. When PHPStan 2.2 comes out, we'll have to manually upgrade.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on when we merge, there might be some value to pinning a minimum patch release too e.g. phpstan/phpstan#8438 (comment) . Leaving this for now, and might even bump it if this PR lingers. Keeping the issue open as a reminder to drop this as low as we think is worthwhile before we merge.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference is to pin to a specific version. Patch releases do introduce changes that affect the results (probably less so at level 0 but it definitely happens at higher levels).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 88d619b

Comment thread.github/workflows/php-static-analysis.yml Outdated
Comment threadtests/phpstan/README.md Outdated
justlevineand others added 4 commits February 12, 2026 23:53
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Comment threadsrc/wp-includes/customize/class-wp-customize-background-image-setting.php Outdated
Comment threadsrc/wp-includes/customize/class-wp-customize-filter-setting.php Outdated
Comment threadsrc/wp-includes/customize/class-wp-customize-filter-setting.php Outdated
Comment threadsrc/wp-includes/customize/class-wp-customize-header-image-setting.php Outdated
Comment threadcomposer.json Outdated
Comment threadpackage.json Outdated
@westonruter

westonruter commented Feb 12, 2026

Copy link
Copy Markdown
Member

In Gruntfile.js, there is a precommit task, which includes precommit:php as well as format:php. Curiously, it doesn't to lint:php (probably because there are still so many violations), but it should run PHPStan since it can run on the entire codebase without errors.

Update: Done in 6a46b90.

@westonruter

Copy link
Copy Markdown
Member

I tried running composer analyse and it seemed to finish but then it hung at 99%. Finally it errored:

image

@justlevine

justlevine commented Feb 12, 2026

Copy link
Copy Markdown
Author

I tried running composer analyse and it seemed to finish but then it hung at 99%. Finally it errored:

image

@westonruter can you run it with -- --vvv? I believe that should show us which file it's timing out on.

(Also confirming that you're using the vanilla phpstan.neon.dist and the CLI is just inaccurate - if not, please share any modifications there too)

@westonruter

Copy link
Copy Markdown
Member

Actually, I was using a local phpstan.neon which was overriding the one provided in this PR. When I try using the one in the PR, the result is actually worse. It never starts analyzing. This seems to be due to the paths config:

paths:
- ../../src

If I change it to:

paths:- ../../src/wp-admin- ../../src/wp-includes

Then it works, aside from two errors being found:

 ------ ---------------------------------------------------------------------- Line wp-admin/load-scripts.php ------ ---------------------------------------------------------------------- 68 Function get_file not found. 🪪 function.notFound 💡 Learn more at https://phpstan.org/user-guide/discovering-symbols at src/wp-admin/load-scripts.php:68 ------ ---------------------------------------------------------------------- ------ ---------------------------------------------------------------------- Line wp-admin/load-styles.php ------ ---------------------------------------------------------------------- 83 Function get_file not found. 🪪 function.notFound 💡 Learn more at https://phpstan.org/user-guide/discovering-symbols at src/wp-admin/load-styles.php:83 ------ ---------------------------------------------------------------------- 

What appears to be the issue is that I use my wordpress-develop clone for core and plugin development together. So my src/wp-content is very large. I know other contributors do this as well, so this is something which should be accounted for.

Nevertheless, the change I made is not ideal because then it doesn't check the PHP files in the root of src, like wp-login.php. I'm confused because from looking at the excludePaths config, it would seem that src/wp-content is supposed to be excluded, and yet PHPStan still seems to be scanning all the 16GB worth of files in the plugins dir.

Comment threadtests/phpstan/base.neon Outdated
Comment on lines +90 to +91
# These files are sourced by wordpress/gutenberg in `tools/release/sync-stable-blocks.js`.
- ../../src/wp-includes/blocks

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose then this would necessitate adding PHPStan to Gutenberg to do the necessary checks?

@justlevinejustlevineFeb 16, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WordPress/gutenberg#66598 but it's been gathering dust for a while.

Comment thread.github/workflows/phpstan-static-analysis.yml
Comment thread.github/workflows/reusable-phpstan-static-analysis.yml
@westonruter

westonruter commented Feb 18, 2026

Copy link
Copy Markdown
Member

@dmsnell As of 4513d92, PHPStan issues do not cause a job to fail:

image

And the annotations show up as warnings instead of errors:

image

Instead of:

image

westonruterand others added 3 commits February 18, 2026 09:27
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Comment thread.github/workflows/reusable-php-static-analysis.yml Outdated
westonruterand others added 2 commits February 18, 2026 12:34
Co-authored-by: Jonathan Desrosiers <359867+desrosj@users.noreply.github.com>
Comment threadtests/phpstan/base.neon Outdated
westonruterand others added 2 commits February 19, 2026 10:41
Co-authored-by: John Blackbourn <johnbillion@git.wordpress.org>
Comment threadsrc/wp-includes/functions.php Outdated
westonruterand others added 2 commits February 19, 2026 10:44
Co-authored-by: John Blackbourn <john@johnblackbourn.com>
Co-authored-by: John Blackbourn <john@johnblackbourn.com>
pento pushed a commit that referenced this pull request Feb 20, 2026
This change introduces PHPStan static analysis configured at [https://phpstan.org/user-guide/rule-levels rule level 0], which includes: "basic checks, unknown classes, unknown functions, unknown methods called on `$this`, wrong number of arguments passed to those methods and functions, always undefined variables". Contributors may elect for a higher PHPStan rule level by creating a `phpstan.neon` which overrides `phpstan.neon.dist`.
* Fix various PHPStan level 0 errors by adding `@phpstan-ignore` comments, updating PHPDoc types, and adding missing return values.
* Remove existing `@phpstan-ignore` comments that are now obsolete or inapplicable for level 0.
* Add a new GitHub Actions workflow for PHPStan Static Analysis. Reports are currently provided as warnings with inline annotations in pull requests and do not fail the build.
* Add a `phpstan` Grunt task and include it in the `precommit:php` task to run before `phpunit`.
* Introduce a `typecheck:php` npm script and a `composer phpstan` script to run analysis in local development environments.
* Add documentation for PHPStan usage in `tests/phpstan/README.md`.
Developed in #10419
Props justlevine, westonruter, johnbillion, desrosj, SirLouen, dmsnell, oglekler, joehoyle, jorbin.
See #64238, #63268, #52217, #51423.
Fixes #61175.
git-svn-id: https://develop.svn.wordpress.org/trunk@61699 602fd350-edb4-49c9-b593-d223f7449a82
@github-actions

Copy link
Copy Markdown

A commit was made that fixes the Trac ticket referenced in the description of this pull request.

SVN changeset: 61699
GitHub commit: 8a82e67

This PR will be closed, but please confirm the accuracy of this and reopen if there is more work to be done.

markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Feb 20, 2026
This change introduces PHPStan static analysis configured at [https://phpstan.org/user-guide/rule-levels rule level 0], which includes: "basic checks, unknown classes, unknown functions, unknown methods called on `$this`, wrong number of arguments passed to those methods and functions, always undefined variables". Contributors may elect for a higher PHPStan rule level by creating a `phpstan.neon` which overrides `phpstan.neon.dist`.
* Fix various PHPStan level 0 errors by adding `@phpstan-ignore` comments, updating PHPDoc types, and adding missing return values.
* Remove existing `@phpstan-ignore` comments that are now obsolete or inapplicable for level 0.
* Add a new GitHub Actions workflow for PHPStan Static Analysis. Reports are currently provided as warnings with inline annotations in pull requests and do not fail the build.
* Add a `phpstan` Grunt task and include it in the `precommit:php` task to run before `phpunit`.
* Introduce a `typecheck:php` npm script and a `composer phpstan` script to run analysis in local development environments.
* Add documentation for PHPStan usage in `tests/phpstan/README.md`.
Developed in WordPress/wordpress-develop#10419
Props justlevine, westonruter, johnbillion, desrosj, SirLouen, dmsnell, oglekler, joehoyle, jorbin.
See #64238, #63268, #52217, #51423.
Fixes #61175.
Built from https://develop.svn.wordpress.org/trunk@61699
git-svn-id: http://core.svn.wordpress.org/trunk@61007 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@szepeviktor

Copy link
Copy Markdown

This is a miracle.

@dmsnell

Copy link
Copy Markdown
Member

Congrats folks, good luck with this! Count-down to level 1?

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants

@justlevine@westonruter@szepeviktor@dmsnell@johnbillion@desrosj