diff -r 48c4eec2b7e6 -r 8c2e4d02f4ef wp/wp-includes/functions.php --- a/wp/wp-includes/functions.php Fri Sep 05 18:40:08 2025 +0200 +++ b/wp/wp-includes/functions.php Fri Sep 05 18:52:52 2025 +0200 @@ -5,6 +5,11 @@ * @package WordPress */ +// Don't load directly. +if ( ! defined( 'ABSPATH' ) ) { + die( '-1' ); +} + require ABSPATH . WPINC . '/option.php'; /** @@ -73,7 +78,7 @@ function current_time( $type, $gmt = 0 ) { // Don't use non-GMT timestamp, unless you know the difference and really need to. if ( 'timestamp' === $type || 'U' === $type ) { - return $gmt ? time() : time() + (int) ( get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ); + return $gmt ? time() : time() + (int) ( (float) get_option( 'gmt_offset' ) * HOUR_IN_SECONDS ); } if ( 'mysql' === $type ) { @@ -594,10 +599,10 @@ $day = mktime( 0, 0, 0, $md, $mm, $my ); // The day of the week from the timestamp. - $weekday = gmdate( 'w', $day ); + $weekday = (int) gmdate( 'w', $day ); if ( ! is_numeric( $start_of_week ) ) { - $start_of_week = get_option( 'start_of_week' ); + $start_of_week = (int) get_option( 'start_of_week' ); } if ( $weekday < $start_of_week ) { @@ -609,6 +614,7 @@ // $start + 1 week - 1 second. $end = $start + WEEK_IN_SECONDS - 1; + return compact( 'start', 'end' ); } @@ -1483,18 +1489,18 @@ * Gets the HTTP header information to prevent caching. * * The several different headers cover the different ways cache prevention - * is handled by different browsers. + * is handled by different browsers or intermediate caches such as proxy servers. * * @since 2.8.0 * @since 6.3.0 The `Cache-Control` header for logged in users now includes the * `no-store` and `private` directives. + * @since 6.8.0 The `Cache-Control` header now includes the `no-store` and `private` + * directives regardless of whether a user is logged in. * * @return array The associative array of header names and field values. */ function wp_get_nocache_headers() { - $cache_control = ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) - ? 'no-cache, must-revalidate, max-age=0, no-store, private' - : 'no-cache, must-revalidate, max-age=0'; + $cache_control = 'no-cache, must-revalidate, max-age=0, no-store, private'; $headers = array( 'Expires' => 'Wed, 11 Jan 1984 05:00:00 GMT', @@ -1709,7 +1715,7 @@ do_action( 'do_robotstxt' ); $output = "User-agent: *\n"; - $public = get_option( 'blog_public' ); + $public = (bool) get_option( 'blog_public' ); $site_url = parse_url( site_url() ); $path = ( ! empty( $site_url['path'] ) ) ? $site_url['path'] : ''; @@ -2257,11 +2263,11 @@ * @return bool Whether the path is writable. */ function wp_is_writable( $path ) { - if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ) { + if ( 'Windows' === PHP_OS_FAMILY ) { return win_is_writable( $path ); - } else { - return @is_writable( $path ); - } + } + + return @is_writable( $path ); } /** @@ -2706,8 +2712,7 @@ * when regenerated. If yes, ensure the new file name will be unique and will produce unique sub-sizes. */ if ( $is_image ) { - /** This filter is documented in wp-includes/class-wp-image-editor.php */ - $output_formats = apply_filters( 'image_editor_output_format', array(), $_dir . $filename, $mime_type ); + $output_formats = wp_get_image_editor_output_format( $_dir . $filename, $mime_type ); $alt_types = array(); if ( ! empty( $output_formats[ $mime_type ] ) ) { @@ -3102,7 +3107,13 @@ // Attempt to figure out what type of image it actually is. $real_mime = wp_get_image_mime( $file ); - if ( $real_mime && $real_mime !== $type ) { + $heic_images_extensions = array( + 'heif', + 'heics', + 'heifs', + ); + + if ( $real_mime && ( $real_mime !== $type || in_array( $ext, $heic_images_extensions, true ) ) ) { /** * Filters the list mapping image mime types to their respective extensions. * @@ -3113,19 +3124,31 @@ $mime_to_ext = apply_filters( 'getimagesize_mimes_to_exts', array( - 'image/jpeg' => 'jpg', - 'image/png' => 'png', - 'image/gif' => 'gif', - 'image/bmp' => 'bmp', - 'image/tiff' => 'tif', - 'image/webp' => 'webp', - 'image/avif' => 'avif', + 'image/jpeg' => 'jpg', + 'image/png' => 'png', + 'image/gif' => 'gif', + 'image/bmp' => 'bmp', + 'image/tiff' => 'tif', + 'image/webp' => 'webp', + 'image/avif' => 'avif', + + /* + * In theory there are/should be file extensions that correspond to the + * mime types: .heif, .heics and .heifs. However it seems that HEIC images + * with any of the mime types commonly have a .heic file extension. + * Seems keeping the status quo here is best for compatibility. + */ + 'image/heic' => 'heic', + 'image/heif' => 'heic', + 'image/heic-sequence' => 'heic', + 'image/heif-sequence' => 'heic', ) ); // Replace whatever is after the last period in the filename with the correct extension. if ( ! empty( $mime_to_ext[ $real_mime ] ) ) { $filename_parts = explode( '.', $filename ); + array_pop( $filename_parts ); $filename_parts[] = $mime_to_ext[ $real_mime ]; $new_filename = implode( '.', $filename_parts ); @@ -3299,6 +3322,7 @@ * @since 4.7.1 * @since 5.8.0 Added support for WebP images. * @since 6.5.0 Added support for AVIF images. + * @since 6.7.0 Added support for HEIC images. * * @param string $file Full path to the file. * @return string|false The actual mime type or false if the type cannot be determined. @@ -3315,9 +3339,7 @@ $mime = ( $imagetype ) ? image_type_to_mime_type( $imagetype ) : false; } elseif ( function_exists( 'getimagesize' ) ) { // Don't silence errors when in debug mode, unless running unit tests. - if ( defined( 'WP_DEBUG' ) && WP_DEBUG - && ! defined( 'WP_RUN_CORE_TESTS' ) - ) { + if ( defined( 'WP_DEBUG' ) && WP_DEBUG && ! defined( 'WP_RUN_CORE_TESTS' ) ) { // Not using wp_getimagesize() here to avoid an infinite loop. $imagesize = getimagesize( $file ); } else { @@ -3364,13 +3386,28 @@ // Divide the header string into 4 byte groups. $magic = str_split( $magic, 8 ); - if ( - isset( $magic[1] ) && - isset( $magic[2] ) && - 'ftyp' === hex2bin( $magic[1] ) && - ( 'avif' === hex2bin( $magic[2] ) || 'avis' === hex2bin( $magic[2] ) ) - ) { - $mime = 'image/avif'; + if ( isset( $magic[1] ) && isset( $magic[2] ) && 'ftyp' === hex2bin( $magic[1] ) ) { + if ( 'avif' === hex2bin( $magic[2] ) || 'avis' === hex2bin( $magic[2] ) ) { + $mime = 'image/avif'; + } elseif ( 'heic' === hex2bin( $magic[2] ) ) { + $mime = 'image/heic'; + } elseif ( 'heif' === hex2bin( $magic[2] ) ) { + $mime = 'image/heif'; + } else { + /* + * HEIC/HEIF images and image sequences/animations may have other strings here + * like mif1, msf1, etc. For now fall back to using finfo_file() to detect these. + */ + if ( extension_loaded( 'fileinfo' ) ) { + $fileinfo = finfo_open( FILEINFO_MIME_TYPE ); + $mime_type = finfo_file( $fileinfo, $file ); + finfo_close( $fileinfo ); + + if ( wp_is_heic_image_mime_type( $mime_type ) ) { + $mime = $mime_type; + } + } + } } } catch ( Exception $e ) { $mime = false; @@ -3386,6 +3423,7 @@ * @since 4.2.0 Support was added for GIMP (.xcf) files. * @since 4.9.2 Support was added for Flac (.flac) files. * @since 4.9.6 Support was added for AAC (.aac) files. + * @since 6.8.0 Support was added for `audio/x-wav`. * * @return string[] Array of mime types keyed by the file extension regex corresponding to those types. */ @@ -3413,7 +3451,13 @@ 'webp' => 'image/webp', 'avif' => 'image/avif', 'ico' => 'image/x-icon', + + // TODO: Needs improvement. All images with the following mime types seem to have .heic file extension. 'heic' => 'image/heic', + 'heif' => 'image/heif', + 'heics' => 'image/heic-sequence', + 'heifs' => 'image/heif-sequence', + // Video formats. 'asf|asx' => 'video/x-ms-asf', 'wmv' => 'video/x-ms-wmv', @@ -3444,7 +3488,7 @@ 'mp3|m4a|m4b' => 'audio/mpeg', 'aac' => 'audio/aac', 'ra|ram' => 'audio/x-realaudio', - 'wav' => 'audio/wav', + 'wav|x-wav' => 'audio/wav', 'ogg|oga' => 'audio/ogg', 'flac' => 'audio/flac', 'mid|midi' => 'audio/midi', @@ -3533,7 +3577,7 @@ return apply_filters( 'ext2type', array( - 'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'webp', 'avif' ), + 'image' => array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'bmp', 'tif', 'tiff', 'ico', 'heic', 'heif', 'webp', 'avif' ), 'audio' => array( 'aac', 'ac3', 'aif', 'aiff', 'flac', 'm3a', 'm4a', 'm4b', 'mka', 'mp1', 'mp2', 'mp3', 'ogg', 'oga', 'ram', 'wav', 'wma' ), 'video' => array( '3g2', '3gp', '3gpp', 'asf', 'avi', 'divx', 'dv', 'flv', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'mpv', 'ogm', 'ogv', 'qt', 'rm', 'vob', 'wmv' ), 'document' => array( 'doc', 'docx', 'docm', 'dotm', 'odt', 'pages', 'pdf', 'xps', 'oxps', 'rtf', 'wp', 'wpd', 'psd', 'xcf' ), @@ -3628,7 +3672,7 @@ */ function wp_nonce_ays( $action ) { // Default title and response code. - $title = __( 'Something went wrong.' ); + $title = __( 'An error occurred.' ); $response_code = 403; if ( 'log-out' === $action ) { @@ -3858,7 +3902,7 @@ > - + '; printf( - '%2$s %3$s', + '%2$s %3$s', esc_url( $direct_update_url ), __( 'Update PHP' ), /* translators: Hidden accessibility text. */ @@ -8805,17 +8868,46 @@ } /** + * Returns the current WordPress version. + * + * Returns an unmodified value of `$wp_version`. Some plugins modify the global + * in an attempt to improve security through obscurity. This practice can cause + * errors in WordPress, so the ability to get an unmodified version is needed. + * + * @since 6.7.0 + * + * @return string The current WordPress version. + */ +function wp_get_wp_version() { + static $wp_version; + + if ( ! isset( $wp_version ) ) { + require ABSPATH . WPINC . '/version.php'; + } + + return $wp_version; +} + +/** * Checks compatibility with the current WordPress version. * * @since 5.2.0 * - * @global string $wp_version The WordPress version string. + * @global string $_wp_tests_wp_version The WordPress version string. Used only in Core tests. * * @param string $required Minimum required WordPress version. * @return bool True if required version is compatible or empty, false if not. */ function is_wp_version_compatible( $required ) { - global $wp_version; + if ( + defined( 'WP_RUN_CORE_TESTS' ) + && WP_RUN_CORE_TESTS + && isset( $GLOBALS['_wp_tests_wp_version'] ) + ) { + $wp_version = $GLOBALS['_wp_tests_wp_version']; + } else { + $wp_version = wp_get_wp_version(); + } // Strip off any -alpha, -RC, -beta, -src suffixes. list( $version ) = explode( '-', $wp_version ); @@ -9003,3 +9095,113 @@ echo wp_kses_post( wp_get_admin_notice( $message, $args ) ); } + +/** + * Checks if a mime type is for a HEIC/HEIF image. + * + * @since 6.7.0 + * + * @param string $mime_type The mime type to check. + * @return bool Whether the mime type is for a HEIC/HEIF image. + */ +function wp_is_heic_image_mime_type( $mime_type ) { + $heic_mime_types = array( + 'image/heic', + 'image/heif', + 'image/heic-sequence', + 'image/heif-sequence', + ); + + return in_array( $mime_type, $heic_mime_types, true ); +} + +/** + * Returns a cryptographically secure hash of a message using a fast generic hash function. + * + * Use the wp_verify_fast_hash() function to verify the hash. + * + * This function does not salt the value prior to being hashed, therefore input to this function must originate from + * a random generator with sufficiently high entropy, preferably greater than 128 bits. This function is used internally + * in WordPress to hash security keys and application passwords which are generated with high entropy. + * + * Important: + * + * - This function must not be used for hashing user-generated passwords. Use wp_hash_password() for that. + * - This function must not be used for hashing other low-entropy input. Use wp_hash() for that. + * + * The BLAKE2b algorithm is used by Sodium to hash the message. + * + * @since 6.8.0 + * + * @throws TypeError Thrown by Sodium if the message is not a string. + * + * @param string $message The message to hash. + * @return string The hash of the message. + */ +function wp_fast_hash( + #[\SensitiveParameter] + string $message +): string { + $hashed = sodium_crypto_generichash( $message, 'wp_fast_hash_6.8+', 30 ); + return '$generic$' . sodium_bin2base64( $hashed, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING ); +} + +/** + * Checks whether a plaintext message matches the hashed value. Used to verify values hashed via wp_fast_hash(). + * + * The function uses Sodium to hash the message and compare it to the hashed value. If the hash is not a generic hash, + * the hash is treated as a phpass portable hash in order to provide backward compatibility for passwords and security + * keys which were hashed using phpass prior to WordPress 6.8.0. + * + * @since 6.8.0 + * + * @throws TypeError Thrown by Sodium if the message is not a string. + * + * @param string $message The plaintext message. + * @param string $hash Hash of the message to check against. + * @return bool Whether the message matches the hashed message. + */ +function wp_verify_fast_hash( + #[\SensitiveParameter] + string $message, + string $hash +): bool { + if ( ! str_starts_with( $hash, '$generic$' ) ) { + // Back-compat for old phpass hashes. + require_once ABSPATH . WPINC . '/class-phpass.php'; + return ( new PasswordHash( 8, true ) )->CheckPassword( $message, $hash ); + } + + return hash_equals( $hash, wp_fast_hash( $message ) ); +} + +/** + * Generates a unique ID based on the structure and values of a given array. + * + * This function serializes the array into a JSON string and generates a hash + * that serves as a unique identifier. Optionally, a prefix can be added to + * the generated ID for context or categorization. + * + * @since 6.8.0 + * + * @param array $data The input array to generate an ID from. + * @param string $prefix Optional. A prefix to prepend to the generated ID. Default ''. + * + * @return string The generated unique ID for the array. + */ +function wp_unique_id_from_values( array $data, string $prefix = '' ): string { + if ( empty( $data ) ) { + _doing_it_wrong( + __FUNCTION__, + sprintf( + /* translators: %s: parameter name. */ + __( 'The %s argument must not be empty.' ), + '$data' + ), + '6.8.0' + ); + } + $serialized = wp_json_encode( $data ); + $hash = substr( md5( $serialized ), 0, 8 ); + return $prefix . $hash; +}