Docs: Bring the PHPStan types from the WordPress stubs' function map into core - #13530
Draft
swissspidy wants to merge 24 commits into
Draft
swissspidy wants to merge 24 commits into
swissspidy wants to merge 24 commits into
Conversation
…e functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. This moves the entries for `functions.php` and `load.php` in, along with `wpdb::prepare()`, which takes a `literal-string`, and `WP_Object_Cache::delete()`, whose second parameter is deprecated. Three entries are adjusted rather than taken as they are. `size_format()` is stated in terms of what core's own parameter allows, `get_tag_regex()` does return an empty string for an empty tag, and neither `wp_is_uuid()` nor the `$version` it validates is as narrow as the map has it: the function calls `_doing_it_wrong()`, so it is not pure. `wp_upload_bits()` documents its second parameter as deprecated and unused, and the one call in core that passes it an empty string rather than null is corrected. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. Among them the conditional returns of `addslashes_gpc()` and `zeroise()`, the template that keeps `urlencode_deep()` and `sanitize_category()` returning what they were given, and `trailingslashit()` returning a `non-falsy-string`, which is what the extension szepeviktor/phpstan-wordpress carries for it does and all it does. The `@phpstan-param-out` the map writes for `wp_parse_str()` is left out: PHPStan reports core's own assignment to `$result` as not matching it, and the narrower type is not worth an error inside the function it describes. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…tions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `wp_get_word_count_type()` and `WP_Locale::$word_count_type` are one of three known values, `load_plugin_textdomain()` takes a deprecated second parameter, and `WP_Translations::translate()` returns null only when it is given null. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…k API. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. A hook name is a `non-empty-string`, `has_filter()` and `has_action()` return a bool only when asked about the hook rather than a callback, `did_action()` and `did_filter()` count from zero, and the callbacks `register_activation_hook()` and the menu page functions accept are described as the callables they are. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. The response array `wp_remote_get()` and its siblings return, which `WP_Http::request()` documents in prose and every caller reaches into, becomes the array shape it is: headers, body, response code and message, cookies, filename and the `WP_HTTP_Requests_Response` behind it. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…category functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `get_term()`, `get_term_by()`, `get_category()` and `get_bookmark()`-style `$output` parameters resolve to the object, the associative array or the list they are asked for, and `sanitize_term()` and `sanitize_term_field()` keep the type they were given. The map states `get_tags()` as returning a `numeric-string` for `fields => count`, which core's own `get_terms()` type does not allow, so that branch is left out. `get_term_to_edit()` documents the `WP_Term` that `sanitize_term()` now says it returns, rather than the `int|string|null` it never returns. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
… functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `register_post_type()` takes a lowercase name, `wp_insert_post()` and `wp_update_post()` return a `WP_Error` only when asked to, `wp_is_post_revision()` never returns a negative ID, and `WP_Query` reads two properties and calls one method that are not declared on it. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `wp_get_comment_status()` returns one of four statuses or false, and `wp_update_comment()` returns a `WP_Error` only when asked to. The map states its success value as `0|1`, where the method behind it, `wpdb::update()`, returns the number of rows it changed, so that is what the type says. `trackback_url()` is left as it is: its `$deprecated_echo` parameter is already documented and already carries the `@phpstan-ignore` that the conditional return needs. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…le functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `get_user_by()` cannot find a user by a non-positive ID, `wp_create_nonce()` and `wp_hash()` return a lowercase hash, the nonce functions take `-1` as well as a string action, and `wp_mail()`, `wp_rand()` and `wp_generate_password()` are marked impure so PHPStan does not treat repeated calls as equal. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…unctions and list tables. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `_get_list-table()` returns an instance of the list table it is asked for, which is what the template the map writes for it expresses, and `WP_List_Table::set_pagination_args()` takes the three keys it reads. The map has `_get_list_table()` accepting only a screen name in `$args`, where core passes a `WP_Screen` in three places and the function hands it to `convert_to_screen()`, which accepts one; the type says so. `iframe_header()` documents its second parameter as deprecated, and the one call in core still passing it is corrected, as is the `add_option()` call whose deprecated argument is null rather than the empty string it is documented as. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ion, meta and cron functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. The cron functions take a list of arguments rather than an arbitrary array, and return a `WP_Error` only when asked to; `get_site_option()`, `get_user_option()` and `register_meta()` take a deprecated parameter that is documented but not typed. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…tisite functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `get_sites()` returns a count, a list of IDs or a list of sites according to the arguments it is given, and `switch_to_blog()` takes a deprecated second parameter. `switch_to_blog()` takes the site ID as an int, and the one call in core passing `WP_Site::$blog_id`, which is documented as a string, now casts it. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ns and classes. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `WP_Widget` becomes generic in the shape of the instance it stores, so a widget that documents its own settings array has `widget()`, `update()` and `form()` typed in terms of it, and `register_widget()` takes the class name or instance it documents. The map has `wp_widget_rss_form()` receiving a bool `error` where the function and `wp_widget_rss_process()` both give it `string|false`, so the shape says that. `WP_Widget_Factory::register()` instantiates a widget class with no arguments, which `WP_Widget`'s own constructor does not accept; the call carries an `@phpstan-ignore` saying why, since every widget class declares a constructor of its own. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…mplate tags. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `WP_Theme`'s magic properties are declared, and the headers its `ArrayAccess` implementation answers for become a named type, so reading an undefined offset resolves to null rather than to mixed. The map states `single_month_title()` as returning void in its display branch, where the function returns nothing at all on one path; the type says null, which is what a caller receives. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…nd style functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `wp_scripts_get_suffix()` returns one of two strings, the script and style tag builders take attributes whose values may be `true`, and `WP_Dependencies::query()` resolves to a `_WP_Dependency` for the two statuses that return one and to a bool for the rest. That last type is written without the template the map uses for it, which PHPStan reports as not being referenced by any parameter, and `wp_get_inline_script_tag()` does return an empty string when the processor rejects the script it is given. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `WP_Block_List` declares the offsets its `ArrayAccess` implementation takes, `get_block_wrapper_attributes()` returns a non-empty string once it is given attributes, and `block_version()` answers 0 for empty content. `block_version()` is not marked pure: it calls `has_blocks()`, which PHPStan cannot see through. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…unctions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. A shortcode tag is a `non-empty-string`, `get_shortcode_regex()` and `get_shortcode_atts_regex()` return a pattern that is never empty, and `get_shortcode_tags_in_content()` returns a list of tags. The callable signature the map writes for `add_shortcode()` is left out. It has the callback receiving an array of attributes, where `do_shortcode_tag()` passes an empty string for a shortcode written without any, and core registers five callbacks that document it the way the function actually calls them. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. The markup builders in the media modal return a string that is never empty, and `image_size_input_fields()` returns the shape the media frame reads. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `register_nav_menus()` takes a map of location slugs to descriptions, and `wp_nav_menu_manage_columns()` returns the fixed set of columns it builds. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ctions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `get_bookmark()` resolves to the object, the associative array or the list its `$output` asks for, the field getters take the field names they accept, and `wp_get_link_cats()` returns an empty array for an empty link ID. `get_bookmark_field()` is typed without the `WP_Error` the map gives it, which the function returns only through `get_bookmark()` in a branch it does not reach and PHPStan reports as unused. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…write functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `get_permalink()` and its variants return false only when they are not handed a post, `path_join()` and `add_feed()` return a string that is never empty, and `wp_internal_hosts()` returns lowercase hosts. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…ities functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `rest_ensure_response()` returns a `WP_Error` only when it is given one, `rest_sanitize_boolean()` keeps a bool it is given and resolves the documented false-y values, `rest_authorization_required_code()` returns one of two codes, and a route namespace, a route and an ability name are each a `non-falsy-string`. The generics the map writes for `WP_REST_Request` are not taken: they make the class generic in the shape of its parameters, which reports every `$request['key'] = ...` in core as an error. That is worth another look upstream before core carries it. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `prep_atom_text_construct()` returns the two-element list its callers destructure, the `$deprecated` parameters of `get_wp_title_rss()` and `wp_title_rss()` are typed as the entity they default to, and the robots filters take and return the directive map they filter. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
…functions. php-stubs/wordpress-stubs, which every plugin's static analysis reads in place of core, applies a map of PHPStan types on top of core's docblocks when it generates those stubs. Each entry is a type core can carry itself, where it serves core's own analysis as well as the stubs generated from it. `is_term()` resolves the same way `term_exists()` does, and `addslashes_gpc()` returns a string only when it is given one. These files are not analyzed themselves, so the types are for the callers that remain. See #65817. Co-authored-by: Pascal Birchler <pascal.birchler@gmail.com> Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
Test using WordPress PlaygroundThe 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
For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Splits the second half of #13437 out, as asked there: that PR keeps the szepeviktor/phpstan-wordpress dependency and the extensions core loads from it, and this one carries the docblock work, one commit per component so it can be reviewed and committed a piece at a time.
Trac ticket: https://core.trac.wordpress.org/ticket/65817
What this is
php-stubs/wordpress-stubs is what every plugin's static analysis reads in place of core. When it generates those stubs it applies a map of PHPStan types on top of core's own docblocks: conditional returns, narrowed parameters, templates, purity, and a few class-level annotations. That map,
functionMap.php, is 361 entries long, and each entry is a type core could carry itself.Carrying them here rather than there means one place instead of two: core's own analysis gets the same types the ecosystem already has, the stubs need no map entry once they are regenerated from a release carrying them, and szepeviktor/phpstan-wordpress needs no extension for them either. That package has already gone this way — its 2.x branch dropped its own return type extensions for
get_post(),get_terms(),current_time(),wp_die(),is_wp_error()and more in favour of the map.How it was produced
A script read the map and inserted each entry into the matching core docblock as
@phpstan-param,@phpstan-return,@phpstan-template,@phpstan-pure/-impure,@phpstan-assert-if-true,@phpstan-property-read,@phpstan-typeor@final, formatted the way core already writes them: multi-line conditionals, no leading backslash on global class names, tags in a consistent order. Every generated tag was then read through, and the analysis was run after each commit.290 of the 361 symbols gained tags, across 77 files. The rest were left alone:
get_posts(),get_terms(),wp_die(),wp_get_upload_dir(),the_title()and others already carry an equivalent@phpstan-return, a template, or hash notation thatHashNotationVisitorderives a shape from.block_core_*andrender_block_core_*live in the plugin, and the map entry belongs there.check_admin_referer(),check_ajax_referer()andsanitize_post_field()because the map's parameter narrowing contradicts the conditional return core already has.Where the map is wrong for core
A type that is right for a plugin calling core is not always right inside core. The analysis surfaced the entries below, each checked against the code rather than reshaped until the error went away:
add_shortcode()array<string>do_shortcode_tag()passes''for a shortcode written without attributes, and core's own five callbacks document it that way_get_list_table()array{screen?: string}WP_Screen, whichconvert_to_screen()acceptsget_tag_regex(),wp_get_inline_script_tag()non-falsy-string''on a documented pathwp_update_comment()0|1|falsewpdb::update()returns, a row countsize_format(),get_tags(),single_month_title()get_terms()type allowsget_html_split_regex(),block_version(),wp_is_uuid()@phpstan-purehas_blocks(), and_doing_it_wrong()respectivelywp_widget_rss_form()error: boolwp_widget_rss_process()both give itstring|falseWP_Dependencies::query()wp_parse_str()@phpstan-param-out$resultdoes not match itOne entry is not adopted at all: the generics the map writes for
WP_REST_Request, which make the class generic in the shape of its parameters. They report every$request['key'] = …in core as an error, about twenty of them across the REST controllers. That is worth another look upstream before core carries it.Calls this exposed
Five calls turn out to pass something other than what the function documents. They are corrected in the commit that adds the type showing it, so each commit stands on its own:
wp_upload_bits()andadd_option()are given the value their deprecated parameter documents, an empty string and null respectively, rather than the other way round.iframe_header()is no longer passed a deprecated argument.switch_to_blog()is givenWP_Site::$blog_idas the int it wants, rather than the string that property is documented as.get_term_to_edit()documents theWP_Termthatsanitize_term()returns, rather than theint|string|nullit never returns.WP_Widget_Factory::register()instantiates a widget class with no arguments, whichWP_Widget's own constructor does not accept; that call carries an@phpstan-ignoreexplaining why, since every widget class declares a constructor of its own.Effect on the analysis
composer run phpstanis green at every one of the commits below, and each regenerates the baselines it changes, so the branch can be committed a piece at a time without leaving trunk red.Over the whole branch the baselines lose 53 entries and gain 8 (seven of those the same error reworded, one new). Two baseline files reach zero and are deleted:
parameter.defaultValue.neonandmethod.nonObject.neon. The one genuinely new entry is aWP_Term_Query::populate_terms()assignment on whatget_term()may return,WP_Errorincluded, which is worth a look of its own.The commits
One per component, each green on its own, so they can be committed one at a time:
General: Add the PHPStan types the stubs carry for the general-purpose functions.Formatting: Add the PHPStan types the stubs carry for the formatting functions.I18N: Add the PHPStan types the stubs carry for the localization functions.Plugins: Add the PHPStan types the stubs carry for the plugin and hook API.HTTP API: Add the PHPStan types the stubs carry for the request functions.Taxonomy: Add the PHPStan types the stubs carry for the taxonomy and category functions.Posts, Post Types: Add the PHPStan types the stubs carry for the post functions.Comments: Add the PHPStan types the stubs carry for the comment functions.Users: Add the PHPStan types the stubs carry for the user and pluggable functions.Administration: Add the PHPStan types the stubs carry for the admin functions and list tables.Options, Meta APIs: Add the PHPStan types the stubs carry for the option, meta and cron functions.Networks and Sites: Add the PHPStan types the stubs carry for the multisite functions.Widgets: Add the PHPStan types the stubs carry for the widget functions and classes.Themes: Add the PHPStan types the stubs carry for WP_Theme and the template tags.Script Loader: Add the PHPStan types the stubs carry for the script and style functions.Editor: Add the PHPStan types the stubs carry for the block functions.Shortcodes: Add the PHPStan types the stubs carry for the shortcode functions.Media: Add the PHPStan types the stubs carry for the media functions.Menus: Add the PHPStan types the stubs carry for the nav menu functions.Bookmarks: Add the PHPStan types the stubs carry for the bookmark functions.Permalinks: Add the PHPStan types the stubs carry for the link and rewrite functions.REST API: Add the PHPStan types the stubs carry for the REST and abilities functions.Feeds: Add the PHPStan types the stubs carry for the feed and robots functions.Deprecated: Add the PHPStan types the stubs carry for the deprecated functions.Follow-ups
WP_REST_Requestgenerics need rework upstream before core can carry them.WP_Term_Query::populate_terms()assigns a property on whateverget_term()returns,WP_Errorincluded; newly baselined here.block_core_*andrender_block_core_*belong in Gutenberg.functionMap.phpcan be dropped as the stubs are regenerated.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 5
Used for: The script that inserted the map entries into core's docblocks, the review of its output against the map and the code, the adjustments and call fixes listed above, splitting the work into the commits above, and drafting this description. Every commit was checked with
composer run phpstanandphpcs. Directed and reviewed by me.Session transcript: https://claude.ai/code/session_013QJ9RphuttTR7cS5G4mU21
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_013QJ9RphuttTR7cS5G4mU21
Generated by Claude Code