diff --git a/src/wp-admin/includes/class-wp-debug-data.php b/src/wp-admin/includes/class-wp-debug-data.php index ceae392fd2d3b..0c3fa045dca2f 100644 --- a/src/wp-admin/includes/class-wp-debug-data.php +++ b/src/wp-admin/includes/class-wp-debug-data.php @@ -615,7 +615,7 @@ private static function get_wp_media(): array { $post_max_size = ini_get( 'post_max_size' ); $upload_max_filesize = ini_get( 'upload_max_filesize' ); $max_file_uploads = ini_get( 'max_file_uploads' ); - $effective = min( wp_convert_hr_to_bytes( $post_max_size ), wp_convert_hr_to_bytes( $upload_max_filesize ) ); + $effective = wp_ini_lesser_quantity( $post_max_size, $upload_max_filesize ); // Add info in Media section. $fields['file_uploads'] = array( diff --git a/src/wp-admin/includes/class-wp-site-health.php b/src/wp-admin/includes/class-wp-site-health.php index dd537296a8655..3c1909fb144f5 100644 --- a/src/wp-admin/includes/class-wp-site-health.php +++ b/src/wp-admin/includes/class-wp-site-health.php @@ -2303,7 +2303,7 @@ public function get_test_file_uploads() { $post_max_size = ini_get( 'post_max_size' ); $upload_max_filesize = ini_get( 'upload_max_filesize' ); - if ( wp_convert_hr_to_bytes( $post_max_size ) < wp_convert_hr_to_bytes( $upload_max_filesize ) ) { + if ( wp_ini_quantity_cmp( $post_max_size, $upload_max_filesize ) < 0 ) { $result['label'] = sprintf( /* translators: 1: post_max_size, 2: upload_max_filesize */ __( 'The "%1$s" value is smaller than "%2$s"' ), @@ -2312,7 +2312,7 @@ public function get_test_file_uploads() { ); $result['status'] = 'recommended'; - if ( 0 === wp_convert_hr_to_bytes( $post_max_size ) ) { + if ( wp_ini_parse_quantity( $post_max_size ) <= 0 ) { $result['description'] = sprintf( '
%s
', sprintf( diff --git a/src/wp-includes/default-constants.php b/src/wp-includes/default-constants.php index acfc878fb7138..7139312c93d3a 100644 --- a/src/wp-includes/default-constants.php +++ b/src/wp-includes/default-constants.php @@ -39,8 +39,7 @@ function wp_initial_constants() { define( 'WP_START_TIMESTAMP', microtime( true ) ); } - $current_limit = ini_get( 'memory_limit' ); - $current_limit_int = wp_convert_hr_to_bytes( $current_limit ); + $current_limit = ini_get( 'memory_limit' ); // Define memory limits. if ( ! defined( 'WP_MEMORY_LIMIT' ) ) { @@ -56,9 +55,9 @@ function wp_initial_constants() { if ( ! defined( 'WP_MAX_MEMORY_LIMIT' ) ) { if ( false === wp_is_ini_value_changeable( 'memory_limit' ) ) { define( 'WP_MAX_MEMORY_LIMIT', $current_limit ); - } elseif ( -1 === $current_limit_int || $current_limit_int > 256 * MB_IN_BYTES ) { + } elseif ( wp_ini_quantity_cmp( $current_limit, '256M' ) > 0 ) { define( 'WP_MAX_MEMORY_LIMIT', $current_limit ); - } elseif ( wp_convert_hr_to_bytes( WP_MEMORY_LIMIT ) > 256 * MB_IN_BYTES ) { + } elseif ( wp_ini_quantity_cmp( WP_MEMORY_LIMIT, '256M' ) > 0 ) { define( 'WP_MAX_MEMORY_LIMIT', WP_MEMORY_LIMIT ); } else { define( 'WP_MAX_MEMORY_LIMIT', '256M' ); @@ -66,8 +65,7 @@ function wp_initial_constants() { } // Set memory limits. - $wp_limit_int = wp_convert_hr_to_bytes( WP_MEMORY_LIMIT ); - if ( -1 !== $current_limit_int && ( -1 === $wp_limit_int || $wp_limit_int > $current_limit_int ) ) { + if ( wp_ini_quantity_cmp( WP_MEMORY_LIMIT, $current_limit ) > 0 ) { ini_set( 'memory_limit', WP_MEMORY_LIMIT ); } diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 9cdeef75788f2..62d1f1183a619 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -7845,16 +7845,15 @@ function wp_raise_memory_limit( $context = 'admin' ) { return false; } - $current_limit = ini_get( 'memory_limit' ); - $current_limit_int = wp_convert_hr_to_bytes( $current_limit ); + $current_limit = ini_get( 'memory_limit' ); - if ( -1 === $current_limit_int ) { + // If we're already set to an unlimited value there's no higher limit to set. + if ( wp_ini_parse_quantity( $current_limit ) <= 0 ) { return false; } - $wp_max_limit = WP_MAX_MEMORY_LIMIT; - $wp_max_limit_int = wp_convert_hr_to_bytes( $wp_max_limit ); - $filtered_limit = $wp_max_limit; + $wp_max_limit = WP_MAX_MEMORY_LIMIT; + $filtered_limit = $wp_max_limit; switch ( $context ) { case 'admin': @@ -7929,23 +7928,19 @@ function wp_raise_memory_limit( $context = 'admin' ) { break; } - $filtered_limit_int = wp_convert_hr_to_bytes( $filtered_limit ); + // Set the memory limit to the greatest of all the filtered value, the MAX limit, and the current limit. + $new_limit = wp_ini_greater_quantity( $current_limit, WP_MAX_MEMORY_LIMIT ); + $new_limit = wp_ini_greater_quantity( $filtered_limit, $new_limit ); - if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) { - if ( false !== ini_set( 'memory_limit', $filtered_limit ) ) { - return $filtered_limit; - } else { - return false; - } - } elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) { - if ( false !== ini_set( 'memory_limit', $wp_max_limit ) ) { - return $wp_max_limit; - } else { - return false; - } + // If we're already set at the greatest limit we don't need to change it. + if ( 0 === wp_ini_quantity_cmp( $new_limit, $current_limit ) ) { + return false; } - return false; + // Otherwise attempt to set the new limit and return the new value if it succeeded. + return false !== ini_set( 'memory_limit', $new_limit ) + ? $new_limit + : false; } /** diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php index 90318acdddcb4..ab6c301bf1936 100644 --- a/src/wp-includes/load.php +++ b/src/wp-includes/load.php @@ -5,6 +5,8 @@ * @package WordPress */ +require_once __DIR__ . '/php-compat.php'; + /** * Returns the HTTP protocol sent by the server. * @@ -1677,6 +1679,7 @@ function is_ssl() { * * @since 2.3.0 * @since 4.6.0 Moved from media.php to load.php. + * @deprecated 6.1.0 Use wp_ini_parse_quantity() or wp_hr_bytes() instead. * * @link https://www.php.net/manual/en/function.ini-get.php * @link https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes @@ -1685,6 +1688,19 @@ function is_ssl() { * @return int An integer byte value. */ function wp_convert_hr_to_bytes( $value ) { + _deprecated_function( __FUNCTION__, '6.1.0', 'wp_ini_parse_quantity' ); + return wp_hr_bytes( $value ); +} + +/** + * Parses a "human-readable" byte value into an integer. + * + * @since 6.1.0 + * + * @param string $value Human-readable description of a byte size + * @return int An integer byte value. + */ +function wp_hr_bytes( $value ) { $value = strtolower( trim( $value ) ); $bytes = (int) $value; diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php index 6933ad69957e2..9ef3dcf65899f 100644 --- a/src/wp-includes/media.php +++ b/src/wp-includes/media.php @@ -4185,8 +4185,9 @@ function wp_expand_dimensions( $example_width, $example_height, $max_width, $max * @return int Allowed upload size. */ function wp_max_upload_size() { - $u_bytes = wp_convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) ); - $p_bytes = wp_convert_hr_to_bytes( ini_get( 'post_max_size' ) ); + $upload_max_filesize = ini_get( 'upload_max_filesize' ); + $post_max_size = ini_get( 'post_max_size' ); + $max_upload = wp_ini_lesser_quantity( $upload_max_filesize, $post_max_size ); /** * Filters the maximum upload size allowed in php.ini. @@ -4197,7 +4198,12 @@ function wp_max_upload_size() { * @param int $u_bytes Maximum upload filesize in bytes. * @param int $p_bytes Maximum size of POST data in bytes. */ - return apply_filters( 'upload_size_limit', min( $u_bytes, $p_bytes ), $u_bytes, $p_bytes ); + return apply_filters( + 'upload_size_limit', + wp_ini_parse_quantity( $max_upload ), + wp_ini_parse_quantity( $upload_max_filesize ), + wp_ini_parse_quantity( $post_max_size ) + ); } /** diff --git a/src/wp-includes/php-compat.php b/src/wp-includes/php-compat.php new file mode 100644 index 0000000000000..7e4652003aa15 --- /dev/null +++ b/src/wp-includes/php-compat.php @@ -0,0 +1,290 @@ += 0 ? $a : $b; +} + +/** + * Returns smaller of two php.ini directive quantity values. + * + * Example: + * wp_ini_lesser_quantity( '256m', -1 ) === '256m' + * wp_ini_lesser_quantity( '64K', '64') === '64' + * wp_ini_lesser_quantity( 1000, 2000 ) === 1000 + * + * @param int|string|false $a Quantity value. + * @param int|string|false $b Quantity value. + * @return int|string|false Smaller quantity value. + */ +function wp_ini_lesser_quantity( $a, $b ) { + return wp_ini_quantity_cmp( $a, $b ) <= 0 ? $a : $b; +} + +/** + * Comparator for php.ini quantity values, can be used + * as the callback for functions such as `usort()`. + * + * Example: + * $a < $b => -1 + * $a === $b => 0 + * $a > $b => 1 + * + * @param int|string|false $a Quantity being compared. + * @param int|string|false $b Quantity against which $a is compared. + * @return int + */ +function wp_ini_quantity_cmp( $a, $b ) { + $a_scalar = wp_ini_parse_quantity( $a ); + $b_scalar = wp_ini_parse_quantity( $b ); + + if ( $a_scalar === $b_scalar ) { + return 0; + } + + // No limit on $a means it's at least as large as any $b value. + if ( $a_scalar <= 0 ) { + return 1; + } + + // No limit on $b means it's at least as large as any $a value. + if ( $b_scalar <= 0 ) { + return -1; + } + + return $a_scalar > $b_scalar ? 1 : -1; +} + +/** + * Fallback function to get interpreted size from ini shorthand syntax for + * systems running versions of PHP up to, but not including, 8.2.0. + * + * @see https://www.php.net/manual/en/function.ini-parse-quantity.php + * @see https://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes + * + * @since 7.0.0 + * + * @param string $shorthand Ini shorthand to parse, must be a number followed by an optional + * multiplier. The following multipliers are supported: k/K (1024), + * m/M (1048576), g/G (1073741824). The number can be a decimal, + * hex (prefixed with 0x or 0X), octal (prefixed with 0o, 0O or 0) + * or binary (prefixed with 0b or 0B). + * @return int the interpreted size in bytes as an int. + */ +function ini_parse_quantity_fallback( $shorthand ) { + $end = strlen( $shorthand ); + $at = 0; + $scalar = 0; + + /** Sign of numeric quantity, either positive (1) or negative (-1). */ + $sign = 1; + + /** + * Numeric base of digits determined by string prefix (e.g. "0x" or "0"). + * Must be 8 for octal, 10 for decimal, or 16 for hexadecimal. + */ + $base = 10; + + // Trim leading whitespace from the value. + $at += strspn( $shorthand, " \t\n\r\v\f", $at ); + if ( $at >= $end ) { + return $scalar; + } + + // Handle optional sign indicator. + switch ( $shorthand[ $at ] ) { + case '+': + $at++; + break; + + case '-': + $sign = -1; + $at++; + break; + } + + // Determine base for digit conversion, if not decimal. + $base_a = $shorthand[ $at ] ?? ''; + $base_b = $shorthand[ $at + 1 ] ?? ''; + + if ( '0' === $base_a && ( 'x' === $base_b || 'X' === $base_b ) ) { + $base = 16; + $at += 2; + } else if ( '0' === $base_a && '0' <= $base_b && $base_b <= '9' ) { + $base = 8; + $at += 1; + } + + // Trim leading zeros from the amount. + $at += strspn( $shorthand, '0', $at ); + + /** + * Numeric values for scanned digits. + * + * These are used to determine the decimal value the digit + * represents and whether it's an allowed character in + * the given base. It's allowed if its value is less + * than the base: e.g. '7' is allowed in octal (base 8) + * but '8' and '9' aren't because they are greater than 8. + * + * @var array