Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6025d75
Add `wp_ini_bytes()` to report numeric php.ini directive values
dmsnell May 2, 2022
3e8e90f
Negative values can have a higher magnitude than positive values.
dmsnell May 2, 2022
ca4ae65
Address style lint issues
dmsnell May 2, 2022
00a7426
Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
2009da7
fixup! Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
bd1034f
fixup! Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
3b9a8aa
fixup! Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
3352c53
fixup! Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
218fe03
fixup! Stub out tests and check for PHP_INT_MAX
dmsnell May 3, 2022
ac81d15
Update src/wp-includes/load.php
dmsnell May 4, 2022
a8b1189
Introduce comparison functions to avoid performing math on php.ini qu…
dmsnell May 13, 2022
d52fd4a
Add call to _deprecated_function
dmsnell May 13, 2022
a691430
style issues
dmsnell May 13, 2022
84739c9
fixup! Introduce comparison functions to avoid performing math on php…
dmsnell May 13, 2022
8460497
Fix example output
dmsnell May 13, 2022
adc2c05
Refactor to use new LIMIT contstants, update comparison code
dmsnell Sep 11, 2022
6265587
Remove distinction between "no limit" and "unlimited" - it doesn't ex…
dmsnell Sep 14, 2022
3ee2b76
Fixup: wrong merge resolution
dmsnell Jan 14, 2026
5f569de
Updates
dmsnell Jan 14, 2026
bc40ee8
Cleanup, simplify
dmsnell Jan 14, 2026
19dd3ab
Update code, normalize negative values to -1
dmsnell Jan 16, 2026
13036d1
Rename file to sort with other compat files.
dmsnell Jan 16, 2026
ef5ebfc
Saved from great devastation and humiliation.
dmsnell Jan 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/wp-admin/includes/class-wp-debug-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions src/wp-admin/includes/class-wp-site-health.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"' ),
Expand All @@ -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(
'<p>%s</p>',
sprintf(
Expand Down
218 changes: 218 additions & 0 deletions src/wp-includes/compat-php.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
<?php

/**
* Returns the numeric byte size value for numeric php.ini directives.
*
* Generally this can be used in combination with {@see \ini_get()}
* for values that accept a numeric "byte" size, such as `post_max_size`.
* It will return the value which PHP interprets from the "shorthand"
* syntax, such as "128m" being 128 MiB and "128mb" being 128 B.
*
* @see \ini_parse_quantity()
*
* @since 7.0.0
*
* @param false|int|string $value
* @return int
*/
function wp_ini_parse_quantity( $value ) {
// A missing value is an implicit lack of limit, thus we return `0`, meaning "no limit."
if ( false === $value ) {
return 0;
}

/*
* Directly return pre-parsed values so we can repeatedly call
* this without tracking if we've already parsed a given value.
*/
if ( is_int( $value ) ) {
return $value;
}

/*
* Non-string inputs "fail" to no limit, because there's
* no limit we could ascribe to this invalid value.
*/
if ( ! is_string( $value ) ) {
return 0;
}

return function_exists( 'ini_parse_quantity' )
? ini_parse_quantity( $value )
: ini_parse_quantity_fallback( $value );
}

/**
* Returns larger of two php.ini directive quantity values.
*
* Example:
* wp_ini_greater_quantity( '256m', -1 ) === -1
* wp_ini_greater_quantity( '64K', '64') === '64K'
* wp_ini_greater_quantity( 1000, 2000 ) === 2000
*
* @since 7.0.0
*
* @param int|string|false $a Quantity value.
* @param int|string|false $b Quantity value.
* @return int|string|false Larger quantity value.
*/
function wp_ini_greater_quantity( $a, $b ) {
return wp_ini_quantity_cmp( $a, $b ) >= 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
*
* @since 7.0.0
*
* @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
*
* @since 7.0.0
*
* @param int|string|false $a Quantity being compared.
* @param int|string|false $b Quantity against which $a is compared.
* @return -1|0|1
*/
function wp_ini_quantity_cmp( $a, $b ): int {
return wp_ini_parse_quantity( $a ) <=> wp_ini_parse_quantity( $b );
}

/**
* 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 );

// Trap explicitly only the numeric digits for parsing to avoid PHP parsing strings like “1e5.”
$digits = 8 === $base ? '01234567' : ( 10 === $base ? '0123456789' : '0123456789abcdefABCDEF' );
$digit_length = strspn( $shorthand, $digits, $at );
$scalar = intval( substr( $shorthand, $at, $digit_length ), $base );

/*
* The internal call to `strtoll()` clamps its return value when the
* parsed value would lead to overflow, so recreate that here.
*/
if ( $sign > 0 && $scalar >= PHP_INT_MAX ) {
$scalar = PHP_INT_MAX;
} else if ( $sign < 0 && $scalar <= PHP_INT_MIN ) {
$scalar = PHP_INT_MIN;
}

/*
* Do not use WP constants here (GB_IN_BYTES, MB_IN_BYTES, KB_IN_BYTES)
* since they are re-definable; PHP shorthand values are hard-coded
* in PHP itself and stay the same regardless of these constants. Also,
* this file loads before these constants are defined.
*
* Note that it’s possible to overflow here, as happens in PHP itself.
* Overflow results will likely not match PHP’s value, but will likely
* break in most cases anyway and so leaving this loose is the best
* that can be done without PHP reporting the internal values.
*/
switch ( $shorthand[ $end - 1 ] ) {
case 'g':
case 'G':
$scalar *= 1073741824; // 1024^3
break;

case 'm':
case 'M':
$scalar *= 1048576; // 1024^2
break;

case 'k':
case 'K':
$scalar *= 1024;
break;
}

/**
* Since the overflow behavior is not reproduced here, any negative
* value will report as `-1`, which normalizes negative values for
* more consistent handling inside of plugin code, while large values
* are capped at the max integer value.
*
* These values would be wrong, they are also undefined behavior in
* PHP, so they are also not wrong in any specific way. This function
* only needs to be reliable enough, given that PHP 8.2.0 introduces
* the {@see \ini_parse_quantity()} function natively.
*/
return (int) max( -1, min( $scalar, PHP_INT_MAX ) );
}
10 changes: 4 additions & 6 deletions src/wp-includes/default-constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) ) {
Expand All @@ -56,18 +55,17 @@ 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' );
}
}

// 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 );
}

Expand Down
35 changes: 15 additions & 20 deletions src/wp-includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -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':
Expand Down Expand Up @@ -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;
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/wp-includes/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,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
Expand All @@ -1685,6 +1686,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;

Expand Down
12 changes: 9 additions & 3 deletions src/wp-includes/media.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 )
);
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/wp-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
require ABSPATH . WPINC . '/version.php';
require ABSPATH . WPINC . '/compat-utf8.php';
require ABSPATH . WPINC . '/compat.php';
require ABSPATH . WPINC . '/compat-php.php';
require ABSPATH . WPINC . '/load.php';

// Check the server requirements.
Expand Down
Loading
Loading