Skip to content

Docs: Add PHPStan array shapes - #13894

Open
swissspidy wants to merge 4 commits into
WordPress:trunkfrom
swissspidy:fix/phpstan-array-shapes
Open

swissspidy wants to merge 4 commits into
WordPress:trunkfrom
swissspidy:fix/phpstan-array-shapes

Conversation

@swissspidy

@swissspidy swissspidy commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

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

This adds array shapes and refined array types to functions that return or accept arrays with a known set of keys:

  • the HTTP API
  • WP_Widget::widget()'s $args
  • the RSS widget form and processing
  • the robots functions
  • _get_list_table()'s $args
  • several admin markup helpers
  • wp_html_split()
  • register_nav_menus()
  • the bookmark link categories
  • get_block_wrapper_attributes()'s $extra_attributes

Changes from #13530

These follow the review analysis:

  • HTTP response. The shape is now defined once, as an HTTP_Response type alias on WP_Http, and used by request(), get(), post() and head(). The 8 wp_remote_*() and wp_safe_remote_*() functions repeat it, because function docblocks cannot import a class type alias. The shape now covers non-blocking requests, which return empty headers, false for the code and message, no filename, and a null http_response. It also matches the hash notation request() already has.
  • WP_Widget::widget() $args. Only the four keys that the_widget() passes are required: before_widget, after_widget, before_title and after_title.
  • wp_widget_rss_form(). number is int|string, since the widget template passes '__i__'.
  • get_shortcode_tags_in_content() returns list<string>, since it yields '' when no shortcodes are registered.
  • get_block_wrapper_attributes(). $extra_attributes also accepts numbers, which the body casts. The conditional return type is dropped, because the function can still return '' when $extra_attributes is non-empty.
  • Left out: list<mixed> $args on the cron functions. The stored cron arrays are still array<mixed>, so wp-cron.php would report errors.
  • Left out: the shapes for WP_List_Table::set_pagination_args(), which rejected numeric-string totals, the documented infinite_scroll key and query strings, and for Custom_Image_Header::set_header_image(), whose body accepts objects and missing keys.
  • Left out: the standalone @phpstan-return void tags on wp_widget_rss_form() and register_widget().

Changes after review

  • HTTP response. http_response is optional in all 9 shapes, because request() returns a pre_http_request filter value as is, and that value doesn't have to include it.
  • wp_widget_rss_form(). error is optional, since the dashboard widget's first render passes only number. The $inputs values are bool|'hidden', since 'hidden' outputs a hidden field.
  • _get_list_table(). The shape includes the documented plural, singular and ajax keys, not just screen.
  • Dropped: the @phpstan-param tags on wp_get_script_tag() and wp_get_inline_script_tag(), which repeated the canonical array<string, string|bool> already on trunk.

Effect on the analysis

composer run phpstan passes with no baseline changes. At level 10, compared against trunk, 57 errors are fixed and 19 are introduced. 2 of the 19 are in WP_Http::request(), where the pre_http_request and http_response filters return mixed. The rest are mostly callers that pass a plain array where a shape is now expected, or existing errors reworded. (These counts were measured before the changes after review.)

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

Adds array shapes and refined array types to functions that return or accept arrays with a known set of keys.

The HTTP response is defined once, as the `HTTP_Response` type on `WP_Http`, and repeated on the `wp_remote_*()` and `wp_safe_remote_*()` functions. It includes the response of a non-blocking request, which has no `filename` and `false` for the code and message.

Compared to the function map in php-stubs/wordpress-stubs, `WP_Widget::widget()` requires only the four keys `the_widget()` passes, `wp_widget_rss_form()` accepts the `'__i__'` placeholder as the widget number, the script tag attributes accept `false`, and `get_shortcode_tags_in_content()` returns `list<string>`. The map's `list<mixed>` for cron arguments, its shapes for `WP_List_Table::set_pagination_args()` and `Custom_Image_Header::set_header_image()`, and its conditional return for `get_block_wrapper_attributes()` are left out, since core does not match them.

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. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @claude.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

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

Props swissspidy.

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

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

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

🟡 Changes recommended

Several new shapes exclude valid RSS widget inputs and short-circuited HTTP responses.

Review effort: Balanced
Findings: 3 Medium severity

Open (3)
What changed in this PR

Adds refined PHPStan array types and shapes across WordPress APIs without changing runtime behavior.

Changes:

  • Defines reusable and public HTTP response shapes.
  • Refines widget, robots, navigation, media, and block attribute arrays.
  • Narrows several utility return and parameter types.
