Skip to content

Docs: Add class-level PHPStan annotations - #13891

Closed
swissspidy wants to merge 7 commits into
WordPress:trunkfrom
swissspidy:fix/phpstan-class-annotations
Closed

swissspidy wants to merge 7 commits into
WordPress:trunkfrom
swissspidy:fix/phpstan-class-annotations

Conversation

@swissspidy

@swissspidy swissspidy commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

✅ Committed in r64794 (5eb7060).


Split out of #13530, as proposed in this comment (subset 4).

  • WP_Theme: @property-read for the 14 magic properties that __get() serves. These are public back-compat API, so they use the plain tag, which is published in the developer docs.
  • WP_Query: @phpstan-property-read for $query_vars_changed and $query_vars_hash, and @phpstan-method for init_query_flags(). All three are private and are reached through the magic methods.
  • WP_Block_List: @phpstan-implements Iterator<int, WP_Block> and @phpstan-implements ArrayAccess<int, WP_Block>. offsetSet()'s canonical @param now documents the null offset it handles for appending.
  • WP_Widget::update_callback() and ::form_callback(): @final. Their documentation already says they are not to be overridden.

Changes from #13530

These follow the review analysis:

  • The WP_Theme properties now include false wherever the underlying method can return it:
    • name, title, version, description and author come from WP_Theme::get() or ::display().
    • parent_theme comes from the parent theme's get( 'Name' ).
    • screenshot comes from ::get_screenshot().
    • tags is string[]|false rather than list<string>, because array_filter() keeps the keys.
  • @final is dropped from display_callback(). Although its documentation also says "(Do NOT override)", at least one popular plugin overrides it.
  • The standalone @phpstan-return void tags on WP_Block_List::offsetSet() and ::offsetUnset() are dropped.

Changes after review

  • WP_Theme::$parent_theme is string|false, like $name, which has the same source.
  • The @phpstan-param int $offset and @phpstan-return WP_Block|null tags on the WP_Block_List offset methods are dropped, since they repeated the canonical tags. For offsetSet(), the canonical @param is corrected to int|null instead.
  • WP_Block_List also declares the types of the generic Iterator it implements, which were otherwise still reported as missing.
  • The WP_Theme @property-read tags are placed before the @phpstan-type Theme_Key alias.

Effect on the analysis

composer run phpstan passes. The property.notFound and property.private baselines lose their WP_Theme and WP_Query entries. At level 10, compared against trunk, 27 errors are fixed and 4 are introduced. The 4 are in WP_Debug_Data, which passes the theme's name, version and author, now typed string|false, to functions that expect a string.

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.5
Used for: Splitting #13530 by tag kind, applying the corrections from the review analysis on #13530 and from review on this PR, checking each subset with composer run phpstan (also at level 10, compared against trunk) and phpcs, and drafting the commit messages and this description. Reviewed by me.


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.

🤖 Generated with Claude Code

https://claude.ai/code/session_015BuoHZQvJMvPEYp4Sfjgvd

Documents the magic properties of `WP_Theme` and `WP_Query` with `@phpstan-property-read`, along with `WP_Query::init_query_flags()` with `@phpstan-method`, and the element type of `WP_Block_List` with `@phpstan-implements ArrayAccess<int, WP_Block>`.

The `WP_Theme` properties that come from `WP_Theme::get()`, `::display()` or `::get_screenshot()` include `false`, as those methods do.

Marks `WP_Widget::update_callback()` and `::form_callback()` as `@final`, as their documentation says they are not to be overridden.

See #65817.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VieGtSQtUoVfHiGwTmkvtc
Copilot AI balanced review requested due to automatic review settings October 1, 2026 09:01
@github-actions

github-actions Bot commented Oct 1, 2026 •

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.

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

Props swissspidy, westonruter.

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


Props Bot commands

You can trigger Props Bot actions by commenting on this PR:

  • @propsbot add <username> to give props to a GitHub user. Requires write access.
  • @propsbot remove <username> to leave a user out of the props. Requires write access.
  • @propsbot refresh to refresh this list. Adding the props-bot label works too.

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The annotations match the documented runtime behavior, and the related baseline removals are consistent.

Review effort: Balanced
Findings: None

What changed in this PR

Adds accurate PHPStan metadata for magic APIs, block array access, and non-overridable widget callbacks while removing obsolete baseline suppressions.

Changes:

  • Documents magic WP_Theme and WP_Query members.
  • Types WP_Block_List array access.
  • Marks widget callbacks final for static analysis.
File Description
src/​wp-includes/​class-wp-block-list.php Adds generic ArrayAccess types.
src/​wp-includes/​class-wp-query.php Documents magic private members.
src/​wp-includes/​class-wp-theme.php Types magic read-only properties.
src/​wp-includes/​class-wp-widget.php Marks non-overridable callbacks.
tests/​phpstan/​baselines/​property.notFound.neon Removes resolved property suppressions.
tests/​phpstan/​baselines/​property.private.neon Removes resolved visibility suppressions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@github-actions

github-actions Bot commented Oct 1, 2026

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.

Copy link
Copy Markdown
Member Author

🤖 Comment by Claude Opus 5.5

