Skip to content
18 changes: 16 additions & 2 deletions src/wp-includes/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -1569,8 +1569,22 @@ function rest_is_boolean( $maybe_bool ) {
* @param mixed $maybe_integer The value being evaluated.
* @return bool True if an integer, otherwise false.
*/
function rest_is_integer( $maybe_integer ) {
return is_numeric( $maybe_integer ) && round( (float) $maybe_integer ) === (float) $maybe_integer;
function rest_is_integer( $maybe_integer ): bool {
if ( is_int( $maybe_integer ) ) {
return true;
}

// A canonical integer string of any magnitude — verified without float conversion.
if ( is_string( $maybe_integer ) && preg_match( '/^\s*[+-]?[0-9]+\s*$/', $maybe_integer ) ) {
return true;
}

// Decimal and scientific-notation strings (and floats) keep their historical behavior.
if ( ! is_numeric( $maybe_integer ) ) {
return false;
}
$float_value = (float) $maybe_integer;
return floor( $float_value ) === $float_value;
}

/**
Expand Down
50 changes: 49 additions & 1 deletion tests/phpunit/tests/rest-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -2291,6 +2291,7 @@ public function data_rest_sanitize_array() {

/**
* @ticket 51146
* @ticket 65271
*
* @dataProvider data_rest_is_integer
*
Expand All @@ -2307,7 +2308,12 @@ public function test_rest_is_integer( $expected, $value ) {
}
}

public function data_rest_is_integer() {
/**
* Data provider for {@see self::test_rest_is_integer()}.
*
* @return list<array{ bool, mixed }>
*/
public function data_rest_is_integer(): array {
return array(
array(
true,
Expand Down Expand Up @@ -2341,6 +2347,14 @@ public function data_rest_is_integer() {
false,
'5.5',
),
array(
false,
-5.5,
),
array(
false,
'-5.5',
),
array(
false,
array(),
Expand All @@ -2349,6 +2363,40 @@ public function data_rest_is_integer() {
false,
true,
),
array(
true,
'15e0',
),
array(
true,
'15e+0',
),
array(
true,
'15e-0',
),
array(
false,
'15e-1',
),

// The following values failed with PHP 8.4 when rest_is_integer() used round() in its implementation.
array(
true,
2 ** 52,
),
array(
true,
2 ** 52 + 2,
),
array(
true,
'4503599627370496', // 2 ** 52
),
array(
true,
'4503599627370498', // 2 ** 52 + 2
),
);
}

Expand Down
Loading