Skip to content

Posts, Post Types: Improve static analysis types for post retrieval functions#12485

Closed
westonruter wants to merge 1 commit into
WordPress:trunkfrom
westonruter:update/post-function-phpstan
Closed

Posts, Post Types: Improve static analysis types for post retrieval functions#12485
westonruter wants to merge 1 commit into
WordPress:trunkfrom
westonruter:update/post-function-phpstan

Conversation

@westonruter

@westonruter westonruter commented Jul 11, 2026

Copy link
Copy Markdown
Member

✅ Committed in r62694 (99fc144)


This improves the static analysis types for the post retrieval functions, so that the type of the returned value can be determined from the requested $output.

Conditional return types

The @phpstan-return types of get_post(), get_page(), get_page_by_path(), get_children(), and wp_get_recent_posts() are narrowed so that the ARRAY_A and ARRAY_N outputs are typed as non-empty-array. This is consistent with WP_Post::to_array(), which always returns at least the object's declared properties and therefore can never return an empty array.

wp_get_post_revision() previously had no conditional return type at all, so its return value was seen as WP_Post|array|null regardless of the requested $output. Describing it precisely resolves 30 pre-existing PHPStan errors in its callers — mostly Cannot access property $ID on array|WP_Post — across wp_restore_post_revision(), wp_delete_post_revision(), wp_xmlrpc_server::wp_restoreRevision(), and wp-admin/revision.php.

Note that non-empty-array is a PHPStan-specific type which phpDocumentor does not understand, so the plain @return tag for WP_Post::to_array() continues to use array<string, mixed>, with the narrower type supplied via @phpstan-return.

Behavior changes

Two small runtime changes were needed to support the above:

  • trackback_url_list() now bails when get_post() returns null. Previously, calling it with an invalid post ID would fall through to $postdata['post_excerpt'], emitting "Trying to access array offset on value of type null" warnings.
  • wp_get_recent_posts() now collects its ARRAY_A output in a separate $posts variable rather than overwriting the WP_Post objects in $results in place. Reassigning array values over the elements of $results left the variable holding a mix of WP_Post objects and arrays partway through the loop, so static analysis could only ever infer array<WP_Post|array<string, mixed>> for it. Building up a dedicated variable guarantees that everything it contains is an array. The returned value is unchanged.

A note on list<>

It may be tempting to type these array returns as list<…> rather than array<int, …>, but that would not be sound. The the_posts, posts_results, and posts_pre_query filters allow plugins to return arbitrary arrays — an array_filter() in a the_posts callback is enough to produce a sparse numeric array — and core never re-indexes the result. array<int, …> is therefore the honest type. (get_children() is keyed by post ID in any case.)

Testing instructions

No behavior change is expected beyond the removal of the PHP warnings noted above. The existing tests pass:

npm run test:php -- tests/phpunit/tests/post.php
npm run test:php -- tests/phpunit/tests/post/revisions.php

Static analysis was verified to introduce no new errors on the changed files (and to fix 30 existing ones).

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

Use of AI Tools

AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.8
Used for: Reviewing the change for correctness and consistency, extending it to wp_get_post_revision() and WP_Post::to_array(), verifying with PHPStan/PHPCS/PHPUnit, and drafting the commit message and this description. All changes were reviewed and edited 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.

…unctions.

Narrow the conditional `@phpstan-return` types of `get_post()`, `get_page()`, `get_page_by_path()`, `get_children()`, `wp_get_recent_posts()`, and `wp_get_post_revision()` so that the `ARRAY_A` and `ARRAY_N` outputs are typed as `non-empty-array`, consistent with `WP_Post::to_array()`, which always returns at least the object's properties. Prior to this, `wp_get_post_revision()` had no conditional return type at all, so its return value was seen as `WP_Post|array|null` regardless of the requested `$output`. Describing it precisely resolves a number of pre-existing `Cannot access property $ID on array|WP_Post` errors in its callers, including `wp_restore_post_revision()`, `wp_delete_post_revision()`, `wp_xmlrpc_server::wp_restoreRevision()`, and `wp-admin/revision.php`.