Props for this subset, gathered from the full history of functionMap.php in php-stubs/wordpress-stubs. A change counts only where it introduced or changed the specific tag this PR carries, not just any change to the function's map entry. Purely mechanical changes are left out: tag key renames (php-stubs/wordpress-stubs#420), parameter renames (php-stubs/wordpress-stubs#115), coding standards (php-stubs/wordpress-stubs#119) and the duplicate-code refactor (php-stubs/wordpress-stubs#85).

Stubs change Author Entries
php-stubs/wordpress-stubs#66 @swissspidy WP_Block_List, WP_Block_List::offsetExists(), WP_Block_List::offsetGet(), WP_Block_List::offsetSet(), WP_Block_List::offsetUnset()
php-stubs/wordpress-stubs#216 @IanDelMar WP_Theme
php-stubs/wordpress-stubs#220 @IanDelMar WP_Query
php-stubs/wordpress-stubs#426 @IanDelMar WP_Widget::form_callback(), WP_Widget::update_callback()

Props for this PR (swissspidy for the split, westonruter for the review that shaped it):

swissspidy, westonruter, marian1

Generated by Claude Code

claude added 2 commits October 5, 2026 15:46
Type WP_Theme::$parent_theme as string|false, since it comes from the parent theme's Name header, the same source as $name. Drop the @phpstan tags in WP_Block_List that repeat the canonical tags, and document the null offset of offsetSet() in the canonical @PARAM instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015BuoHZQvJMvPEYp4Sfjgvd
@swissspidy
swissspidy requested a review from westonruter October 7, 2026 11:56
Comment thread src/wp-includes/class-wp-query.php Outdated
Comment thread src/wp-includes/class-wp-theme.php Outdated
claude and others added 4 commits October 8, 2026 07:29
Use `@property-read` for the `WP_Theme` magic properties, as `WP_Comment` does, and align the property names. Type `WP_Query::$query_vars_hash` as `false|(lowercase-string&non-falsy-string)`.

See #65817.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3XYNUvB9sQNDXA14uJQeH
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`WP_Block_List` implements both `Iterator` and `ArrayAccess`, but only `ArrayAccess` was given its key and value types, so PHPStan still reported the generic `Iterator` as missing them, and iterating over a block list yielded untyped values. The iterator yields the same `WP_Block` instances as offset access, since `valid()` stops it before `current()` could return null.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The plain `@property-read` tags are published in the developer docs, so they now come first, separated by a blank line from the `@phpstan-type` alias, which only PHPStan reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wporg-sync pushed a commit that referenced this pull request Oct 27, 2026
Members that `WP_Theme` and `WP_Query` serve through their magic methods were unknown to static analysis, so every access to one was reported as an undefined or private property and recorded in the PHPStan baselines. These are now declared at the class level: the `WP_Theme` properties with `@property-read`, since they are public back-compat API that belongs in the developer docs, and the private `WP_Query` members that its `$compat_fields` and `$compat_methods` expose with PHPStan-only tags. Where a `WP_Theme` property comes from a method that can return `false`, its type includes `false`, which surfaces a few existing cases in `WP_Debug_Data` that pass such a value on as a string.

`WP_Block_List` declares the key and value types of the `Iterator` and `ArrayAccess` interfaces it implements, so that iterating over or indexing a block list yields `WP_Block` instances. The `WP_Widget::update_callback()` and `WP_Widget::form_callback()` methods are marked `@final`, as their documentation already says not to override them. The same note on `WP_Widget::display_callback()` is not enforced, since at least one popular plugin overrides it.

The annotations are adopted from the function map of the php-stubs/wordpress-stubs package.

Developed in #13891.
Follow-up to r31151, r48159, r63020.

Props swissspidy, marian1, westonruter.
See #65817.


git-svn-id: https://develop.svn.wordpress.org/trunk@64794 602fd350-edb4-49c9-b593-d223f7449a82
wporg-sync pushed a commit to WordPress/WordPress that referenced this pull request Oct 27, 2026
Members that `WP_Theme` and `WP_Query` serve through their magic methods were unknown to static analysis, so every access to one was reported as an undefined or private property and recorded in the PHPStan baselines. These are now declared at the class level: the `WP_Theme` properties with `@property-read`, since they are public back-compat API that belongs in the developer docs, and the private `WP_Query` members that its `$compat_fields` and `$compat_methods` expose with PHPStan-only tags. Where a `WP_Theme` property comes from a method that can return `false`, its type includes `false`, which surfaces a few existing cases in `WP_Debug_Data` that pass such a value on as a string.

`WP_Block_List` declares the key and value types of the `Iterator` and `ArrayAccess` interfaces it implements, so that iterating over or indexing a block list yields `WP_Block` instances. The `WP_Widget::update_callback()` and `WP_Widget::form_callback()` methods are marked `@final`, as their documentation already says not to override them. The same note on `WP_Widget::display_callback()` is not enforced, since at least one popular plugin overrides it.

The annotations are adopted from the function map of the php-stubs/wordpress-stubs package.

Developed in WordPress/wordpress-develop#13891.
Follow-up to r31151, r48159, r63020.

Props swissspidy, marian1, westonruter.
See #65817.

Built from https://develop.svn.wordpress.org/trunk@64794


git-svn-id: http://core.svn.wordpress.org/trunk@63944 1a063a9b-81f0-0310-95a4-ce76da25c4cd
@swissspidy
swissspidy deleted the fix/phpstan-class-annotations branch October 27, 2026 04:31
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.

4 participants