diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/pluggable.php --- a/wp/wp-includes/pluggable.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/pluggable.php Fri Sep 05 18:40:08 2025 +0200 @@ -30,7 +30,7 @@ // If `$id` matches the current user, there is nothing to do. if ( isset( $current_user ) && ( $current_user instanceof WP_User ) - && ( $id == $current_user->ID ) + && ( $id === $current_user->ID ) && ( null !== $id ) ) { return $current_user; @@ -53,7 +53,7 @@ if ( ! function_exists( 'wp_get_current_user' ) ) : /** - * Retrieve the current user object. + * Retrieves the current user object. * * Will set the current user, if the current user is not set. The current user * will be set to the logged-in person. If no user is logged-in, then it will @@ -73,7 +73,7 @@ if ( ! function_exists( 'get_userdata' ) ) : /** - * Retrieve user info by user ID. + * Retrieves user info by user ID. * * @since 0.71 * @@ -87,11 +87,10 @@ if ( ! function_exists( 'get_user_by' ) ) : /** - * Retrieve user info by a given field + * Retrieves user info by a given field. * * @since 2.8.0 * @since 4.4.0 Added 'ID' as an alias of 'id' for the `$field` parameter. - * @since 5.8.0 Returns the global `$current_user` if it's the user being fetched. * * @global WP_User $current_user The current user object which holds the user data. * @@ -100,19 +99,13 @@ * @return WP_User|false WP_User object on success, false on failure. */ function get_user_by( $field, $value ) { - global $current_user; - $userdata = WP_User::get_data_by( $field, $value ); if ( ! $userdata ) { return false; } - if ( $current_user instanceof WP_User && $current_user->ID === (int) $userdata->ID ) { - return $current_user; - } - - $user = new WP_User; + $user = new WP_User(); $user->init( $userdata ); return $user; @@ -121,7 +114,7 @@ if ( ! function_exists( 'cache_users' ) ) : /** - * Retrieve info for user lists to prevent multiple queries by get_userdata() + * Retrieves info for user lists to prevent multiple queries by get_userdata(). * * @since 3.0.0 * @@ -132,6 +125,8 @@ function cache_users( $user_ids ) { global $wpdb; + update_meta_cache( 'user', $user_ids ); + $clean = _get_non_cached_ids( $user_ids, 'users' ); if ( empty( $clean ) ) { @@ -142,12 +137,9 @@ $users = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($list)" ); - $ids = array(); foreach ( $users as $user ) { update_user_caches( $user ); - $ids[] = $user->ID; } - update_meta_cache( 'user', $ids ); } endif; @@ -275,8 +267,10 @@ $headers = array(); } else { if ( ! is_array( $headers ) ) { - // Explode the headers out, so this function can take - // both string headers and an array of headers. + /* + * Explode the headers out, so this function can take + * both string headers and an array of headers. + */ $tempheaders = explode( "\n", str_replace( "\r\n", "\n", $headers ) ); } else { $tempheaders = $headers; @@ -287,7 +281,7 @@ if ( ! empty( $tempheaders ) ) { // Iterate through the raw headers. foreach ( (array) $tempheaders as $header ) { - if ( strpos( $header, ':' ) === false ) { + if ( ! str_contains( $header, ':' ) ) { if ( false !== stripos( $header, 'boundary=' ) ) { $parts = preg_split( '/boundary=/i', trim( $header ) ); $boundary = trim( str_replace( array( "'", '"' ), '', $parts[1] ) ); @@ -308,7 +302,7 @@ if ( false !== $bracket_pos ) { // Text before the bracketed email is the "From" name. if ( $bracket_pos > 0 ) { - $from_name = substr( $content, 0, $bracket_pos - 1 ); + $from_name = substr( $content, 0, $bracket_pos ); $from_name = str_replace( '"', '', $from_name ); $from_name = trim( $from_name ); } @@ -323,7 +317,7 @@ } break; case 'content-type': - if ( strpos( $content, ';' ) !== false ) { + if ( str_contains( $content, ';' ) ) { list( $type, $charset_content ) = explode( ';', $content ); $content_type = trim( $type ); if ( false !== stripos( $charset_content, 'charset=' ) ) { @@ -361,6 +355,8 @@ $phpmailer->clearAttachments(); $phpmailer->clearCustomHeaders(); $phpmailer->clearReplyTos(); + $phpmailer->Body = ''; + $phpmailer->AltBody = ''; // Set "From" name and email. @@ -382,7 +378,7 @@ $from_email = 'wordpress@'; if ( null !== $sitename ) { - if ( 'www.' === substr( $sitename, 0, 4 ) ) { + if ( str_starts_with( $sitename, 'www.' ) ) { $sitename = substr( $sitename, 4 ); } @@ -438,7 +434,7 @@ $recipient_name = ''; if ( preg_match( '/(.*)<(.+)>/', $address, $matches ) ) { - if ( count( $matches ) == 3 ) { + if ( count( $matches ) === 3 ) { $recipient_name = $matches[1]; $address = $matches[2]; } @@ -469,7 +465,7 @@ // Set Content-Type and charset. - // If we don't have a content-type from the input headers. + // If we don't have a Content-Type from the input headers. if ( ! isset( $content_type ) ) { $content_type = 'text/plain'; } @@ -523,9 +519,11 @@ } if ( ! empty( $attachments ) ) { - foreach ( $attachments as $attachment ) { + foreach ( $attachments as $filename => $attachment ) { + $filename = is_string( $filename ) ? $filename : ''; + try { - $phpmailer->addAttachment( $attachment ); + $phpmailer->addAttachment( $attachment, $filename ); } catch ( PHPMailer\PHPMailer\Exception $e ) { continue; } @@ -589,7 +587,7 @@ if ( ! function_exists( 'wp_authenticate' ) ) : /** - * Authenticate a user, confirming the login credentials are valid. + * Authenticates a user, confirming the login credentials are valid. * * @since 2.5.0 * @since 4.5.0 `$username` now accepts an email address. @@ -615,14 +613,16 @@ * @param null|WP_User|WP_Error $user WP_User if the user is authenticated. * WP_Error or null otherwise. * @param string $username Username or email address. - * @param string $password User password + * @param string $password User password. */ $user = apply_filters( 'authenticate', null, $username, $password ); - if ( null == $user ) { - // TODO: What should the error message be? (Or would these even happen?) - // Only needed if all authentication handlers fail to return anything. - $user = new WP_Error( 'authentication_failed', __( 'Error: Invalid username, email address or incorrect password.' ) ); + if ( null === $user || false === $user ) { + /* + * TODO: What should the error message be? (Or would these even happen?) + * Only needed if all authentication handlers fail to return anything. + */ + $user = new WP_Error( 'authentication_failed', __( 'Error: Invalid username, email address or incorrect password.' ) ); } $ignore_codes = array( 'empty_username', 'empty_password' ); @@ -649,7 +649,7 @@ if ( ! function_exists( 'wp_logout' ) ) : /** - * Log the current user out. + * Logs the current user out. * * @since 2.5.0 */ @@ -896,6 +896,7 @@ * Parses a cookie into its components. * * @since 2.7.0 + * @since 4.0.0 The `$token` element was added to the return value. * * @param string $cookie Authentication cookie. * @param string $scheme Optional. The cookie scheme to use: 'auth', 'secure_auth', or 'logged_in'. @@ -1073,17 +1074,26 @@ * Allows preventing auth cookies from actually being sent to the client. * * @since 4.7.4 + * @since 6.2.0 The `$expire`, `$expiration`, `$user_id`, `$scheme`, and `$token` parameters were added. * - * @param bool $send Whether to send auth cookies to the client. + * @param bool $send Whether to send auth cookies to the client. Default true. + * @param int $expire The time the login grace period expires as a UNIX timestamp. + * Default is 12 hours past the cookie's expiration time. Zero when clearing cookies. + * @param int $expiration The time when the logged-in authentication cookie expires as a UNIX timestamp. + * Default is 14 days from now. Zero when clearing cookies. + * @param int $user_id User ID. Zero when clearing cookies. + * @param string $scheme Authentication scheme. Values include 'auth' or 'secure_auth'. + * Empty string when clearing cookies. + * @param string $token User's session token to use for this cookie. Empty string when clearing cookies. */ - if ( ! apply_filters( 'send_auth_cookies', true ) ) { + if ( ! apply_filters( 'send_auth_cookies', true, $expire, $expiration, $user_id, $scheme, $token ) ) { return; } setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true ); setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true ); setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true ); - if ( COOKIEPATH != SITECOOKIEPATH ) { + if ( COOKIEPATH !== SITECOOKIEPATH ) { setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true ); } } @@ -1104,7 +1114,7 @@ do_action( 'clear_auth_cookie' ); /** This filter is documented in wp-includes/pluggable.php */ - if ( ! apply_filters( 'send_auth_cookies', true ) ) { + if ( ! apply_filters( 'send_auth_cookies', true, 0, 0, 0, '', '' ) ) { return; } @@ -1180,8 +1190,8 @@ $secure = apply_filters( 'secure_auth_redirect', $secure ); // If https is required and request is http, redirect. - if ( $secure && ! is_ssl() && false !== strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { - if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) { + if ( $secure && ! is_ssl() && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { + if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); exit; } else { @@ -1211,8 +1221,8 @@ do_action( 'auth_redirect', $user_id ); // If the user wants ssl but the session is not ssl, redirect. - if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && false !== strpos( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { - if ( 0 === strpos( $_SERVER['REQUEST_URI'], 'http' ) ) { + if ( ! $secure && get_user_option( 'use_ssl', $user_id ) && str_contains( $_SERVER['REQUEST_URI'], 'wp-admin' ) ) { + if ( str_starts_with( $_SERVER['REQUEST_URI'], 'http' ) ) { wp_redirect( set_url_scheme( $_SERVER['REQUEST_URI'], 'https' ) ); exit; } else { @@ -1227,7 +1237,11 @@ // The cookie is no good, so force login. nocache_headers(); - $redirect = ( strpos( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) ? wp_get_referer() : set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + if ( str_contains( $_SERVER['REQUEST_URI'], '/options.php' ) && wp_get_referer() ) { + $redirect = wp_get_referer(); + } else { + $redirect = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); + } $login_url = wp_login_url( $redirect, true ); @@ -1241,7 +1255,7 @@ * Ensures intent by verifying that a user was referred from another admin page with the correct security nonce. * * This function ensures the user intends to perform a given action, which helps protect against clickjacking style - * attacks. It verifies intent, not authorisation, therefore it does not verify the user's capabilities. This should + * attacks. It verifies intent, not authorization, therefore it does not verify the user's capabilities. This should * be performed with `current_user_can()` or similar. * * If the nonce value is invalid, the function will exit with an "Are You Sure?" style message. @@ -1275,7 +1289,7 @@ */ do_action( 'check_admin_referer', $action, $result ); - if ( ! $result && ! ( -1 === $action && strpos( $referer, $adminurl ) === 0 ) ) { + if ( ! $result && ! ( -1 === $action && str_starts_with( $referer, $adminurl ) ) ) { wp_nonce_ays( $action ); die(); } @@ -1294,14 +1308,14 @@ * @param false|string $query_arg Optional. Key to check for the nonce in `$_REQUEST` (since 2.5). If false, * `$_REQUEST` values will be evaluated for '_ajax_nonce', and '_wpnonce' * (in that order). Default false. - * @param bool $die Optional. Whether to die early when the nonce cannot be verified. + * @param bool $stop Optional. Whether to stop early when the nonce cannot be verified. * Default true. * @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. */ - function check_ajax_referer( $action = -1, $query_arg = false, $die = true ) { - if ( -1 == $action ) { + function check_ajax_referer( $action = -1, $query_arg = false, $stop = true ) { + if ( -1 === $action ) { _doing_it_wrong( __FUNCTION__, __( 'You should specify an action to be verified by using the first parameter.' ), '4.7.0' ); } @@ -1328,7 +1342,7 @@ */ do_action( 'check_ajax_referer', $action, $result ); - if ( $die && false === $result ) { + if ( $stop && false === $result ) { if ( wp_doing_ajax() ) { wp_die( -1, 403 ); } else { @@ -1351,7 +1365,7 @@ * exit; * * Exiting can also be selectively manipulated by using wp_redirect() as a conditional - * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_location'} filters: + * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters: * * if ( wp_redirect( $url ) ) { * exit; @@ -1363,10 +1377,10 @@ * * @global bool $is_IIS * - * @param string $location The path or URL to redirect to. - * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). - * @param string $x_redirect_by Optional. The application doing the redirect. Default 'WordPress'. - * @return bool False if the redirect was cancelled, true otherwise. + * @param string $location The path or URL to redirect to. + * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). + * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'. + * @return bool False if the redirect was canceled, true otherwise. */ function wp_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) { global $is_IIS; @@ -1412,9 +1426,9 @@ * * @since 5.1.0 * - * @param string $x_redirect_by The application doing the redirect. - * @param int $status Status code to use. - * @param string $location The path to redirect to. + * @param string|false $x_redirect_by The application doing the redirect or false to omit the header. + * @param int $status Status code to use. + * @param string $location The path to redirect to. */ $x_redirect_by = apply_filters( 'x_redirect_by', $x_redirect_by, $status, $location ); if ( is_string( $x_redirect_by ) ) { @@ -1462,7 +1476,7 @@ } /** - * URL encode UTF-8 characters in a URL. + * URL encodes UTF-8 characters in a URL. * * @ignore * @since 4.2.0 @@ -1497,7 +1511,7 @@ * exit; * * Exiting can also be selectively manipulated by using wp_safe_redirect() as a conditional - * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_location'} filters: + * in conjunction with the {@see 'wp_redirect'} and {@see 'wp_redirect_status'} filters: * * if ( wp_safe_redirect( $url ) ) { * exit; @@ -1506,10 +1520,10 @@ * @since 2.3.0 * @since 5.1.0 The return value from wp_redirect() is now passed on, and the `$x_redirect_by` parameter was added. * - * @param string $location The path or URL to redirect to. - * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). - * @param string $x_redirect_by Optional. The application doing the redirect. Default 'WordPress'. - * @return bool False if the redirect was cancelled, true otherwise. + * @param string $location The path or URL to redirect to. + * @param int $status Optional. HTTP response status code to use. Default '302' (Moved Temporarily). + * @param string|false $x_redirect_by Optional. The application doing the redirect or false to omit. Default 'WordPress'. + * @return bool False if the redirect was canceled, true otherwise. */ function wp_safe_redirect( $location, $status = 302, $x_redirect_by = 'WordPress' ) { @@ -1524,7 +1538,9 @@ * @param string $fallback_url The fallback URL to use by default. * @param int $status The HTTP response status code to use. */ - $location = wp_validate_redirect( $location, apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ) ); + $fallback_url = apply_filters( 'wp_safe_redirect_fallback', admin_url(), $status ); + + $location = wp_validate_redirect( $location, $fallback_url ); return wp_redirect( $location, $status, $x_redirect_by ); } @@ -1538,23 +1554,25 @@ * path. A plugin can therefore set or remove allowed host(s) to or from the * list. * - * If the host is not allowed, then the redirect is to $default supplied + * If the host is not allowed, then the redirect is to $fallback_url supplied. * * @since 2.8.1 * - * @param string $location The redirect to validate - * @param string $default The value to return if $location is not allowed - * @return string redirect-sanitized URL + * @param string $location The redirect to validate. + * @param string $fallback_url The value to return if $location is not allowed. + * @return string Redirect-sanitized URL. */ - function wp_validate_redirect( $location, $default = '' ) { + function wp_validate_redirect( $location, $fallback_url = '' ) { $location = wp_sanitize_redirect( trim( $location, " \t\n\r\0\x08\x0B" ) ); // Browsers will assume 'http' is your protocol, and will obey a redirect to a URL starting with '//'. - if ( '//' === substr( $location, 0, 2 ) ) { + if ( str_starts_with( $location, '//' ) ) { $location = 'http:' . $location; } - // In PHP 5 parse_url() may fail if the URL query part contains 'http://'. - // See https://bugs.php.net/bug.php?id=38143 + /* + * In PHP 5 parse_url() may fail if the URL query part contains 'http://'. + * See https://bugs.php.net/bug.php?id=38143 + */ $cut = strpos( $location, '?' ); $test = $cut ? substr( $location, 0, $cut ) : $location; @@ -1562,12 +1580,12 @@ // Give up if malformed URL. if ( false === $lp ) { - return $default; + return $fallback_url; } // Allow only 'http' and 'https' schemes. No 'data:', etc. if ( isset( $lp['scheme'] ) && ! ( 'http' === $lp['scheme'] || 'https' === $lp['scheme'] ) ) { - return $default; + return $fallback_url; } if ( ! isset( $lp['host'] ) && ! empty( $lp['path'] ) && '/' !== $lp['path'][0] ) { @@ -1579,16 +1597,18 @@ $location = '/' . ltrim( $path . '/', '/' ) . $location; } - // Reject if certain components are set but host is not. - // This catches URLs like https:host.com for which parse_url() does not set the host field. + /* + * Reject if certain components are set but host is not. + * This catches URLs like https:host.com for which parse_url() does not set the host field. + */ if ( ! isset( $lp['host'] ) && ( isset( $lp['scheme'] ) || isset( $lp['user'] ) || isset( $lp['pass'] ) || isset( $lp['port'] ) ) ) { - return $default; + return $fallback_url; } // Reject malformed components parse_url() can return on odd inputs. foreach ( array( 'user', 'pass', 'host' ) as $component ) { if ( isset( $lp[ $component ] ) && strpbrk( $lp[ $component ], ':/?#@' ) ) { - return $default; + return $fallback_url; } } @@ -1605,7 +1625,7 @@ $allowed_hosts = (array) apply_filters( 'allowed_redirect_hosts', array( $wpp['host'] ), isset( $lp['host'] ) ? $lp['host'] : '' ); if ( isset( $lp['host'] ) && ( ! in_array( $lp['host'], $allowed_hosts, true ) && strtolower( $wpp['host'] ) !== $lp['host'] ) ) { - $location = $default; + $location = $fallback_url; } return $location; @@ -1614,12 +1634,12 @@ if ( ! function_exists( 'wp_notify_postauthor' ) ) : /** - * Notify an author (and/or others) of a comment/trackback/pingback on a post. + * Notifies an author (and/or others) of a comment/trackback/pingback on a post. * * @since 1.0.0 * * @param int|WP_Comment $comment_id Comment ID or WP_Comment object. - * @param string $deprecated Not used + * @param string $deprecated Not used. * @return bool True on completion. False if no email addresses were specified. */ function wp_notify_postauthor( $comment_id, $deprecated = null ) { @@ -1678,12 +1698,12 @@ $notify_author = apply_filters( 'comment_notification_notify_author', false, $comment->comment_ID ); // The comment was left by the author. - if ( $author && ! $notify_author && $comment->user_id == $post->post_author ) { + if ( $author && ! $notify_author && (int) $comment->user_id === (int) $post->post_author ) { unset( $emails[ $author->user_email ] ); } // The author moderated a comment on their own post. - if ( $author && ! $notify_author && get_current_user_id() == $post->post_author ) { + if ( $author && ! $notify_author && get_current_user_id() === (int) $post->post_author ) { unset( $emails[ $author->user_email ] ); } @@ -1706,8 +1726,10 @@ $comment_author_domain = gethostbyaddr( $comment->comment_author_IP ); } - // The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). - // We want to reverse this for the plain text arena of emails. + /* + * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). + * We want to reverse this for the plain text arena of emails. + */ $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $comment_content = wp_specialchars_decode( $comment->comment_content ); @@ -1867,7 +1889,7 @@ * @since 4.4.0 * * @param bool $maybe_notify Whether to notify blog moderator. - * @param int $comment_ID The id of the comment for the notification. + * @param int $comment_id The ID of the comment for the notification. */ $maybe_notify = apply_filters( 'notify_moderator', $maybe_notify, $comment_id ); @@ -1895,8 +1917,10 @@ $comments_waiting = $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE comment_approved = '0'" ); - // The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). - // We want to reverse this for the plain text arena of emails. + /* + * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). + * We want to reverse this for the plain text arena of emails. + */ $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $comment_content = wp_specialchars_decode( $comment->comment_content ); @@ -2027,20 +2051,24 @@ if ( ! function_exists( 'wp_password_change_notification' ) ) : /** - * Notify the blog admin of a user changing password, normally via email. + * Notifies the blog admin of a user changing password, normally via email. * * @since 2.7.0 * * @param WP_User $user User object. */ function wp_password_change_notification( $user ) { - // Send a copy of password change notification to the admin, - // but check to see if it's the admin whose password we're changing, and skip this. + /* + * Send a copy of password change notification to the admin, + * but check to see if it's the admin whose password we're changing, and skip this. + */ if ( 0 !== strcasecmp( $user->user_email, get_option( 'admin_email' ) ) ) { /* translators: %s: User name. */ $message = sprintf( __( 'Password changed for user: %s' ), $user->user_login ) . "\r\n"; - // The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). - // We want to reverse this for the plain text arena of emails. + /* + * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). + * We want to reverse this for the plain text arena of emails. + */ $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $wp_password_change_notification_email = array( @@ -2081,7 +2109,7 @@ if ( ! function_exists( 'wp_new_user_notification' ) ) : /** - * Email login credentials to a newly-registered user. + * Emails login credentials to a newly-registered user. * * A new user registration notification is also sent to admin email. * @@ -2107,11 +2135,23 @@ $user = get_userdata( $user_id ); - // The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). - // We want to reverse this for the plain text arena of emails. + /* + * The blogname option is escaped with esc_html() on the way into the database in sanitize_option(). + * We want to reverse this for the plain text arena of emails. + */ $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); - if ( 'user' !== $notify ) { + /** + * Filters whether the admin is notified of a new user registration. + * + * @since 6.1.0 + * + * @param bool $send Whether to send the email. Default true. + * @param WP_User $user User object for new user. + */ + $send_notification_to_admin = apply_filters( 'wp_send_new_user_notification_to_admin', true, $user ); + + if ( 'user' !== $notify && true === $send_notification_to_admin ) { $switched_locale = switch_to_locale( get_locale() ); /* translators: %s: Site title. */ @@ -2159,8 +2199,18 @@ } } + /** + * Filters whether the user is notified of their new user registration. + * + * @since 6.1.0 + * + * @param bool $send Whether to send the email. Default true. + * @param WP_User $user User object for new user. + */ + $send_notification_to_user = apply_filters( 'wp_send_new_user_notification_to_user', true, $user ); + // `$deprecated` was pre-4.3 `$plaintext_pass`. An empty `$plaintext_pass` didn't sent a user notification. - if ( 'admin' === $notify || ( empty( $deprecated ) && empty( $notify ) ) ) { + if ( 'admin' === $notify || true !== $send_notification_to_user || ( empty( $deprecated ) && empty( $notify ) ) ) { return; } @@ -2169,7 +2219,7 @@ return; } - $switched_locale = switch_to_locale( get_user_locale( $user ) ); + $switched_locale = switch_to_user_locale( $user_id ); /* translators: %s: User login. */ $message = sprintf( __( 'Username: %s' ), $user->user_login ) . "\r\n\r\n"; @@ -2225,18 +2275,22 @@ * updated, e.g. by autosave. * * @since 2.5.0 + * @since 6.1.0 Added `$action` argument. * + * @param string|int $action Optional. The nonce action. Default -1. * @return float Float value rounded up to the next highest integer. */ - function wp_nonce_tick() { + function wp_nonce_tick( $action = -1 ) { /** * Filters the lifespan of nonces in seconds. * * @since 2.5.0 + * @since 6.1.0 Added `$action` argument to allow for more targeted filters. * - * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day. + * @param int $lifespan Lifespan of nonces in seconds. Default 86,400 seconds, or one day. + * @param string|int $action The nonce action, or -1 if none was provided. */ - $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS ); + $nonce_life = apply_filters( 'nonce_life', DAY_IN_SECONDS, $action ); return ceil( time() / ( $nonce_life / 2 ) ); } @@ -2266,8 +2320,8 @@ * * @since 3.5.0 * - * @param int $uid ID of the nonce-owning user. - * @param string $action The nonce action. + * @param int $uid ID of the nonce-owning user. + * @param string|int $action The nonce action, or -1 if none was provided. */ $uid = apply_filters( 'nonce_user_logged_out', $uid, $action ); } @@ -2277,7 +2331,7 @@ } $token = wp_get_session_token(); - $i = wp_nonce_tick(); + $i = wp_nonce_tick( $action ); // Nonce generated 0-12 hours ago. $expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); @@ -2314,7 +2368,7 @@ * and window of time. * * @since 2.0.3 - * @since 4.0.0 Session tokens were integrated with nonce creation + * @since 4.0.0 Session tokens were integrated with nonce creation. * * @param string|int $action Scalar value to add context to the nonce. * @return string The token. @@ -2328,7 +2382,7 @@ } $token = wp_get_session_token(); - $i = wp_nonce_tick(); + $i = wp_nonce_tick( $action ); return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); } @@ -2364,7 +2418,7 @@ * * @link https://api.wordpress.org/secret-key/1.1/salt/ Create secrets for wp-config.php * - * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce) + * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). * @return string Salt value */ function wp_salt( $scheme = 'auth' ) { @@ -2384,7 +2438,17 @@ static $duplicated_keys; if ( null === $duplicated_keys ) { - $duplicated_keys = array( 'put your unique phrase here' => true ); + $duplicated_keys = array( + 'put your unique phrase here' => true, + ); + + /* + * translators: This string should only be translated if wp-config-sample.php is localized. + * You can check the localized release package or + * https://i18n.svn.wordpress.org//branches//dist/wp-config-sample.php + */ + $duplicated_keys[ __( 'put your unique phrase here' ) ] = true; + foreach ( array( 'AUTH', 'SECURE_AUTH', 'LOGGED_IN', 'NONCE', 'SECRET' ) as $first ) { foreach ( array( 'KEY', 'SALT' ) as $second ) { if ( ! defined( "{$first}_{$second}" ) ) { @@ -2440,13 +2504,13 @@ if ( ! function_exists( 'wp_hash' ) ) : /** - * Get hash of given string. + * Gets hash of given string. * * @since 2.0.3 * - * @param string $data Plain text to hash - * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce) - * @return string Hash of $data + * @param string $data Plain text to hash. + * @param string $scheme Authentication scheme (auth, secure_auth, logged_in, nonce). + * @return string Hash of $data. */ function wp_hash( $data, $scheme = 'auth' ) { $salt = wp_salt( $scheme ); @@ -2457,17 +2521,17 @@ if ( ! function_exists( 'wp_hash_password' ) ) : /** - * Create a hash (encrypt) of a plain text password. + * Creates a hash (encrypt) of a plain text password. * * For integration with other applications, this function can be overwritten to * instead use the other package password checking algorithm. * * @since 2.5.0 * - * @global PasswordHash $wp_hasher PHPass object + * @global PasswordHash $wp_hasher PHPass object. * - * @param string $password Plain text user password to hash - * @return string The hash string of the password + * @param string $password Plain text user password to hash. + * @return string The hash string of the password. */ function wp_hash_password( $password ) { global $wp_hasher; @@ -2497,13 +2561,13 @@ * @since 2.5.0 * * @global PasswordHash $wp_hasher PHPass object used for checking the password - * against the $hash + $password + * against the $hash + $password. * @uses PasswordHash::CheckPassword * - * @param string $password Plaintext user's password + * @param string $password Plaintext user's password. * @param string $hash Hash of the user's password to check against. * @param string|int $user_id Optional. User ID. - * @return bool False, if the $password does not match the hashed password + * @return bool False, if the $password does not match the hashed password. */ function wp_check_password( $password, $hash, $user_id = '' ) { global $wp_hasher; @@ -2530,8 +2594,10 @@ return apply_filters( 'check_password', $check, $password, $hash, $user_id ); } - // If the stored hash is longer than an MD5, - // presume the new style phpass portable hash. + /* + * If the stored hash is longer than an MD5, + * presume the new style phpass portable hash. + */ if ( empty( $wp_hasher ) ) { require_once ABSPATH . WPINC . '/class-phpass.php'; // By default, use the portable hash from phpass. @@ -2549,7 +2615,7 @@ /** * Generates a random password drawn from the defined set of characters. * - * Uses wp_rand() is used to create passwords with far less predictability + * Uses wp_rand() to create passwords with far less predictability * than similar native PHP functions like `rand()` or `mt_rand()`. * * @since 2.5.0 @@ -2592,24 +2658,37 @@ if ( ! function_exists( 'wp_rand' ) ) : /** - * Generates a random number. + * Generates a random non-negative number. * * @since 2.6.2 * @since 4.4.0 Uses PHP7 random_int() or the random_compat library if available. + * @since 6.1.0 Returns zero instead of a random number if both `$min` and `$max` are zero. * * @global string $rnd_value * - * @param int $min Lower limit for the generated number - * @param int $max Upper limit for the generated number - * @return int A random number between min and max + * @param int $min Optional. Lower limit for the generated number. + * Accepts positive integers or zero. Defaults to 0. + * @param int $max Optional. Upper limit for the generated number. + * Accepts positive integers. Defaults to 4294967295. + * @return int A random non-negative number between min and max. */ - function wp_rand( $min = 0, $max = 0 ) { + function wp_rand( $min = null, $max = null ) { global $rnd_value; - // Some misconfigured 32-bit environments (Entropy PHP, for example) - // truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats. + /* + * Some misconfigured 32-bit environments (Entropy PHP, for example) + * truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats. + */ $max_random_number = 3000000000 === 2147483647 ? (float) '4294967295' : 4294967295; // 4294967295 = 0xffffffff + if ( null === $min ) { + $min = 0; + } + + if ( null === $max ) { + $max = $max_random_number; + } + // We only handle ints, floats are truncated to their integer value. $min = (int) $min; $max = (int) $max; @@ -2618,10 +2697,9 @@ static $use_random_int_functionality = true; if ( $use_random_int_functionality ) { try { - $_max = ( 0 != $max ) ? $max : $max_random_number; // wp_rand() can accept arguments in either order, PHP cannot. - $_max = max( $min, $_max ); - $_min = min( $min, $_max ); + $_max = max( $min, $max ); + $_min = min( $min, $max ); $val = random_int( $_min, $_max ); if ( false !== $val ) { return absint( $val ); @@ -2635,8 +2713,10 @@ } } - // Reset $rnd_value after 14 uses. - // 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value. + /* + * Reset $rnd_value after 14 uses. + * 32 (md5) + 40 (sha1) + 40 (sha1) / 8 = 14 random numbers from $rnd_value. + */ if ( strlen( $rnd_value ) < 8 ) { if ( defined( 'WP_SETUP_CONFIG' ) ) { static $seed = ''; @@ -2661,9 +2741,7 @@ $value = abs( hexdec( $value ) ); // Reduce the value to be within the min - max range. - if ( 0 != $max ) { - $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 ); - } + $value = $min + ( $max - $min + 1 ) * $value / ( $max_random_number + 1 ); return abs( (int) $value ); } @@ -2684,8 +2762,8 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param string $password The plaintext new user password - * @param int $user_id User ID + * @param string $password The plaintext new user password. + * @param int $user_id User ID. */ function wp_set_password( $password, $user_id ) { global $wpdb; @@ -2701,34 +2779,59 @@ ); clean_user_cache( $user_id ); + + /** + * Fires after the user password is set. + * + * @since 6.2.0 + * + * @param string $password The plaintext password just set. + * @param int $user_id The ID of the user whose password was just set. + */ + do_action( 'wp_set_password', $password, $user_id ); } endif; if ( ! function_exists( 'get_avatar' ) ) : /** - * Retrieve the avatar `` tag for a user, email address, MD5 hash, comment, or post. + * Retrieves the avatar `` tag for a user, email address, MD5 hash, comment, or post. * * @since 2.5.0 - * @since 4.2.0 Optional `$args` parameter added. + * @since 4.2.0 Added the optional `$args` parameter. + * @since 5.5.0 Added the `loading` argument. + * @since 6.1.0 Added the `decoding` argument. + * @since 6.3.0 Added the `fetchpriority` argument. * - * @param mixed $id_or_email The Gravatar to retrieve. Accepts a user_id, gravatar md5 hash, - * user email, WP_User object, WP_Post object, or WP_Comment object. - * @param int $size Optional. Height and width of the avatar image file in pixels. Default 96. - * @param string $default Optional. URL for the default image or a default type. Accepts '404' - * (return a 404 instead of a default image), 'retro' (8bit), 'monsterid' - * (monster), 'wavatar' (cartoon face), 'indenticon' (the "quilt"), - * 'mystery', 'mm', or 'mysteryman' (The Oyster Man), 'blank' (transparent GIF), - * or 'gravatar_default' (the Gravatar logo). Default is the value of the - * 'avatar_default' option, with a fallback of 'mystery'. - * @param string $alt Optional. Alternative text to use in img tag. Default empty. + * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, + * user email, WP_User object, WP_Post object, or WP_Comment object. + * @param int $size Optional. Height and width of the avatar in pixels. Default 96. + * @param string $default_value URL for the default image or a default type. Accepts: + * - '404' (return a 404 instead of a default image) + * - 'retro' (a 8-bit arcade-style pixelated face) + * - 'robohash' (a robot) + * - 'monsterid' (a monster) + * - 'wavatar' (a cartoon face) + * - 'identicon' (the "quilt", a geometric pattern) + * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man) + * - 'blank' (transparent GIF) + * - 'gravatar_default' (the Gravatar logo) + * Default is the value of the 'avatar_default' option, + * with a fallback of 'mystery'. + * @param string $alt Optional. Alternative text to use in the avatar image tag. + * Default empty. * @param array $args { * Optional. Extra arguments to retrieve the avatar. * * @type int $height Display height of the avatar in pixels. Defaults to $size. * @type int $width Display width of the avatar in pixels. Defaults to $size. - * @type bool $force_default Whether to always show the default image, never the Gravatar. Default false. - * @type string $rating What rating to display avatars up to. Accepts 'G', 'PG', 'R', 'X', and are - * judged in that order. Default is the value of the 'avatar_rating' option. + * @type bool $force_default Whether to always show the default image, never the Gravatar. + * Default false. + * @type string $rating What rating to display avatars up to. Accepts: + * - 'G' (suitable for all audiences) + * - 'PG' (possibly offensive, usually for audiences 13 and above) + * - 'R' (intended for adult audiences above 17) + * - 'X' (even more mature than above) + * Default is the value of the 'avatar_rating' option. * @type string $scheme URL scheme to use. See set_url_scheme() for accepted values. * Default null. * @type array|string $class Array or string of additional classes to add to the img element. @@ -2737,11 +2840,16 @@ * Default false. * @type string $loading Value for the `loading` attribute. * Default null. - * @type string $extra_attr HTML attributes to insert in the IMG element. Is not sanitized. Default empty. + * @type string $fetchpriority Value for the `fetchpriority` attribute. + * Default null. + * @type string $decoding Value for the `decoding` attribute. + * Default null. + * @type string $extra_attr HTML attributes to insert in the IMG element. Is not sanitized. + * Default empty. * } * @return string|false `` tag for the user's avatar. False on failure. */ - function get_avatar( $id_or_email, $size = 96, $default = '', $alt = '', $args = null ) { + function get_avatar( $id_or_email, $size = 96, $default_value = '', $alt = '', $args = null ) { $defaults = array( // get_avatar_data() args. 'size' => 96, @@ -2755,19 +2863,17 @@ 'class' => null, 'force_display' => false, 'loading' => null, + 'fetchpriority' => null, + 'decoding' => null, 'extra_attr' => '', ); - if ( wp_lazy_loading_enabled( 'img', 'get_avatar' ) ) { - $defaults['loading'] = wp_get_loading_attr_default( 'get_avatar' ); - } - if ( empty( $args ) ) { $args = array(); } $args['size'] = (int) $size; - $args['default'] = $default; + $args['default'] = $default_value; $args['alt'] = $alt; $args = wp_parse_args( $args, $defaults ); @@ -2779,6 +2885,11 @@ $args['width'] = $args['size']; } + // Update args with loading optimized attributes. + $loading_optimization_attr = wp_get_loading_optimization_attributes( 'img', $args, 'get_avatar' ); + + $args = array_merge( $args, $loading_optimization_attr ); + if ( is_object( $id_or_email ) && isset( $id_or_email->comment_ID ) ) { $id_or_email = get_comment( $id_or_email ); } @@ -2792,7 +2903,7 @@ * @since 4.2.0 * * @param string|null $avatar HTML for the user's avatar. Default null. - * @param mixed $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash, + * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, * user email, WP_User object, WP_Post object, or WP_Comment object. * @param array $args Arguments passed to get_avatar_url(), after processing. */ @@ -2831,16 +2942,37 @@ } } - // Add `loading` attribute. + // Add `loading`, `fetchpriority`, and `decoding` attributes. $extra_attr = $args['extra_attr']; - $loading = $args['loading']; - - if ( in_array( $loading, array( 'lazy', 'eager' ), true ) && ! preg_match( '/\bloading\s*=/', $extra_attr ) ) { + + if ( in_array( $args['loading'], array( 'lazy', 'eager' ), true ) + && ! preg_match( '/\bloading\s*=/', $extra_attr ) + ) { if ( ! empty( $extra_attr ) ) { $extra_attr .= ' '; } - $extra_attr .= "loading='{$loading}'"; + $extra_attr .= "loading='{$args['loading']}'"; + } + + if ( in_array( $args['fetchpriority'], array( 'high', 'low', 'auto' ), true ) + && ! preg_match( '/\bfetchpriority\s*=/', $extra_attr ) + ) { + if ( ! empty( $extra_attr ) ) { + $extra_attr .= ' '; + } + + $extra_attr .= "fetchpriority='{$args['fetchpriority']}'"; + } + + if ( in_array( $args['decoding'], array( 'async', 'sync', 'auto' ), true ) + && ! preg_match( '/\bdecoding\s*=/', $extra_attr ) + ) { + if ( ! empty( $extra_attr ) ) { + $extra_attr .= ' '; + } + + $extra_attr .= "decoding='{$args['decoding']}'"; } $avatar = sprintf( @@ -2858,16 +2990,24 @@ * Filters the HTML for a user's avatar. * * @since 2.5.0 - * @since 4.2.0 The `$args` parameter was added. + * @since 4.2.0 Added the `$args` parameter. * - * @param string $avatar HTML for the user's avatar. - * @param mixed $id_or_email The avatar to retrieve. Accepts a user_id, Gravatar MD5 hash, - * user email, WP_User object, WP_Post object, or WP_Comment object. - * @param int $size Square avatar width and height in pixels to retrieve. - * @param string $default URL for the default image or a default type. Accepts '404', 'retro', 'monsterid', - * 'wavatar', 'indenticon', 'mystery', 'mm', 'mysteryman', 'blank', or 'gravatar_default'. - * @param string $alt Alternative text to use in the avatar image tag. - * @param array $args Arguments passed to get_avatar_data(), after processing. + * @param string $avatar HTML for the user's avatar. + * @param mixed $id_or_email The avatar to retrieve. Accepts a user ID, Gravatar MD5 hash, + * user email, WP_User object, WP_Post object, or WP_Comment object. + * @param int $size Height and width of the avatar in pixels. + * @param string $default_value URL for the default image or a default type. Accepts: + * - '404' (return a 404 instead of a default image) + * - 'retro' (a 8-bit arcade-style pixelated face) + * - 'robohash' (a robot) + * - 'monsterid' (a monster) + * - 'wavatar' (a cartoon face) + * - 'identicon' (the "quilt", a geometric pattern) + * - 'mystery', 'mm', or 'mysteryman' (The Oyster Man) + * - 'blank' (transparent GIF) + * - 'gravatar_default' (the Gravatar logo) + * @param string $alt Alternative text to use in the avatar image tag. + * @param array $args Arguments passed to get_avatar_data(), after processing. */ return apply_filters( 'get_avatar', $avatar, $id_or_email, $args['size'], $args['default'], $args['alt'], $args ); } @@ -2887,8 +3027,8 @@ * @uses Text_Diff * @uses WP_Text_Diff_Renderer_Table * - * @param string $left_string "old" (left) version of string - * @param string $right_string "new" (right) version of string + * @param string $left_string "old" (left) version of string. + * @param string $right_string "new" (right) version of string. * @param string|array $args { * Associative array of options to pass to WP_Text_Diff_Renderer_Table(). *