Additionally:

* Guard against `get_post()` returning `null` in `trackback_url_list()`, which would otherwise emit "Trying to access array offset on value of type null" warnings when called with an invalid post ID.
* Collect the array output of `wp_get_recent_posts()` in a separate `$posts` variable rather than overwriting the `WP_Post` objects in `$results` in place. Reassigning array values over the elements of `$results` left the variable holding a mix of `WP_Post` objects and arrays partway through the loop, so static analysis could only infer `array<WP_Post|array<string, mixed>>` for it. Building up a dedicated variable guarantees that everything it contains is an array, allowing the `ARRAY_A` branch of the return type to be expressed precisely. This does not change the returned value.

Note that `non-empty-array` is a PHPStan-specific type which phpDocumentor does not understand, so the plain `@return` tag for `WP_Post::to_array()` continues to use `array<string, mixed>` and the narrower type is supplied via `@phpstan-return`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@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 westonruter.

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

Comment thread src/wp-includes/post.php
Comment on lines -4493 to +4498
return $results ? $results : array();
return $posts;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

No need for a ternary here, because if $results is falsy, then it was an empty string to begin with. So passing through an empty $posts is the same.

Comment thread src/wp-includes/post.php
Comment on lines +6154 to +6156
if ( ! $postdata ) {
return;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This prevents a PHP error where the post_excerpt key could be attempted to be read from a null value on $postdata if the lookup failed.

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

pento pushed a commit that referenced this pull request Jul 11, 2026
Narrow the conditional `@phpstan-return` types of `get_post()`, `get_page()`, `get_page_by_path()`, `get_children()`, and `wp_get_recent_posts()` so that the `ARRAY_A` and `ARRAY_N` outputs are typed as `non-empty-array` instead of `array`. This is consistent with `WP_Post::to_array()`, which always returns at least the object's declared properties and so can never yield an empty array. Slight refactoring is done on `wp_get_recent_posts()` to support static analysis.

Additionally, `wp_get_post_revision()` had no conditional return type at all, so its result was seen as `WP_Post|array|null` regardless of the requested `$output`. Describing it precisely resolves 30 pre-existing `Cannot access property $ID on array|WP_Post` errors in its callers.

Furthermore, `trackback_url_list()` now bails when `get_post()` returns `null`. Previously it fell through to `$postdata['post_excerpt']`, emitting "Trying to access array offset on value of type null" warnings when called with an invalid post ID.

Developed in #12485.
Follow-up to r62648.

See #64898.


git-svn-id: https://develop.svn.wordpress.org/trunk@62694 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Jul 11, 2026
Narrow the conditional `@phpstan-return` types of `get_post()`, `get_page()`, `get_page_by_path()`, `get_children()`, and `wp_get_recent_posts()` so that the `ARRAY_A` and `ARRAY_N` outputs are typed as `non-empty-array` instead of `array`. This is consistent with `WP_Post::to_array()`, which always returns at least the object's declared properties and so can never yield an empty array. Slight refactoring is done on `wp_get_recent_posts()` to support static analysis.

Additionally, `wp_get_post_revision()` had no conditional return type at all, so its result was seen as `WP_Post|array|null` regardless of the requested `$output`. Describing it precisely resolves 30 pre-existing `Cannot access property $ID on array|WP_Post` errors in its callers.

Furthermore, `trackback_url_list()` now bails when `get_post()` returns `null`. Previously it fell through to `$postdata['post_excerpt']`, emitting "Trying to access array offset on value of type null" warnings when called with an invalid post ID.

Developed in WordPress/wordpress-develop#12485.
Follow-up to r62648.

See #64898.

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


git-svn-id: http://core.svn.wordpress.org/trunk@61978 1a063a9b-81f0-0310-95a4-ce76da25c4cd
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.

1 participant