[wp-trac] [WordPress Trac] #64704: Code Modernization: Replace void in PHPDoc union return types with null in various core files
WordPress Trac
noreply at wordpress.org
Mon Feb 23 09:40:58 UTC 2026
#64704: Code Modernization: Replace void in PHPDoc union return types with null in
various core files
--------------------------+-----------------------------
Reporter: apermo | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: General | Version: 3.9
Severity: normal | Keywords:
Focuses: |
--------------------------+-----------------------------
Following up on [#64694] which fixed `paginate_links()`, this ticket
addresses 34 instances across various `wp-includes` files where `@return`
annotations incorrectly use `void` as part of a union type.
The `|void` pattern in these files spans from WordPress 3.9
(`unregister_default_headers()`) through WordPress 6.8 (`handle_error()`,
`WP_Widget::form()`), with two instances on unreleased trunk (`wp_die()`,
`print_late_styles()`). The bulk originated in WordPress 4.3 via [32568] —
"Use `void` instead of `null` where appropriate when pipe-delimiting
`@return` types".
=== Why this matters ===
In PHP's type system, `void` means "the function does not return a value"
and '''cannot''' be part of a union type (this is a compile error in PHP
8.0+). When a function uses bare `return;` or falls off the end without
returning, the actual runtime value is `null`. Therefore `@return
Type|void` should be `@return Type|null`.
This affects:
* Static analysis tools (PHPStan, Psalm)
* IDE autocompletion and type inference
* Developer expectations about return values
=== Backward Compatibility ===
This is a safe, non-breaking change. As demonstrated by @westonruter in
the [https://github.com/WordPress/wordpress-
develop/pull/10999#pullrequestreview-3839142928 #64694 PR review],
`return;` and `return null;` are identical at runtime across every PHP
version from 4.3.0 through 8.5.3: https://3v4l.org/3KQC8
=== Proposed Changes ===
For each instance:
1. Update the `@return` annotation to replace `void` with `null` (or
remove `|void` where the function always returns a typed value)
2. Change bare `return;` statements to `return null;`
3. Update description text to say "null" instead of "void" / "nothing"
=== Affected Functions ===
||= File =||= Function =||= Line =||= Current =||= Recommendation =||=
Since =||
|| `functions.php` || `wp_ext2type()` || 2997 || `string|void` ||
`string|null` || 4.3 ||
|| `functions.php` || `wp_die()` || 3767 || `never|void` || No change
needed || trunk ||
|| `functions.php` || `wp_update_php_annotation()` || 8556 ||
`string|void` || `string|null` || 6.4 ||
|| `capabilities.php` || `add_role()` || 1133 || `WP_Role|void` ||
`WP_Role|null` || 6.1 ||
|| `class-wp-roles.php` || `add_role()` || 174 || `WP_Role|void` ||
`WP_Role|null` || 6.1 ||
|| `formatting.php` || `sanitize_hex_color()` || 6235 || `string|void` ||
`string|null` || 4.6 ||
|| `canonical.php` || `redirect_canonical()` || 40 || `string|void` ||
`string|null` || 4.3 ||
|| `revision.php` || `wp_save_post_revision()` || 128 ||
`int|WP_Error|void` || `int|WP_Error|null` || 4.3 ||
|| `class-wp-scripts.php` || `print_scripts_l10n()` || 204 ||
`bool|string|void` || `bool|string|null` || 6.1 ||
|| `class-wp-scripts.php` || `print_extra_script()` || 220 ||
`bool|string|void` || `bool|string|null` || 6.1 ||
|| `script-loader.php` || `print_late_styles()` || 2374 || `string[]|void`
|| `string[]|null` || trunk ||
|| `ms-functions.php` || `get_active_blog_for_user()` || 43 ||
`WP_Site|void` || `WP_Site|null` || 4.5 ||
|| `ms-functions.php` || `add_existing_user_to_blog()` || 2272 ||
`true|WP_Error|void` || `true|WP_Error|null` || 5.4 ||
|| `class-wp-block-type.php` || `__get()` || 361 ||
`string|string[]|null|void` || `string|string[]|null` || 6.2 ||
|| `class-wp-plugin-dependencies.php` || `get_dependency_api_data()` ||
646 || `array|void` || `array|null` || 6.5 ||
|| `class-wp-recovery-mode.php` || `handle_error()` || 164 ||
`true|WP_Error|void` || `true|WP_Error|null` || 6.8 ||
|| `class-wp-xmlrpc-server.php` || `escape()` || 356 || `string|void` ||
`string|null` || 4.3 ||
|| `class-wp-theme-json.php` || `set_spacing_sizes()` || 4270 ||
`null|void` || `null` (redundant) || 6.1 ||
|| `class-wp-style-engine-css-rules-store.php` || `get_store()` || 54 ||
`WP_Style_Engine_CSS_Rules_Store|void` || `...|null` || 6.1 ||
|| `class-wp-style-engine-css-rules-store.php` || `add_rule()` || 131 ||
`WP_Style_Engine_CSS_Rule|void` || `...|null` || 6.3 ||
|| `widgets.php` || `wp_widget_description()` || 449 || `string|void` ||
`string|null` || 4.4 ||
|| `widgets.php` || `wp_sidebar_description()` || 474 || `string|void` ||
`string|null` || 4.4 ||
|| `class-wp-widget.php` || `WP_Widget::form()` || 141 || `string|void` ||
`string` (void incorrect) || 6.8 ||
|| `theme.php` || `remove_theme_support()` || 3064 || `bool|void` ||
`bool` (void incorrect) || 4.3 ||
|| `theme.php` || `unregister_default_headers()` || 1623 || `bool|void` ||
`bool|null` || 3.9 ||
|| `media.php` || `wp_audio_shortcode()` || 3392 || `string|void` ||
`string|null` || 4.3 ||
|| `media.php` || `wp_video_shortcode()` || 3617 || `string|void` ||
`string|null` || 4.3 ||
|| `media.php` || `wp_prepare_attachment_for_js()` || 4467 || `array|void`
|| `array|null` || 5.6 ||
|| `post.php` || `get_post_custom_keys()` || 2853 || `array|void` ||
`array|null` || 4.4 ||
|| `post.php` || `wp_trash_post_comments()` || 4218 || `mixed|void` ||
`int|false|null` || 4.4 ||
|| `post.php` || `wp_untrash_post_comments()` || 4279 || `true|void` ||
`true|null` || 4.4 ||
|| `comment.php` || `wp_update_comment_count()` || 2809 || `bool|void` ||
`bool|null` || 4.5 ||
|| `comment.php` || `trackback()` || 3316 || `int|false|void` ||
`int|false|null` || 4.4 ||
=== Notes on special cases ===
* **`wp_die()`**: `never|void` is arguably correct — terminates when
`exit=true`, returns when `exit=false`. No change needed.
* **`escape()`**: Returns string for scalar, modifies by-reference for
array. Unusual pattern.
* **`set_spacing_sizes()`**: `null|void` is redundant — both mean "no
value". Should be just `null`.
* **`wp_trash_post_comments()`**: Returns `int|false` on success, bare
return on failure. Should be `int|false|null`.
* **`wp_untrash_post_comments()`**: Returns true, bare return, or falls
off end. Possible bug at end of function.
* **`unregister_default_headers()`**: Returns bool for single header, void
for array input (recursive). Oldest instance (WP 3.9).
See [#64694] for prior art.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/64704>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list