File Description
src/​wp-includes/​widgets.php Types RSS widget data.
src/​wp-includes/​shortcodes.php Types shortcode lists.
src/​wp-includes/​script-loader.php Types script attributes.
src/​wp-includes/​robots-template.php Types robots directives.
src/​wp-includes/​nav-menu.php Types menu registrations.
src/​wp-includes/​http.php Types remote responses.
src/​wp-includes/​functions.php Refines unique-ID input.
src/​wp-includes/​formatting.php Types HTML split results.
src/​wp-includes/​feed.php Types Atom text tuples.
src/​wp-includes/​class-wp-widget.php Shapes widget display arguments.
src/​wp-includes/​class-wp-http.php Defines the HTTP response alias.
src/​wp-includes/​class-wp-dependencies.php Refines dependency groups.
src/​wp-includes/​class-wp-block-supports.php Types wrapper attributes.
src/​wp-admin/​includes/​post.php Types status lists.
src/​wp-admin/​includes/​nav-menu.php Shapes menu columns.
src/​wp-admin/​includes/​ms.php Shapes help-tab arguments.
src/​wp-admin/​includes/​media.php Shapes media helper results.
src/​wp-admin/​includes/​list-table.php Shapes list-table arguments.
src/​wp-admin/​includes/​bookmark.php Refines bookmark results.

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

Comment thread src/wp-includes/class-wp-http.php Outdated
Comment thread src/wp-includes/http.php Outdated
Comment thread src/wp-includes/widgets.php Outdated

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@873417a @johnbillion wp_remote_get(), wp_remote_head(), wp_remote_post(), wp_remote_request()
php-stubs/wordpress-stubs@fc6e383 @johnbillion wp_remote_get(), wp_remote_head(), wp_remote_post(), wp_remote_request()
php-stubs/wordpress-stubs@72ea260 @johnbillion wp_safe_remote_get(), wp_safe_remote_head(), wp_safe_remote_post(), wp_safe_remote_request()
php-stubs/wordpress-stubs@ed1b85e @johnbillion WP_Http::get(), WP_Http::head(), WP_Http::post()
php-stubs/wordpress-stubs#67 @IanDelMar wp_widget_rss_form()
php-stubs/wordpress-stubs#151 @mociofiletto WP_Http::get(), WP_Http::head(), WP_Http::post(), wp_remote_get(), wp_remote_head(), wp_remote_post(), wp_remote_request(), wp_safe_remote_get(), wp_safe_remote_head(), wp_safe_remote_post(), wp_safe_remote_request()
php-stubs/wordpress-stubs#160 @lipemat WP_Widget::widget()
php-stubs/wordpress-stubs#209 @IanDelMar _get_list_table()
php-stubs/wordpress-stubs#302 @IanDelMar prep_atom_text_construct()
php-stubs/wordpress-stubs#306 @IanDelMar get_block_wrapper_attributes()
php-stubs/wordpress-stubs#311 @IanDelMar wp_get_inline_script_tag(), wp_get_script_tag()
php-stubs/wordpress-stubs#314 @IanDelMar wp_widget_rss_process()
php-stubs/wordpress-stubs#316 @IanDelMar image_size_input_fields()
php-stubs/wordpress-stubs#319 @IanDelMar get_compat_media_markup()
php-stubs/wordpress-stubs#321 @IanDelMar get_site_screen_help_tab_args()
php-stubs/wordpress-stubs#322 @IanDelMar wp_nav_menu_manage_columns()
php-stubs/wordpress-stubs#325 @IanDelMar get_available_post_statuses()
php-stubs/wordpress-stubs#355 @IanDelMar wp_unique_id_from_values()
php-stubs/wordpress-stubs#359 @IanDelMar get_default_link_to_edit(), wp_get_link_cats()
php-stubs/wordpress-stubs#362 @IanDelMar WP_Dependencies::$groups
php-stubs/wordpress-stubs#363 @IanDelMar wp_html_split()
php-stubs/wordpress-stubs#368 @IanDelMar register_nav_menus()
php-stubs/wordpress-stubs#376 @IanDelMar get_shortcode_tags_in_content()
php-stubs/wordpress-stubs#402 @IanDelMar wp_robots_max_image_preview_large(), wp_robots_no_robots(), wp_robots_noindex(), wp_robots_noindex_embeds(), wp_robots_noindex_search(), wp_robots_sensitive_page()

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

swissspidy, westonruter, marian1, mat-lipe, johnbillion, mociofiletto

Generated by Claude Code

claude added 2 commits October 5, 2026 15:46
Make http_response optional in the HTTP response shapes, since a pre_http_request filter can return a response without it. In wp_widget_rss_form(), make error optional and allow 'hidden' for the inputs, matching what the function handles. Add the documented plural, singular and ajax keys to the _get_list_table() shape, and drop two @phpstan-param tags that repeated the canonical ones.

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:58
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.

3 participants