Skip to content

Code Quality: Annotate three more impure functions for PHPStan - #13077

Open
tstokes8040 wants to merge 2 commits into
WordPress:trunkfrom
tstokes8040:phpstan/redundant-checks
Open

Code Quality: Annotate three more impure functions for PHPStan#13077
tstokes8040 wants to merge 2 commits into
WordPress:trunkfrom
tstokes8040:phpstan/redundant-checks

Conversation

@tstokes8040

@tstokes8040 tstokes8040 commented Aug 16, 2026

Copy link
Copy Markdown

PHPStan infers current_theme_supports(), wp_installing() and is_front_page() as pure, so it remembers what each returned and reuses that value at a later call in the same scope. All three read state that can change between calls:

Function Mutable state it reads
current_theme_supports() The $_wp_theme_features global, and applies the current_theme_supports-{$feature} filter before returning
wp_installing() A static $installing, which the same function sets when passed an argument
is_front_page() The $wp_query global

This resolves three baselined errors:

Site Error
Custom_Image_Header::step_1(), class-custom-image-header.php:619 booleanNot.alwaysTrue
get_transient(), option.php:1459 booleanNot.alwaysTrue
redirect_canonical(), canonical.php:688 booleanAnd.leftAlwaysTrue

In the first, the enclosing elseif chain has already tested current_theme_supports( 'custom-header', 'flex-height' ), so PHPStan carried that answer into the inner check. In the second, get_transient() reaches the else branch only when wp_using_ext_object_cache() || wp_installing() was false, so the later ! wp_installing() looked settled.

booleanNot.alwaysTrue goes from 8 to 6 and booleanAnd.leftAlwaysTrue from 5 to 4. The baselines are generated, not hand-edited; neither reaches zero, so no baseline is deleted and phpstan.neon.dist is unchanged.

A note on redirect_canonical()

The condition reads ( ! is_front_page() || is_front_page() && get_query_var( 'paged' ) > 1 ). Both calls sit inside one expression with nothing in between, so ! A || A && B could equally be simplified to ! A || B rather than annotated. I chose the annotation because it is independently true — is_front_page() really does read mutable global state, and PHPStan should not assume otherwise anywhere else either — and because rewriting a condition in the canonical redirect path carries behaviour risk for no analytical gain. Happy to switch to the simplification if reviewers prefer it.

Testing instructions

  1. npm run typecheck:php on trunk reports [OK] No errors, because the occurrences are baselined.
  2. With this branch applied, npm run typecheck:php reports [OK] No errors across 1289 files with both baselines regenerated.
  3. To confirm the change is surgical, run the analysis with all baselines suppressed before and after and diff the results on file plus message rather than line number: exactly three errors disappear and none appears, anywhere in the codebase, for any identifier.
  4. composer lint reports no errors for the three modified files. src/wp-includes/query.php carries four pre-existing WordPress.DB.PreparedSQL.NotPrepared warnings at lines 1228 and 1231; the same four are present in the file on trunk and are untouched by a docblock change at line 462.

Documentation-only change; no runtime behaviour is affected.

Related: #13074 annotates current_user_can(), WP_Filesystem_Base::chmod() and WP_HTML_Processor::next_token() for the same reason, and #13057 annotates WP_HTML_Tag_Processor::parse_next_tag(). This PR is independent of both and touches disjoint files.

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

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: grouping the remaining boolean PHPStan baselines by root cause, identifying this cluster, measuring the before/after error diff across the whole codebase, regenerating the baselines, and drafting this description. The change itself and the verification runs were reviewed and confirmed by me in a local development environment.

Count of removed errors: 2

PHPStan infers `current_theme_supports()`, `wp_installing()` and `is_front_page()` as pure, so it remembers what each returned and reuses that value at a later call in the same scope. All three read state that can change between calls: `current_theme_supports()` reads the `$_wp_theme_features` global and applies the `current_theme_supports-{$feature}` filter, `wp_installing()` reads a static that the same function can also set, and `is_front_page()` reads the `$wp_query` global.

This resolves three baselined errors: the `! current_theme_supports( 'custom-header', 'flex-height' )` check in `Custom_Image_Header::step_1()`, the `! wp_installing()` branch in `get_transient()`, and the repeated `is_front_page()` call in `redirect_canonical()`. The baselines were regenerated with:
{{{
composer phpstan:baselines -- --identifier=booleanNot.alwaysTrue,booleanAnd.leftAlwaysTrue
}}}

Props tstokes8040.
See #65817.
@github-actions

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 tstokes8040.

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

  • 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.

Comment thread src/wp-includes/load.php
* @return bool True if WP is installing, otherwise false. When a `$is_installing` is passed, the function will
* report whether WP was in installing mode prior to the change to `$is_installing`.
*
* @phpstan-impure

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.

@westonruter this isn’t related to this PR, but just like with WPCS “ignore” comments I find these to be a bit distracting in the code. it’s like we’re polluting our code to cover the deficiencies of a tool we happen to be using right now.

can these functions and methods not be defined as impure in external config files for PHPStan? if we could do that, it would keep the code clean while making up for the fact that PHPStan didn’t infer the purity aspect properly.

Sign up for free to 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.

2 participants