[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