From 628bd24697f3f5569f7aa8600ae2ee1cb4b09cdc Mon Sep 17 00:00:00 2001 From: Weston Ruter Date: Mon, 8 Jun 2026 21:25:37 +0000 Subject: [PATCH] REST API: Fix `rest_is_integer()` returning false for large integers. The previous `round( (float) $maybe_integer ) === (float) $maybe_integer` check rejected large integers on PHP 8.4. The check itself was fragile: a PHP `float` (a 64-bit IEEE-754 double) can represent every integer exactly only up to 2^53, so casting larger values is lossy. Nevertheless, that lossiness alone did not reject anything, since both sides of the comparison were munged identically. What actually broke it was a `round()` regression in PHP 8.4, where `round( (float) $x )` can return a value different from `(float) $x` for certain numbers. That inequality caused canonical integers still valid for a `BIGINT UNSIGNED` column (such as unusually high post IDs) to be incorrectly rejected by REST validation, only on PHP 8.4+. The function now short-circuits returning true for native integers and canonical integer strings so that integer-like values of any magnitude are detected correctly. Decimal and scientific-notation strings (and floats) retain their historical behavior, including the existing float comparison, now rewritten as a `floor()` check whose strict equality compares a float to its own floor and is therefore exact. The limitations around `PHP_INT_MAX` and fractional magnitudes beyond `2 ** 53` are documented on the function, and the data provider gains coverage for large integers, negative floats, and scientific notation. Developed in https://github.com/WordPress/wordpress-develop/pull/11893. Follow-up to r48306. Props siliconforks, gautam23, westonruter, kevinfodness, mboynes, desrosj. Fixes #65271. Built from https://develop.svn.wordpress.org/trunk@62474 git-svn-id: http://core.svn.wordpress.org/trunk@61755 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/rest-api.php | 34 ++++++++++++++++++++++++++++++++-- wp-includes/version.php | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/wp-includes/rest-api.php b/wp-includes/rest-api.php index 5548ecf5c6..a4c22e8f1c 100644 --- a/wp-includes/rest-api.php +++ b/wp-includes/rest-api.php @@ -1564,13 +1564,43 @@ function rest_is_boolean( $maybe_bool ) { /** * Determines if a given value is integer-like. * + * This reports whether the value represents an integer; it does not guarantee that the + * value can be represented as a native PHP integer. Values whose magnitude exceeds + * `PHP_INT_MAX` are still reported as integer-like, even though the `(int)` cast that + * {@see rest_sanitize_value_from_schema()} applies for the 'integer' type cannot round-trip + * them: an out-of-range numeric *string* saturates to `PHP_INT_MAX` or `PHP_INT_MIN`, while + * an out-of-range *float* is an undefined conversion in PHP that yields an arbitrary wrapped + * value. Likewise, a numeric value with a fractional part that is too large for the fraction + * to be represented as a float (greater than 2 ** 53) is reported as integer-like. + * * @since 5.5.0 * * @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; + + /* + * The strict equality here is not the unreliable "are two computed floats equal" comparison + * (e.g. 0.1 + 0.2 === 0.3, which is false). It compares a float to its own floor() to ask + * "does this float have a fractional part?". A float is whole exactly when it equals its floor, + * so the comparison is exact and safe regardless of floating-point representation error. + */ + return floor( $float_value ) === $float_value; } /** diff --git a/wp-includes/version.php b/wp-includes/version.php index 7b9d6341d5..b4dc17b6d7 100644 --- a/wp-includes/version.php +++ b/wp-includes/version.php @@ -16,7 +16,7 @@ * * @global string $wp_version */ -$wp_version = '7.1-alpha-62473'; +$wp_version = '7.1-alpha-62474'; /** * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.