diff --git a/src/wp-admin/includes/bookmark.php b/src/wp-admin/includes/bookmark.php index aae87b9abc60f..b7295237ddbde 100644 --- a/src/wp-admin/includes/bookmark.php +++ b/src/wp-admin/includes/bookmark.php @@ -12,6 +12,8 @@ * @since 2.0.0 * * @return int The link ID on success. The value 0 on failure. + * + * @phpstan-return non-negative-int */ function add_link() { return edit_link(); @@ -24,6 +26,8 @@ function add_link() { * * @param int $link_id Optional. ID of the link to edit. Default 0. * @return int The link ID on success. The value 0 on failure. + * + * @phpstan-return non-negative-int */ function edit_link( $link_id = 0 ) { if ( ! current_user_can( 'manage_links' ) ) { @@ -174,7 +178,7 @@ function get_link_to_edit( $link ) { * @param bool $wp_error Optional. Whether to return a WP_Error object on failure. Default false. * @return int|WP_Error The link ID on success. The value 0 or WP_Error on failure. * - * @phpstan-return ( $wp_error is false ? int : int|WP_Error ) + * @phpstan-return ( $wp_error is false ? non-negative-int : non-negative-int|WP_Error ) */ function wp_insert_link( $linkdata, $wp_error = false ) { global $wpdb; @@ -189,10 +193,14 @@ function wp_insert_link( $linkdata, $wp_error = false ) { $parsed_args = wp_parse_args( $linkdata, $defaults ); $parsed_args = wp_unslash( sanitize_bookmark( $parsed_args, 'db' ) ); - $link_id = $parsed_args['link_id']; + $link_id = (int) $parsed_args['link_id']; $link_name = $parsed_args['link_name']; $link_url = $parsed_args['link_url']; + if ( $link_id < 0 ) { + return 0; + } + $update = false; if ( ! empty( $link_id ) ) { $update = true; @@ -301,6 +309,8 @@ function wp_set_link_cats( $link_id = 0, $link_categories = array() ) { * * @param array $linkdata Link data to update. See wp_insert_link() for accepted arguments. * @return int The updated link ID on success. The value 0 on failure. + * + * @phpstan-return non-negative-int */ function wp_update_link( $linkdata ) { $link_id = (int) $linkdata['link_id']; diff --git a/src/wp-admin/includes/class-custom-image-header.php b/src/wp-admin/includes/class-custom-image-header.php index 367ba04cdc340..8981ce60f4c6e 100644 --- a/src/wp-admin/includes/class-custom-image-header.php +++ b/src/wp-admin/includes/class-custom-image-header.php @@ -310,6 +310,8 @@ public function process_default_headers() { * * @param string $type The header type. One of 'default' (for the Uploaded Images control) * or 'uploaded' (for the Uploaded Images control). + * + * @phpstan-param 'default'|'uploaded' $type */ public function show_header_selector( $type = 'default' ) { if ( 'default' === $type ) { diff --git a/src/wp-admin/includes/class-wp-importer.php b/src/wp-admin/includes/class-wp-importer.php index a1b76e388f04e..5205393458551 100644 --- a/src/wp-admin/includes/class-wp-importer.php +++ b/src/wp-admin/includes/class-wp-importer.php @@ -280,8 +280,8 @@ public function min_whitespace( $text ) { * * @since 3.0.0 * - * @global wpdb $wpdb WordPress database abstraction object. - * @global int[] $wp_actions Stores the number of times each action was triggered. + * @global wpdb $wpdb WordPress database abstraction object. + * @global array $wp_actions Stores the number of times each action was triggered. */ public function stop_the_insanity() { global $wpdb, $wp_actions; diff --git a/src/wp-admin/includes/taxonomy.php b/src/wp-admin/includes/taxonomy.php index dbdab1af4fab8..73677c9d7617d 100644 --- a/src/wp-admin/includes/taxonomy.php +++ b/src/wp-admin/includes/taxonomy.php @@ -119,7 +119,7 @@ function wp_create_categories( $categories, $post_id = 0 ) { * depending on param `$wp_error`. * * @phpstan-return ( - * $wp_error is false ? int : int|WP_Error + * $wp_error is false ? non-negative-int : non-negative-int|WP_Error * ) */ function wp_insert_category( $catarr, $wp_error = false ) { @@ -188,6 +188,8 @@ function wp_insert_category( $catarr, $wp_error = false ) { * * @param array $catarr The 'cat_ID' value is required. All other keys are optional. * @return int|false The ID number of the new or updated Category on success. Zero or FALSE on failure. + * + * @phpstan-return non-negative-int|false */ function wp_update_category( $catarr ) { $cat_id = (int) $catarr['cat_ID']; diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php index c01d9ffaf8ad0..1417ed9c0a3f4 100644 --- a/src/wp-admin/includes/template.php +++ b/src/wp-admin/includes/template.php @@ -1302,6 +1302,8 @@ function _get_plugin_from_callback( $callback ) { * Often this is the object that's the focus of the current screen, * for example a `WP_Post` or `WP_Comment` object. * @return int Number of meta_boxes. + * + * @phpstan-return non-negative-int */ function do_meta_boxes( $screen, $context, $data_object ) { global $wp_meta_boxes; diff --git a/src/wp-includes/bookmark.php b/src/wp-includes/bookmark.php index e1c8a6e522a00..f8314af645f49 100644 --- a/src/wp-includes/bookmark.php +++ b/src/wp-includes/bookmark.php @@ -80,16 +80,15 @@ function get_bookmark( $bookmark, $output = OBJECT, $filter = 'raw' ) { * @param string $field The name of the data field to return. * @param int $bookmark The bookmark ID to get field. * @param string $context Optional. The context of how the field will be used. Default 'display'. - * @return string|WP_Error + * @return mixed The sanitized field value, or an empty string if the bookmark or field does not exist. + * + * @phpstan-param 'link_id'|'link_url'|'link_name'|'link_image'|'link_target'|'link_description'|'link_visible'|'link_owner'|'link_rating'|'link_updated'|'link_rel'|'link_notes'|'link_rss'|'link_category' $field + * @phpstan-return ( $field is 'link_category' ? array|'' : ( $field is 'link_id'|'link_rating' ? int|'' : mixed ) ) */ function get_bookmark_field( $field, $bookmark, $context = 'display' ) { $bookmark = (int) $bookmark; $bookmark = get_bookmark( $bookmark ); - if ( is_wp_error( $bookmark ) ) { - return $bookmark; - } - if ( ! is_object( $bookmark ) ) { return ''; } @@ -403,6 +402,9 @@ function sanitize_bookmark( $bookmark, $context = 'display' ) { * @param string $context How to filter the field value. Accepts 'raw', 'edit', 'db', * 'display', 'attribute', or 'js'. Default 'display'. * @return mixed The filtered value. + * + * @phpstan-param 'link_id'|'link_url'|'link_name'|'link_image'|'link_target'|'link_description'|'link_visible'|'link_owner'|'link_rating'|'link_updated'|'link_rel'|'link_notes'|'link_rss'|'link_category' $field + * @phpstan-return ($field is 'link_category' ? array : ($field is 'link_id'|'link_rating' ? int : mixed)) */ function sanitize_bookmark_field( $field, $value, $bookmark_id, $context ) { $int_fields = array( 'link_id', 'link_rating' ); diff --git a/src/wp-includes/class-wp-locale.php b/src/wp-includes/class-wp-locale.php index b1e091e5196ca..9b7a35e71225e 100644 --- a/src/wp-includes/class-wp-locale.php +++ b/src/wp-includes/class-wp-locale.php @@ -118,7 +118,9 @@ class WP_Locale { * Default is 'words'. * * @since 6.2.0 - * @var string + * @var string|null + * + * @phpstan-var 'characters_excluding_spaces'|'characters_including_spaces'|'words'|null */ public $word_count_type; diff --git a/src/wp-includes/class-wp-user.php b/src/wp-includes/class-wp-user.php index b50a6790a2dc7..557845f756f43 100644 --- a/src/wp-includes/class-wp-user.php +++ b/src/wp-includes/class-wp-user.php @@ -58,6 +58,7 @@ class WP_User { * * @since 2.1.0 * @var int + * @phpstan-var non-negative-int */ public $ID = 0; @@ -181,11 +182,13 @@ public function __construct( $id = 0, $name = '', $site_id = 0 ) { * @param int $site_id Optional. The site ID to initialize for. */ public function init( $data, $site_id = 0 ) { - if ( ! isset( $data->ID ) ) { + $id = isset( $data->ID ) ? (int) $data->ID : 0; + if ( ! isset( $data->ID ) || $id < 0 ) { + $id = 0; $data->ID = 0; } $this->data = $data; - $this->ID = (int) $data->ID; + $this->ID = $id; $this->for_site( $site_id ); } @@ -364,7 +367,7 @@ public function __set( $key, $value ) { 'WP_User->ID' ) ); - $this->ID = (int) $value; + $this->ID = max( 0, (int) $value ); return; } diff --git a/src/wp-includes/class-wpdb.php b/src/wp-includes/class-wpdb.php index 869111efd0797..57e4ba4259ace 100644 --- a/src/wp-includes/class-wpdb.php +++ b/src/wp-includes/class-wpdb.php @@ -116,6 +116,7 @@ class wpdb { * @since 0.71 * * @var int + * @phpstan-var non-negative-int */ public $insert_id = 0; @@ -3098,6 +3099,8 @@ public function get_var( $query = null, $x = 0, $y = 0 ) { * ) * : null * ) + * + * @phpstan-param non-negative-int $y */ public function get_row( $query = null, $output = OBJECT, $y = 0 ) { $this->func_call = "\$db->get_row(\"$query\",$output,$y)"; diff --git a/src/wp-includes/comment.php b/src/wp-includes/comment.php index 9111358efc40b..488239f69201d 100644 --- a/src/wp-includes/comment.php +++ b/src/wp-includes/comment.php @@ -1899,6 +1899,8 @@ function wp_unspam_comment( $comment_id ) { * * @param int|WP_Comment $comment_id Comment ID or WP_Comment object * @return string|false Status might be 'trash', 'approved', 'unapproved', 'spam'. False on failure. + * + * @phpstan-return 'approved'|'spam'|'trash'|'unapproved'|false */ function wp_get_comment_status( $comment_id ) { $comment = get_comment( $comment_id ); diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 552c5f17c21a3..a31d2c8851ef8 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -2834,6 +2834,7 @@ function format_to_edit( $content, $rich_text = false ) { * @param int $threshold Digit places number needs to be to not have zeros added. * @return string Adds leading zeros to number if needed. * + * @phpstan-param non-negative-int $threshold * @phpstan-return ( * $threshold is 0 * ? lowercase-string&non-empty-string&numeric-string @@ -3005,6 +3006,8 @@ function urldecode_deep( $value ) { * @param string $email_address Email address. * @param int $hex_encoding Optional. Set to 1 to enable hex encoding. * @return string Converted email address. + * + * @phpstan-param 0|1 $hex_encoding */ function antispambot( $email_address, $hex_encoding = 0 ) { $obfuscated = ''; diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 114a9484f5659..170d9e4822618 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -1036,6 +1036,8 @@ function wp_get_http_headers( $url, $deprecated = false ) { * @global string $previousday The day of the previous post in the loop. * * @return int 1 when new day, 0 if not a new day. + * + * @phpstan-return 0|1 */ function is_new_day() { global $currentday, $previousday; @@ -4485,6 +4487,7 @@ function _wp_die_process_input( $message, $title = '', $args = array() ) { * greater than 0. Default 512. * @return string|false The JSON encoded string, or false if it cannot be encoded. * + * @phpstan-param positive-int $depth * @phpstan-return non-empty-string|false */ function wp_json_encode( $value, $flags = 0, $depth = 512 ) { @@ -6256,6 +6259,8 @@ function _doing_it_wrong( $function_name, $message, $version ) { * before passing to this function to avoid being stripped {@see wp_kses()}. * @param int $error_level Optional. The designated error type for this error. * Only works with E_USER family of constants. Default E_USER_NOTICE. + * + * @phpstan-param \E_USER_ERROR|\E_USER_WARNING|\E_USER_NOTICE|\E_USER_DEPRECATED $error_level */ function wp_trigger_error( $function_name, $message, $error_level = E_USER_NOTICE ) { /** @@ -6692,6 +6697,8 @@ function is_main_network( $network_id = null ) { * @since 4.3.0 * * @return int The ID of the main network. + * + * @phpstan-return non-negative-int */ function get_main_network_id() { if ( ! is_multisite() ) { @@ -6722,7 +6729,7 @@ function get_main_network_id() { * * @param int $main_network_id The ID of the main network. */ - return (int) apply_filters( 'get_main_network_id', $main_network_id ); + return max( 0, (int) apply_filters( 'get_main_network_id', $main_network_id ) ); } /** @@ -7203,6 +7210,8 @@ function __return_false() { // phpcs:ignore WordPress.NamingConventions.ValidFun * @since 3.0.0 * * @return int 0. + * + * @phpstan-return 0 */ function __return_zero() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore return 0; @@ -7216,6 +7225,8 @@ function __return_zero() { // phpcs:ignore WordPress.NamingConventions.ValidFunc * @since 3.0.0 * * @return array Empty array. + * + * @phpstan-return array{} */ function __return_empty_array() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore return array(); @@ -7244,6 +7255,8 @@ function __return_null() { // phpcs:ignore WordPress.NamingConventions.ValidFunc * @see __return_null() * * @return string Empty string. + * + * @phpstan-return '' */ function __return_empty_string() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore return ''; diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 1558a44189226..e3964cc5bffbd 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -11,6 +11,8 @@ * @since 4.4.0 * * @return string The HTTP protocol. Default: HTTP/1.0. + * + * @phpstan-return 'HTTP/1.0'|'HTTP/1.1'|'HTTP/2'|'HTTP/2.0'|'HTTP/3' */ function wp_get_server_protocol() { $protocol = $_SERVER['SERVER_PROTOCOL'] ?? ''; @@ -1509,6 +1511,8 @@ function absint( $maybeint ): int { * @global int $blog_id * * @return int Site ID. + * + * @phpstan-return non-negative-int */ function get_current_blog_id() { global $blog_id; @@ -1522,6 +1526,8 @@ function get_current_blog_id() { * @since 4.6.0 * * @return int The ID of the current network. + * + * @phpstan-return non-negative-int */ function get_current_network_id() { if ( ! is_multisite() ) { diff --git a/src/wp-includes/pluggable.php b/src/wp-includes/pluggable.php index d13769cf84afe..d749b89f26732 100644 --- a/src/wp-includes/pluggable.php +++ b/src/wp-includes/pluggable.php @@ -1375,7 +1375,7 @@ function auth_redirect() { * 2 if the nonce is valid and generated between 12-24 hours ago. * False if the nonce is invalid. Only possible when `$action` is -1, * as the function otherwise exits rather than returning false. - * @phpstan-return ( $action is -1 ? int|false : int ) + * @phpstan-return ( $action is -1 ? 1|2|false : 1|2 ) */ function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) { if ( -1 === $action ) { @@ -1422,7 +1422,7 @@ function check_admin_referer( $action = -1, $query_arg = '_wpnonce' ) { * 2 if the nonce is valid and generated between 12-24 hours ago. * False if the nonce is invalid. Only possible when `$stop` is false, * as the function otherwise exits rather than returning false. - * @phpstan-return ( $stop is true ? int : int|false ) + * @phpstan-return ( $stop is true ? 1|2 : 1|2|false ) */ function check_ajax_referer( $action = -1, $query_arg = false, $stop = true ) { if ( -1 === $action ) { @@ -2477,6 +2477,8 @@ function wp_nonce_tick( $action = -1 ) { * @return int|false 1 if the nonce is valid and generated between 0-12 hours ago, * 2 if the nonce is valid and generated between 12-24 hours ago. * False if the nonce is invalid. + * + * @phpstan-return 1|2|false */ function wp_verify_nonce( $nonce, $action = -1 ) { $nonce = (string) $nonce; diff --git a/src/wp-includes/plugin.php b/src/wp-includes/plugin.php index a3e9afacdb8dc..b9988567ffb05 100644 --- a/src/wp-includes/plugin.php +++ b/src/wp-includes/plugin.php @@ -28,10 +28,10 @@ /** @var WP_Hook[] $wp_filter */ global $wp_filter; -/** @var int[] $wp_actions */ +/** @var array $wp_actions */ global $wp_actions; -/** @var int[] $wp_filters */ +/** @var array $wp_filters */ global $wp_filters; /** @var string[] $wp_current_filter */ @@ -162,9 +162,9 @@ function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) * @since 6.0.0 Formalized the existing and already documented `...$args` parameter * by adding it to the function signature. * - * @global WP_Hook[] $wp_filter Stores all of the filters and actions. - * @global int[] $wp_filters Stores the number of times each filter was triggered. - * @global string[] $wp_current_filter Stores the list of current filters with the current one last. + * @global WP_Hook[] $wp_filter Stores all of the filters and actions. + * @global array $wp_filters Stores the number of times each filter was triggered. + * @global string[] $wp_current_filter Stores the list of current filters with the current one last. * * @param string $hook_name The name of the filter hook. * @param mixed $value The value to filter. @@ -219,9 +219,9 @@ function apply_filters( $hook_name, $value, ...$args ) { * @see apply_filters() This function is identical, but the arguments passed to the * functions hooked to `$hook_name` are supplied using an array. * - * @global WP_Hook[] $wp_filter Stores all of the filters and actions. - * @global int[] $wp_filters Stores the number of times each filter was triggered. - * @global string[] $wp_current_filter Stores the list of current filters with the current one last. + * @global WP_Hook[] $wp_filter Stores all of the filters and actions. + * @global array $wp_filters Stores the number of times each filter was triggered. + * @global string[] $wp_current_filter Stores the list of current filters with the current one last. * * @param string $hook_name The name of the filter hook. * @param non-empty-list $args The arguments supplied to the functions hooked to `$hook_name`. @@ -419,10 +419,12 @@ function doing_filter( $hook_name = null ) { * * @since 6.1.0 * - * @global int[] $wp_filters Stores the number of times each filter was triggered. + * @global array $wp_filters Stores the number of times each filter was triggered. * * @param string $hook_name The name of the filter hook. * @return int The number of times the filter hook has been applied. + * + * @phpstan-return non-negative-int */ function did_filter( $hook_name ) { global $wp_filters; @@ -487,9 +489,9 @@ function add_action( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) * @since 5.3.0 Formalized the existing and already documented `...$arg` parameter * by adding it to the function signature. * - * @global WP_Hook[] $wp_filter Stores all of the filters and actions. - * @global int[] $wp_actions Stores the number of times each action was triggered. - * @global string[] $wp_current_filter Stores the list of current filters with the current one last. + * @global WP_Hook[] $wp_filter Stores all of the filters and actions. + * @global array $wp_actions Stores the number of times each action was triggered. + * @global string[] $wp_current_filter Stores the list of current filters with the current one last. * * @param string $hook_name The name of the action to be executed. * @param mixed ...$arg Optional. Additional arguments which are passed on to the @@ -544,9 +546,9 @@ function do_action( $hook_name, ...$arg ) { * @see do_action() This function is identical, but the arguments passed to the * functions hooked to `$hook_name` are supplied using an array. * - * @global WP_Hook[] $wp_filter Stores all of the filters and actions. - * @global int[] $wp_actions Stores the number of times each action was triggered. - * @global string[] $wp_current_filter Stores the list of current filters with the current one last. + * @global WP_Hook[] $wp_filter Stores all of the filters and actions. + * @global array $wp_actions Stores the number of times each action was triggered. + * @global string[] $wp_current_filter Stores the list of current filters with the current one last. * * @param string $hook_name The name of the action to be executed. * @param list $args The arguments supplied to the functions hooked to `$hook_name`. @@ -698,10 +700,12 @@ function doing_action( $hook_name = null ) { * * @since 2.1.0 * - * @global int[] $wp_actions Stores the number of times each action was triggered. + * @global array $wp_actions Stores the number of times each action was triggered. * * @param string $hook_name The name of the action hook. * @return int The number of times the action hook has been fired. + * + * @phpstan-return non-negative-int */ function did_action( $hook_name ) { global $wp_actions; diff --git a/src/wp-includes/post-template.php b/src/wp-includes/post-template.php index 8a571495e96ca..20a5424e362a1 100644 --- a/src/wp-includes/post-template.php +++ b/src/wp-includes/post-template.php @@ -2056,6 +2056,8 @@ function wp_post_revision_title_expanded( $revision, $link = true ) { * * @param int|WP_Post $post Optional. Post ID or WP_Post object. Default is global $post. * @param string $type 'all' (default), 'revision' or 'autosave' + * + * @phpstan-param 'all'|'revision'|'autosave' $type */ function wp_list_post_revisions( $post = 0, $type = 'all' ) { $post = get_post( $post ); diff --git a/src/wp-includes/rest-api.php b/src/wp-includes/rest-api.php index 36b5b23de9f00..01cc25a798e32 100644 --- a/src/wp-includes/rest-api.php +++ b/src/wp-includes/rest-api.php @@ -1436,6 +1436,8 @@ function rest_get_date_with_gmt( $date, $is_utc = false ) { * @since 4.7.0 * * @return int 401 if the user is not logged in, 403 if the user is logged in. + * + * @phpstan-return 401|403 */ function rest_authorization_required_code() { return is_user_logged_in() ? 403 : 401; diff --git a/src/wp-includes/script-loader.php b/src/wp-includes/script-loader.php index 2d839af4a1a82..96cfe4482a68e 100644 --- a/src/wp-includes/script-loader.php +++ b/src/wp-includes/script-loader.php @@ -685,8 +685,11 @@ function wp_default_packages( $scripts ) { * * @param string $type The type of suffix to retrieve. * @return string The script suffix. + * + * @phpstan-return ''|'.min' */ function wp_scripts_get_suffix( $type = '' ) { + /** @var array{ suffix: ''|'.min', dev_suffix: ''|'.min' }|null $suffixes */ static $suffixes; if ( null === $suffixes ) { diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php index ea13e99271cf0..369766e54c1d6 100644 --- a/src/wp-includes/taxonomy.php +++ b/src/wp-includes/taxonomy.php @@ -2567,7 +2567,7 @@ function wp_get_object_terms( $object_ids, $taxonomies, $args = array() ) { * ... * } $args * @phpstan-return array{ - * term_id: int, + * term_id: non-negative-int, * term_taxonomy_id: int|numeric-string, * }|WP_Error */ @@ -2801,7 +2801,7 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { $wpdb->delete( $wpdb->terms, array( 'term_id' => $term_id ) ); $wpdb->delete( $wpdb->term_taxonomy, array( 'term_taxonomy_id' => $tt_id ) ); - $term_id = (int) $duplicate_term->term_id; + $term_id = max( 0, (int) $duplicate_term->term_id ); $tt_id = (int) $duplicate_term->term_taxonomy_id; clean_term_cache( $term_id, $taxonomy ); @@ -2857,7 +2857,7 @@ function wp_insert_term( $term, $taxonomy, $args = array() ) { * @param int $tt_id Term taxonomy ID. * @param array $args Arguments passed to wp_insert_term(). */ - $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id, $args ); + $term_id = max( 0, (int) apply_filters( 'term_id_filter', $term_id, $tt_id, $args ) ); clean_term_cache( $term_id, $taxonomy ); @@ -3379,7 +3379,7 @@ function wp_unique_term_slug( $slug, $term ) { * ... * } $args * @phpstan-return array{ - * term_id: int, + * term_id: non-negative-int, * term_taxonomy_id: int, * }|WP_Error */ @@ -3618,7 +3618,7 @@ function wp_update_term( $term_id, $taxonomy, $args = array() ) { do_action( "edit_{$taxonomy}", $term_id, $tt_id, $args ); /** This filter is documented in wp-includes/taxonomy.php */ - $term_id = apply_filters( 'term_id_filter', $term_id, $tt_id, $args ); + $term_id = max( 0, (int) apply_filters( 'term_id_filter', $term_id, $tt_id, $args ) ); clean_term_cache( $term_id, $taxonomy ); diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php index 1d0523cfc9a9a..8628c16f4f823 100644 --- a/src/wp-includes/user.php +++ b/src/wp-includes/user.php @@ -722,6 +722,8 @@ function count_many_users_posts( $users, $post_type = 'post', $public_only = fal * @since MU (3.0.0) * * @return int The current user's ID, or 0 if no user is logged in. + * + * @phpstan-return non-negative-int */ function get_current_user_id() { if ( ! function_exists( 'wp_get_current_user' ) ) { diff --git a/tests/phpstan/baselines/argument.type.neon b/tests/phpstan/baselines/argument.type.neon index 193d2aa64f6f3..dd4bb6b2f6e9b 100644 --- a/tests/phpstan/baselines/argument.type.neon +++ b/tests/phpstan/baselines/argument.type.neon @@ -829,7 +829,7 @@ parameters: count: 1 path: ../../../src/wp-includes/class-wp-widget.php - - message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\{post_author\: int, post_date\: string, post_date_gmt\: string, post_content\: string, post_title\: string, post_category\: array\\|string, post_status\: ''draft''\|''publish''\} given\.$#' + message: '#^Parameter \#1 \$postarr of function wp_insert_post expects array\{ID\?\: int, post_author\?\: int, post_date\?\: string, post_date_gmt\?\: string, post_content\?\: string, post_content_filtered\?\: string, post_title\?\: string, post_excerpt\?\: string, \.\.\., \.\.\.\}, array\{post_author\: int\<0, max\>, post_date\: string, post_date_gmt\: string, post_content\: string, post_title\: string, post_category\: array\\|string, post_status\: ''draft''\|''publish''\} given\.$#' identifier: argument.type count: 1 path: ../../../src/wp-includes/class-wp-xmlrpc-server.php @@ -1119,7 +1119,7 @@ parameters: count: 1 path: ../../../src/wp-includes/post-template.php - - message: '#^Parameter \#2 \$fallback of function sanitize_html_class expects string, int given\.$#' + message: '#^Parameter \#2 \$fallback of function sanitize_html_class expects string, int\<0, max\> given\.$#' identifier: argument.type count: 2 path: ../../../src/wp-includes/post-template.php @@ -1326,7 +1326,12 @@ parameters: - message: '#^Parameter \#2 \$fallback_title of function sanitize_title expects string, int given\.$#' identifier: argument.type - count: 2 + count: 1 + path: ../../../src/wp-includes/taxonomy.php + - + message: '#^Parameter \#2 \$fallback_title of function sanitize_title expects string, int\<0, max\> given\.$#' + identifier: argument.type + count: 1 path: ../../../src/wp-includes/taxonomy.php - message: '#^Parameter \#2 \$meta_id of function delete_metadata_by_mid expects int, string\|null given\.$#' diff --git a/tests/phpstan/baselines/nullCoalesce.property.neon b/tests/phpstan/baselines/nullCoalesce.property.neon index 431dd45d9d3c9..e4090e3bf9b3f 100644 --- a/tests/phpstan/baselines/nullCoalesce.property.neon +++ b/tests/phpstan/baselines/nullCoalesce.property.neon @@ -18,11 +18,6 @@ parameters: ignoreErrors: - - - message: '#^Property WP_Locale\:\:\$word_count_type \(string\) on left side of \?\? is not nullable\.$#' - identifier: nullCoalesce.property - count: 1 - path: ../../../src/wp-includes/class-wp-locale.php - message: '#^Property WP_Query\:\:\$max_num_pages \(int\) on left side of \?\? is not nullable\.$#' identifier: nullCoalesce.property diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index aaf0c8aa57b5e..9579b2a663f21 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -422,10 +422,10 @@ protected function reset__SERVER() { * Stores $wp_filter, $wp_actions, $wp_filters, and $wp_current_filter * on a class variable so they can be restored on tear_down() using _restore_hooks(). * - * @global array $wp_filter All of the filters and actions. - * @global array $wp_actions The number of times each action was triggered. - * @global array $wp_filters The number of times each filter was triggered. - * @global array $wp_current_filter The list of current filters with the current one last. + * @global array $wp_filter All of the filters and actions. + * @global array $wp_actions The number of times each action was triggered. + * @global array $wp_filters The number of times each filter was triggered. + * @global array $wp_current_filter The list of current filters with the current one last. */ protected function _backup_hooks() { self::$hooks_saved['wp_filter'] = array(); @@ -445,10 +445,10 @@ protected function _backup_hooks() { * Restores the hook-related globals to their state at set_up() * so that future tests aren't affected by hooks set during this last test. * - * @global array $wp_filter All of the filters and actions. - * @global array $wp_actions The number of times each action was triggered. - * @global array $wp_filters The number of times each filter was triggered. - * @global array $wp_current_filter The list of current filters with the current one last. + * @global array $wp_filter All of the filters and actions. + * @global array $wp_actions The number of times each action was triggered. + * @global array $wp_filters The number of times each filter was triggered. + * @global array $wp_current_filter The list of current filters with the current one last. */ protected function _restore_hooks() { if ( isset( self::$hooks_saved['wp_filter'] ) ) { diff --git a/tests/phpunit/includes/utils.php b/tests/phpunit/includes/utils.php index a99e640ced4b9..272a1e55050e3 100644 --- a/tests/phpunit/includes/utils.php +++ b/tests/phpunit/includes/utils.php @@ -101,7 +101,7 @@ public function reset(): void { /** * @since UT (3.7.0) * - * @global array $wp_actions + * @global array $wp_actions * @return string|false */ public function current_filter() { diff --git a/tests/phpunit/tests/user.php b/tests/phpunit/tests/user.php index 2d4ed48f73c35..b48bee1e27cc4 100644 --- a/tests/phpunit/tests/user.php +++ b/tests/phpunit/tests/user.php @@ -378,6 +378,26 @@ public function test_construction() { $this->assertSame( $user->user_login, $user7->user_login ); } + /** + * Tests that {@see WP_User::init()} keeps the ID property and the ID in the data in agreement. + * + * @covers WP_User::init + */ + public function test_init_normalizes_invalid_id_in_data(): void { + $negative = new WP_User( (object) array( 'ID' => -1 ) ); + $this->assertSame( 0, $negative->ID, 'Expected a negative ID to be replaced with 0.' ); + $this->assertSame( 0, $negative->data->ID, 'Expected the data to agree with the ID property.' ); + $this->assertFalse( $negative->exists(), 'Expected a user with a negative ID not to exist.' ); + + $missing = new WP_User( (object) array( 'user_login' => 'nobody' ) ); + $this->assertSame( 0, $missing->ID, 'Expected a missing ID to be 0.' ); + $this->assertSame( 0, $missing->data->ID, 'Expected the data to be given an ID of 0.' ); + + $user_id = self::factory()->user->create(); + $author = new WP_User( $user_id ); + $this->assertSame( (string) $user_id, $author->data->ID, 'Expected the numeric-string ID of a database row to be preserved.' ); + } + public function test_get() { $user = new WP_User( self::$author_id ); $this->assertSame( 'author_login', $user->get( 'user_login' ) );