From 718c4fda184508d4e39635e3805c5ff8b152cce6 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 30 Jul 2026 00:12:42 +0800 Subject: [PATCH 1/5] ext/intl: Fix Collator sort conversion error handling (#22893) Report UTF-8/UTF-16 conversion failures from Collator sort comparisons through the intl error mechanism. Previously, these paths emitted a warning and continued with an empty string. Keep the input array unchanged when sorting cannot complete, preserve the active collator state across nested sorts, and stop invoking further comparisons after a conversion failure. Co-authored-by: David CARLIER --- NEWS | 6 ++ UPGRADING | 5 + ext/intl/collator/collator_convert.cpp | 49 +++++++--- ext/intl/collator/collator_sort.cpp | 95 +++++++++++++++++-- ext/intl/php_intl.c | 1 + ext/intl/php_intl.h | 1 + .../tests/collator_sort_conversion_error.phpt | 27 ++++++ ...lator_sort_conversion_error_exception.phpt | 50 ++++++++++ .../collator_sort_conversion_error_level.phpt | 29 ++++++ ...r_sort_conversion_error_message_sites.phpt | 27 ++++++ ...ator_sort_conversion_error_procedural.phpt | 54 +++++++++++ ...ollator_sort_conversion_error_regular.phpt | 70 ++++++++++++++ .../collator_sort_error_reset_reentrancy.phpt | 36 +++++++ .../collator_sort_nested_compare_func.phpt | 63 ++++++++++++ ext/intl/tests/collator_sort_numeric.phpt | 83 ++++++++++++++++ .../collator_sort_stops_after_failure.phpt | 32 +++++++ .../tests/collator_sort_tostring_throws.phpt | 67 +++++++++++++ 17 files changed, 674 insertions(+), 21 deletions(-) create mode 100644 ext/intl/tests/collator_sort_conversion_error.phpt create mode 100644 ext/intl/tests/collator_sort_conversion_error_exception.phpt create mode 100644 ext/intl/tests/collator_sort_conversion_error_level.phpt create mode 100644 ext/intl/tests/collator_sort_conversion_error_message_sites.phpt create mode 100644 ext/intl/tests/collator_sort_conversion_error_procedural.phpt create mode 100644 ext/intl/tests/collator_sort_conversion_error_regular.phpt create mode 100644 ext/intl/tests/collator_sort_error_reset_reentrancy.phpt create mode 100644 ext/intl/tests/collator_sort_nested_compare_func.phpt create mode 100644 ext/intl/tests/collator_sort_numeric.phpt create mode 100644 ext/intl/tests/collator_sort_stops_after_failure.phpt create mode 100644 ext/intl/tests/collator_sort_tostring_throws.phpt diff --git a/NEWS b/NEWS index 0c2fd04ad469..c144b3c62c23 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,12 @@ PHP NEWS (Weilin Du) . Added gmp_powm_sec(). (Weilin Du) +- Intl: + . Fixed Collator::sort(), collator_sort(), Collator::asort(), and + collator_asort() to report UTF-8/UTF-16 conversion errors through the intl + error handler instead of emitting a warning and continuing with an empty + string. (Weilin Du) + 30 Jul 2026, PHP 8.6.0alpha3 - Core: diff --git a/UPGRADING b/UPGRADING index d8c8a953766a..47a9c023bf3f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -76,6 +76,11 @@ PHP 8.6 UPGRADE NOTES IntlDateFormatter::localtime()/datefmt_localtime() now raise a TypeError when the offset argument is not of type int instead of silently converting the value. + . Collator::sort(), collator_sort(), Collator::asort(), and + collator_asort() now report UTF-8/UTF-16 conversion failures during + comparison through the intl error mechanism and return false. With + intl.use_exceptions enabled, these failures throw IntlException. Previously, + these paths emitted a warning and compared the value as an empty string. - PCNTL: . pcntl_alarm() now raises a ValueError if the seconds argument is diff --git a/ext/intl/collator/collator_convert.cpp b/ext/intl/collator/collator_convert.cpp index dd3360a69092..0f6e62f1b557 100644 --- a/ext/intl/collator/collator_convert.cpp +++ b/ext/intl/collator/collator_convert.cpp @@ -38,6 +38,20 @@ extern "C" { return retval; \ } +static void collator_set_conversion_error(UErrorCode status, const char *message) +{ + if (U_SUCCESS(status)) { + status = U_MEMORY_ALLOCATION_ERROR; + } + + ZEND_ASSERT(INTL_G(current_collator_error) != nullptr); + intl_error *err = INTL_G(current_collator_error); + intl_error_reset(err); + err->code = status; + err->custom_error_message = zend_string_init( + message, strlen(message), false); +} + /* {{{ collator_convert_hash_item_from_utf8_to_utf16 */ static void collator_convert_hash_item_from_utf8_to_utf16( HashTable* hash, zval *hashData, zend_string *hashKey, zend_ulong hashIndex, @@ -166,11 +180,12 @@ U_CFUNC zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval, zval *rv ) u8str = intl_convert_utf16_to_utf8( (UChar*) Z_STRVAL_P(utf16_zval), UCHARS( Z_STRLEN_P(utf16_zval) ), &status ); if( !u8str ) { - php_error( E_WARNING, "Error converting utf16 to utf8 in collator_convert_zval_utf16_to_utf8()" ); - ZVAL_EMPTY_STRING( rv ); - } else { - ZVAL_NEW_STR( rv, u8str ); + collator_set_conversion_error(status, "Error converting string from UTF-16 to UTF-8"); + ZVAL_NULL( rv ); + return nullptr; } + + ZVAL_NEW_STR( rv, u8str ); return rv; } /* }}} */ @@ -183,11 +198,9 @@ U_CFUNC zend_string *collator_convert_zstr_utf8_to_utf16(zend_string *utf8_str) zend_string *zstr = intl_convert_utf8_to_utf16_zstr( ZSTR_VAL(utf8_str), ZSTR_LEN(utf8_str), &status); - // FIXME Or throw error or use intl internal error handler if (U_FAILURE(status)) { - php_error(E_WARNING, - "Error casting object to string in collator_convert_zstr_utf8_to_utf16()"); - zstr = ZSTR_EMPTY_ALLOC(); + collator_set_conversion_error(status, "Error converting string from UTF-8 to UTF-16"); + return nullptr; } return zstr; @@ -214,12 +227,19 @@ U_CFUNC zval* collator_convert_object_to_string( zval* obj, zval *rv ) { /* cast_object failed => bail out. */ zval_ptr_dtor( zstr ); + ZVAL_NULL( zstr ); + if( EG(exception) ) { + return nullptr; + } COLLATOR_CONVERT_RETURN_FAILED( obj ); } /* Object wasn't successfully converted => bail out. */ if( zstr == nullptr ) { + if( EG(exception) ) { + return nullptr; + } COLLATOR_CONVERT_RETURN_FAILED( obj ); } @@ -227,10 +247,11 @@ U_CFUNC zval* collator_convert_object_to_string( zval* obj, zval *rv ) zend_string *converted_str = intl_convert_utf8_to_utf16_zstr( Z_STRVAL_P( zstr ), Z_STRLEN_P( zstr ), &status ); - // FIXME Or throw error or use intl internal error handler if( U_FAILURE( status ) ) { - php_error( E_WARNING, "Error casting object to string in collator_convert_object_to_string()" ); - converted_str = ZSTR_EMPTY_ALLOC(); + collator_set_conversion_error(status, "Error converting object string from UTF-8 to UTF-16"); + zval_ptr_dtor( zstr ); + ZVAL_NULL( zstr ); + return nullptr; } /* Cleanup zstr to hold utf16 string. */ @@ -339,7 +360,11 @@ U_CFUNC zend_string *collator_zval_to_string(zval *arg) return zend_string_copy(Z_STR_P(arg)); } - zend_string *utf8_str = zval_get_string(arg); + zend_string *utf8_str = zval_try_get_string(arg); + if (utf8_str == nullptr) { + return nullptr; + } + zend_string *utf16_str = collator_convert_zstr_utf8_to_utf16(utf8_str); zend_string_release(utf8_str); return utf16_str; diff --git a/ext/intl/collator/collator_sort.cpp b/ext/intl/collator/collator_sort.cpp index b7c2b8736596..cb1f2aefc358 100644 --- a/ext/intl/collator/collator_sort.cpp +++ b/ext/intl/collator/collator_sort.cpp @@ -50,6 +50,17 @@ static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576; static const size_t DEF_UTF16_BUF_SIZE = 1024; +static void collator_report_sort_error(Collator_object *co, const intl_error *sort_error) +{ + if (sort_error->custom_error_message) { + intl_errors_set( + COLLATOR_ERROR_P(co), sort_error->code, + ZSTR_VAL(sort_error->custom_error_message)); + } else { + intl_errors_set_code(COLLATOR_ERROR_P(co), sort_error->code); + } +} + /* {{{ collator_regular_compare_function */ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2) { @@ -59,12 +70,20 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2) zval norm1, norm2; zval *num1_p = nullptr, *num2_p = nullptr; zval *norm1_p = nullptr, *norm2_p = nullptr; - zval *str1_p, *str2_p; + zval *str1_p = nullptr, *str2_p = nullptr; ZVAL_NULL(&str1); str1_p = collator_convert_object_to_string( op1, &str1 ); + if( str1_p == nullptr ) { + return FAILURE; + } + ZVAL_NULL(&str2); str2_p = collator_convert_object_to_string( op2, &str2 ); + if( str2_p == nullptr ) { + rc = FAILURE; + goto cleanup; + } /* If both args are strings AND either of args is not numeric string * then use ICU-compare. Otherwise PHP-compare. */ @@ -90,9 +109,17 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2) * just convert it to utf8. */ norm1_p = collator_convert_zstr_utf16_to_utf8( str1_p, &norm1 ); + if( norm1_p == nullptr ) { + rc = FAILURE; + goto cleanup; + } /* num2 is not set but str2 is string => do normalization. */ norm2_p = collator_normalize_sort_argument( str2_p, &norm2 ); + if( norm2_p == nullptr ) { + rc = FAILURE; + goto cleanup; + } } else { @@ -109,16 +136,28 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2) { /* num1 is not set if str1 or str2 is not a string => do normalization. */ norm1_p = collator_normalize_sort_argument( str1_p, &norm1 ); + if( norm1_p == nullptr ) { + rc = FAILURE; + goto cleanup; + } /* if num1 is not set then num2 is not set as well => do normalization. */ norm2_p = collator_normalize_sort_argument( str2_p, &norm2 ); + if( norm2_p == nullptr ) { + rc = FAILURE; + goto cleanup; + } } rc = compare_function( result, norm1_p, norm2_p ); + } +cleanup: + if( norm1_p ) zval_ptr_dtor( norm1_p ); + + if( norm2_p ) zval_ptr_dtor( norm2_p ); - } if( num1_p ) zval_ptr_dtor( num1_p ); @@ -126,8 +165,11 @@ static int collator_regular_compare_function(zval *result, zval *op1, zval *op2) if( num2_p ) zval_ptr_dtor( num2_p ); - zval_ptr_dtor( str1_p ); - zval_ptr_dtor( str2_p ); + if( str1_p ) + zval_ptr_dtor( str1_p ); + + if( str2_p ) + zval_ptr_dtor( str2_p ); return rc; } @@ -170,9 +212,16 @@ static int collator_numeric_compare_function(zval *result, zval *op1, zval *op2) */ static int collator_icu_compare_function(zval *result, zval *op1, zval *op2) { - int rc = SUCCESS; zend_string *str1 = collator_zval_to_string(op1); + if( str1 == nullptr ) { + return FAILURE; + } + zend_string *str2 = collator_zval_to_string(op2); + if( str2 == nullptr ) { + zend_string_release(str1); + return FAILURE; + } /* Compare the strings using ICU. */ ZEND_ASSERT(INTL_G(current_collator) != nullptr); @@ -184,7 +233,7 @@ static int collator_icu_compare_function(zval *result, zval *op1, zval *op2) zend_string_release(str1); zend_string_release(str2); - return rc; + return SUCCESS; } /* }}} */ @@ -197,6 +246,11 @@ static int collator_compare_func(Bucket *f, Bucket *s) zval *first = &f->val; zval *second = &s->val; + ZEND_ASSERT(INTL_G(current_collator_error) != nullptr); + if( EG(exception) || U_FAILURE( INTL_ERROR_CODE(*INTL_G(current_collator_error)) ) ) { + return 0; + } + if( INTL_G(compare_func)( &result, first, second) == FAILURE ) return 0; @@ -260,6 +314,9 @@ static collator_compare_func_t collator_get_compare_function( const zend_long so static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS ) { UCollator* saved_collator; + intl_error* saved_collator_error; + intl_error sort_error; + collator_compare_func_t saved_compare_func; zval* array = nullptr; HashTable* hash = nullptr; zend_array* sorted = nullptr; @@ -282,9 +339,6 @@ static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS ) RETURN_THROWS(); } - /* Set 'compare function' according to sort flags. */ - INTL_G(compare_func) = collator_get_compare_function( sort_flags ); - hash = Z_ARRVAL_P( array ); /* Copy array, so the in-place modifications will not be visible to the callback function */ @@ -297,15 +351,38 @@ static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS ) } COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" ); + intl_error_init( &sort_error ); + /* Save specified collator in the request-global (?) variable. */ saved_collator = INTL_G( current_collator ); + saved_collator_error = INTL_G( current_collator_error ); + saved_compare_func = INTL_G( compare_func ); INTL_G( current_collator ) = co->ucoll; + INTL_G( current_collator_error ) = &sort_error; + INTL_G( compare_func ) = collator_get_compare_function( sort_flags ); /* Sort specified array. */ zend_hash_sort( sorted, collator_compare_func, renumber ); /* Restore saved collator. */ INTL_G( current_collator ) = saved_collator; + INTL_G( current_collator_error ) = saved_collator_error; + INTL_G( compare_func ) = saved_compare_func; + + if( EG(exception) ) { + zend_array_destroy( sorted ); + intl_error_reset( &sort_error ); + RETURN_THROWS(); + } + + if( U_FAILURE( INTL_ERROR_CODE(sort_error) ) ) { + zend_array_destroy( sorted ); + collator_report_sort_error(co, &sort_error); + intl_error_reset( &sort_error ); + RETURN_FALSE; + } + + intl_error_reset( &sort_error ); /* Convert strings in the specified array back to UTF-8. */ collator_convert_hash_from_utf16_to_utf8( sorted, COLLATOR_ERROR_CODE_P( co ) ); diff --git a/ext/intl/php_intl.c b/ext/intl/php_intl.c index 697d77a98bfa..7bc599728e68 100644 --- a/ext/intl/php_intl.c +++ b/ext/intl/php_intl.c @@ -278,6 +278,7 @@ PHP_RINIT_FUNCTION( intl ) PHP_RSHUTDOWN_FUNCTION( intl ) { INTL_G(current_collator) = NULL; + INTL_G(current_collator_error) = NULL; if (INTL_G(grapheme_iterator)) { grapheme_close_global_iterator( ); INTL_G(grapheme_iterator) = NULL; diff --git a/ext/intl/php_intl.h b/ext/intl/php_intl.h index 1a9b4f769e86..6b6cce59b920 100644 --- a/ext/intl/php_intl.h +++ b/ext/intl/php_intl.h @@ -45,6 +45,7 @@ extern zend_module_entry intl_module_entry; ZEND_BEGIN_MODULE_GLOBALS(intl) struct UCollator *current_collator; + intl_error *current_collator_error; char* default_locale; collator_compare_func_t compare_func; UBreakIterator* grapheme_iterator; diff --git a/ext/intl/tests/collator_sort_conversion_error.phpt b/ext/intl/tests/collator_sort_conversion_error.phpt new file mode 100644 index 000000000000..acc62df0704c --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error.phpt @@ -0,0 +1,27 @@ +--TEST-- +Collator::sort() reports conversion errors from comparison callbacks +--EXTENSIONS-- +intl +--FILE-- +sort($array, Collator::SORT_STRING)); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), "\n"; +?> +--EXPECT-- +bool(false) +bool(true) +Collator::sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +Collator::sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND diff --git a/ext/intl/tests/collator_sort_conversion_error_exception.phpt b/ext/intl/tests/collator_sort_conversion_error_exception.phpt new file mode 100644 index 000000000000..c9a0e29694f0 --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error_exception.phpt @@ -0,0 +1,50 @@ +--TEST-- +Collator::sort() throws IntlException on conversion errors with intl.use_exceptions +--EXTENSIONS-- +intl +--INI-- +intl.use_exceptions=1 +--FILE-- +sort($array, Collator::SORT_STRING); + echo 'no exception', PHP_EOL; +} catch (IntlException $e) { + echo get_class($e), ': ', $e->getMessage(), PHP_EOL; +} +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +var_dump($array[0], $array[1] instanceof BadString, $array[2]); + +$array = ['b' => 'b', 'bad' => new BadString(), 'a' => 'a']; +try { + collator_asort($coll, $array, Collator::SORT_STRING); + echo 'no exception', PHP_EOL; +} catch (IntlException $e) { + echo get_class($e), ': ', $e->getMessage(), PHP_EOL; +} +var_dump(array_keys($array)); +?> +--EXPECT-- +IntlException: Collator::sort(): Error converting string from UTF-8 to UTF-16 +bool(true) +string(1) "b" +bool(true) +string(1) "a" +IntlException: collator_asort(): Error converting string from UTF-8 to UTF-16 +array(3) { + [0]=> + string(1) "b" + [1]=> + string(3) "bad" + [2]=> + string(1) "a" +} diff --git a/ext/intl/tests/collator_sort_conversion_error_level.phpt b/ext/intl/tests/collator_sort_conversion_error_level.phpt new file mode 100644 index 000000000000..adcf96d1ed79 --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error_level.phpt @@ -0,0 +1,29 @@ +--TEST-- +Collator::sort() reports comparison conversion errors through intl.error_level +--EXTENSIONS-- +intl +--FILE-- +sort($array, Collator::SORT_STRING)); +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), PHP_EOL; +?> +--EXPECTF-- +Deprecated: ini_set(): Using a value different than 0 for intl.error_level is deprecated, as the intl.error_level INI setting is deprecated. Instead the intl.use_exceptions INI setting should be enabled to throw exceptions on errors or intl_get_error_code()/intl_get_error_message() should be used to manually deal with errors in %s on line %d + +Warning: Collator::sort(): Error converting string from UTF-8 to UTF-16 in %s on line %d +bool(false) +bool(true) +Collator::sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND diff --git a/ext/intl/tests/collator_sort_conversion_error_message_sites.phpt b/ext/intl/tests/collator_sort_conversion_error_message_sites.phpt new file mode 100644 index 000000000000..b300c4e7499d --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error_message_sites.phpt @@ -0,0 +1,27 @@ +--TEST-- +Collator::sort() conversion error messages describe the failed conversion +--EXTENSIONS-- +intl +--FILE-- +sort($array, Collator::SORT_REGULAR)); +echo $coll->getErrorMessage(), PHP_EOL; + +$array = ['a', new BadString()]; +var_dump($coll->sort($array, Collator::SORT_STRING)); +echo $coll->getErrorMessage(), PHP_EOL; +?> +--EXPECT-- +bool(false) +Collator::sort(): Error converting object string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(false) +Collator::sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND diff --git a/ext/intl/tests/collator_sort_conversion_error_procedural.phpt b/ext/intl/tests/collator_sort_conversion_error_procedural.phpt new file mode 100644 index 000000000000..e7be7806f63d --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error_procedural.phpt @@ -0,0 +1,54 @@ +--TEST-- +collator_sort() and collator_asort() report conversion errors +--EXTENSIONS-- +intl +--FILE-- + 'b', 'bad' => new BadProceduralString(), 'a' => 'a']; +var_dump(collator_asort($coll, $array, Collator::SORT_STRING)); +var_dump(array_keys($array)); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump(collator_get_error_code($coll) === U_INVALID_CHAR_FOUND); +echo collator_get_error_message($coll), "\n"; +?> +--EXPECT-- +bool(false) +string(1) "b" +bool(true) +string(1) "a" +bool(true) +collator_sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +collator_sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(false) +array(3) { + [0]=> + string(1) "b" + [1]=> + string(3) "bad" + [2]=> + string(1) "a" +} +bool(true) +collator_asort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +collator_asort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND diff --git a/ext/intl/tests/collator_sort_conversion_error_regular.phpt b/ext/intl/tests/collator_sort_conversion_error_regular.phpt new file mode 100644 index 000000000000..439d9293d5d6 --- /dev/null +++ b/ext/intl/tests/collator_sort_conversion_error_regular.phpt @@ -0,0 +1,70 @@ +--TEST-- +Collator::sort() SORT_REGULAR conversion error cleanup paths +--EXTENSIONS-- +intl +--FILE-- +sort($array, Collator::SORT_REGULAR)); +var_dump($array[0] instanceof BadRegularString); +var_dump($array[1]); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), "\n"; + +$array = ['a', new BadRegularString()]; +var_dump($coll->sort($array, Collator::SORT_REGULAR)); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +var_dump($array[0], $array[1] instanceof BadRegularString); + +$array = ['10', '9']; +var_dump($coll->sort($array, Collator::SORT_REGULAR)); +var_dump($coll->getErrorCode() === U_ZERO_ERROR); +var_dump($array); + +$array = ['b', '9']; +var_dump($coll->sort($array, Collator::SORT_REGULAR)); +var_dump($coll->getErrorCode() === U_ZERO_ERROR); +var_dump($array); +?> +--EXPECT-- +bool(false) +bool(true) +int(1) +bool(true) +Collator::sort(): Error converting object string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +Collator::sort(): Error converting object string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(false) +bool(true) +Collator::sort(): Error converting object string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +string(1) "a" +bool(true) +bool(true) +bool(true) +array(2) { + [0]=> + string(1) "9" + [1]=> + string(2) "10" +} +bool(true) +bool(true) +array(2) { + [0]=> + string(1) "9" + [1]=> + string(1) "b" +} diff --git a/ext/intl/tests/collator_sort_error_reset_reentrancy.phpt b/ext/intl/tests/collator_sort_error_reset_reentrancy.phpt new file mode 100644 index 000000000000..ac218e276933 --- /dev/null +++ b/ext/intl/tests/collator_sort_error_reset_reentrancy.phpt @@ -0,0 +1,36 @@ +--TEST-- +Collator::sort() conversion failure must survive a re-entrant Collator call in __toString() +--EXTENSIONS-- +intl +--FILE-- +getLocale(Locale::VALID_LOCALE); + return "\xFF"; + } +} + +$coll = new Collator('en_US'); +BadAfterReentry::$coll = $coll; + +$array = ['a', new BadAfterReentry(), 'b']; + +var_dump($coll->sort($array, Collator::SORT_STRING)); +var_dump(BadAfterReentry::$called); +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), PHP_EOL; +var_dump($array[0], $array[1] instanceof BadAfterReentry, $array[2]); +?> +--EXPECT-- +bool(false) +bool(true) +bool(true) +Collator::sort(): Error converting string from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +string(1) "a" +bool(true) +string(1) "b" diff --git a/ext/intl/tests/collator_sort_nested_compare_func.phpt b/ext/intl/tests/collator_sort_nested_compare_func.phpt new file mode 100644 index 000000000000..bc08a466f79b --- /dev/null +++ b/ext/intl/tests/collator_sort_nested_compare_func.phpt @@ -0,0 +1,63 @@ +--TEST-- +Collator::sort() must not let a nested sort change the running comparator +--EXTENSIONS-- +intl +--FILE-- +sort($inner, Collator::SORT_NUMERIC); + } + return 'm'; + } +} + +function names(array $a): array { + return array_map(static fn($v) => is_object($v) ? get_class($v) : $v, $a); +} + +$coll = new Collator('en_US'); +Nest::$coll = $coll; + +$a = ['20', '3', new Nest(), '100', '9']; +var_dump($coll->sort($a, Collator::SORT_STRING)); +var_dump(names($a)); + +Nest::$done = true; +$b = ['20', '3', new Nest(), '100', '9']; +var_dump($coll->sort($b, Collator::SORT_STRING)); +var_dump(names($b)); +?> +--EXPECT-- +bool(true) +array(5) { + [0]=> + string(3) "100" + [1]=> + string(2) "20" + [2]=> + string(1) "3" + [3]=> + string(1) "9" + [4]=> + string(4) "Nest" +} +bool(true) +array(5) { + [0]=> + string(3) "100" + [1]=> + string(2) "20" + [2]=> + string(1) "3" + [3]=> + string(1) "9" + [4]=> + string(4) "Nest" +} diff --git a/ext/intl/tests/collator_sort_numeric.phpt b/ext/intl/tests/collator_sort_numeric.phpt new file mode 100644 index 000000000000..33f000c10eb7 --- /dev/null +++ b/ext/intl/tests/collator_sort_numeric.phpt @@ -0,0 +1,83 @@ +--TEST-- +Collator::sort() and asort() SORT_NUMERIC paths +--EXTENSIONS-- +intl +--FILE-- +sort($array, Collator::SORT_NUMERIC)); +var_dump($coll->getErrorCode() === U_ZERO_ERROR); +var_dump($array); + +$array = ['ten' => '10', 'minus' => '-3', 'four' => '4.5', 'two' => '2']; +var_dump($coll->asort($array, Collator::SORT_NUMERIC)); +var_dump($coll->getErrorCode() === U_ZERO_ERROR); +var_dump(array_keys($array)); + +$array = ["\xFF", '10', '1']; +var_dump($coll->sort($array, Collator::SORT_NUMERIC)); +var_dump($array[0] === "\xFF"); +var_dump($array[1], $array[2]); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), "\n"; + +$array = ['ten' => '10', 'bad' => "\xFF", 'one' => '1']; +var_dump($coll->asort($array, Collator::SORT_NUMERIC)); +var_dump(array_keys($array)); +var_dump($array['bad'] === "\xFF"); +var_dump(intl_get_error_code() === U_INVALID_CHAR_FOUND); +echo intl_get_error_message(), "\n"; +var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); +echo $coll->getErrorMessage(), "\n"; +?> +--EXPECT-- +bool(true) +bool(true) +array(4) { + [0]=> + string(2) "-3" + [1]=> + string(1) "2" + [2]=> + string(3) "4.5" + [3]=> + string(2) "10" +} +bool(true) +bool(true) +array(4) { + [0]=> + string(5) "minus" + [1]=> + string(3) "two" + [2]=> + string(4) "four" + [3]=> + string(3) "ten" +} +bool(false) +bool(true) +string(2) "10" +string(1) "1" +bool(true) +Collator::sort(): Error converting hash from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +Collator::sort(): Error converting hash from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(false) +array(3) { + [0]=> + string(3) "ten" + [1]=> + string(3) "bad" + [2]=> + string(3) "one" +} +bool(true) +bool(true) +Collator::asort(): Error converting hash from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND +bool(true) +Collator::asort(): Error converting hash from UTF-8 to UTF-16: U_INVALID_CHAR_FOUND diff --git a/ext/intl/tests/collator_sort_stops_after_failure.phpt b/ext/intl/tests/collator_sort_stops_after_failure.phpt new file mode 100644 index 000000000000..b9166e6abb81 --- /dev/null +++ b/ext/intl/tests/collator_sort_stops_after_failure.phpt @@ -0,0 +1,32 @@ +--TEST-- +Collator::sort() must stop comparing once a conversion has failed +--EXTENSIONS-- +intl +--FILE-- +sort($array, $flag)); + var_dump($coll->getErrorCode() === U_INVALID_CHAR_FOUND); + var_dump(CountingBad::$calls); +} +?> +--EXPECT-- +bool(false) +bool(true) +int(1) +bool(false) +bool(true) +int(1) diff --git a/ext/intl/tests/collator_sort_tostring_throws.phpt b/ext/intl/tests/collator_sort_tostring_throws.phpt new file mode 100644 index 000000000000..309c641af2a7 --- /dev/null +++ b/ext/intl/tests/collator_sort_tostring_throws.phpt @@ -0,0 +1,67 @@ +--TEST-- +Collator::sort() must not reorder the array when __toString() fails +--EXTENSIONS-- +intl +--FILE-- + is_object($v) ? get_class($v) : $v, $a); +} + +$coll = new Collator('en_US'); + +foreach ([Collator::SORT_REGULAR, Collator::SORT_STRING] as $flag) { + $array = ['b', new Thrower(), 'a']; + try { + var_dump($coll->sort($array, $flag)); + } catch (Throwable $e) { + echo get_class($e), ': ', $e->getMessage(), PHP_EOL; + } + var_dump(names($array)); +} + +$array = ['b', new NoToString(), 'a']; +try { + var_dump($coll->sort($array, Collator::SORT_STRING)); +} catch (Throwable $e) { + echo get_class($e), ': ', $e->getMessage(), PHP_EOL; +} +var_dump(names($array)); +?> +--EXPECT-- +Exception: boom +array(3) { + [0]=> + string(1) "b" + [1]=> + string(7) "Thrower" + [2]=> + string(1) "a" +} +Exception: boom +array(3) { + [0]=> + string(1) "b" + [1]=> + string(7) "Thrower" + [2]=> + string(1) "a" +} +Error: Object of class NoToString could not be converted to string +array(3) { + [0]=> + string(1) "b" + [1]=> + string(10) "NoToString" + [2]=> + string(1) "a" +} From 78fc4cddfae46e2ca5b179bbb36259faa8c2c067 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 30 Jul 2026 01:21:12 +0800 Subject: [PATCH 2/5] ext/standard: Fix phpcredits() heading alignment (#22918) --- ext/standard/credits.c | 6 +++--- ext/standard/tests/general_functions/phpcredits.phpt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/standard/credits.c b/ext/standard/credits.c index 1fc74d72db35..affed4adfee0 100644 --- a/ext/standard/credits.c +++ b/ext/standard/credits.c @@ -35,7 +35,7 @@ PHPAPI ZEND_COLD void php_print_credits(int flag) /* {{{ */ /* Group */ php_info_print_table_start(); - php_info_print_table_header(1, "PHP Group"); + php_info_print_table_colspan_header(1, "PHP Group"); php_info_print_table_row(1, "Thies C. Arntzen, Stig Bakken, Shane Caraveo, Andi Gutmans, Rasmus Lerdorf, Sam Ruby, Sascha Schumann, Zeev Suraski, Jim Winstead, Andrei Zmievski"); php_info_print_table_end(); } @@ -44,9 +44,9 @@ PHPAPI ZEND_COLD void php_print_credits(int flag) /* {{{ */ /* Design & Concept */ php_info_print_table_start(); if (!sapi_module.phpinfo_as_text) { - php_info_print_table_header(1, "Language Design & Concept"); + php_info_print_table_colspan_header(1, "Language Design & Concept"); } else { - php_info_print_table_header(1, "Language Design & Concept"); + php_info_print_table_colspan_header(1, "Language Design & Concept"); } php_info_print_table_row(1, "Andi Gutmans, Rasmus Lerdorf, Zeev Suraski, Marcus Boerger"); php_info_print_table_end(); diff --git a/ext/standard/tests/general_functions/phpcredits.phpt b/ext/standard/tests/general_functions/phpcredits.phpt index 347f3018d49a..7c0895d4e0f7 100644 --- a/ext/standard/tests/general_functions/phpcredits.phpt +++ b/ext/standard/tests/general_functions/phpcredits.phpt @@ -15,10 +15,10 @@ var_dump(phpcredits(CREDITS_GROUP)); --EXPECTF-- PHP Credits -PHP Group +%wPHP Group%w %a -Language Design & Concept +%wLanguage Design & Concept%w %a %wPHP Authors%w @@ -45,6 +45,6 @@ bool(true) -- PHP Credits -PHP Group +%wPHP Group%w %a bool(true) From 24892648447d170ded6706a8d3c5473f9bdc679c Mon Sep 17 00:00:00 2001 From: NickSdot <32384907+NickSdot@users.noreply.github.com> Date: Thu, 30 Jul 2026 01:05:33 +0700 Subject: [PATCH 3/5] Tests: Fixes leaked System V shared-memory segments in tests (#22911) The two tests leave System V IPC segments behind after successful execution. Repeated test-suite runs can exhaust IPC limits and cause unrelated SHMOP/SysV tests to fail. --- ext/shmop/tests/shmop_open_private.phpt | 2 ++ ext/sysvshm/tests/gh16591.phpt | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ext/shmop/tests/shmop_open_private.phpt b/ext/shmop/tests/shmop_open_private.phpt index 8757de0a6334..0ba74e5af65c 100644 --- a/ext/shmop/tests/shmop_open_private.phpt +++ b/ext/shmop/tests/shmop_open_private.phpt @@ -13,6 +13,8 @@ $shm2 = shmop_open(0, 'c', 0777, 1024); $read = shmop_read($shm2, 0, 4); var_dump(is_string($read) && $read !== $write); +shmop_delete($shm1); +shmop_delete($shm2); ?> --EXPECT-- bool(true) diff --git a/ext/sysvshm/tests/gh16591.phpt b/ext/sysvshm/tests/gh16591.phpt index d3ece7cd796a..fb1c58168f57 100644 --- a/ext/sysvshm/tests/gh16591.phpt +++ b/ext/sysvshm/tests/gh16591.phpt @@ -13,11 +13,15 @@ class C { } } -$mem = shm_attach(1); +$key = ftok(__FILE__, 't'); +$mem = shm_attach($key); +$cleanup = shm_attach($key); try { shm_put_var($mem, 1, new C); } catch (Error $e) { echo $e->getMessage(), "\n"; +} finally { + shm_remove($cleanup); } ?> From 5eb9bbddb4cb4c8cd0560ad933f27514cbaf9e91 Mon Sep 17 00:00:00 2001 From: Sjoerd Langkemper Date: Wed, 29 Jul 2026 20:42:55 +0200 Subject: [PATCH 4/5] ext/standard: Warn when number base conversion loses precision (#22371) _php_math_basetozval is used for base_convert, bindec, hexdec and octdec. It uses an integer or double for the internal representation of the number. When the input number is too large to fit in there, it loses precision. This commit emits a notice when this happens. Discussion thread: https://news-web.php.net/php.internals/131364 PR: https://github.com/php/php-src/pull/22371 --- NEWS | 2 ++ .../zend_ini/zend_ini_parse_uquantity_overflow.phpt | 4 ++-- ext/standard/math.c | 1 + ext/standard/tests/math/base_convert_overflow.phpt | 10 ++++++++++ ext/standard/tests/math/bindec_basic.phpt | 2 ++ ext/standard/tests/math/bindec_basiclong_64bit.phpt | 8 +++++++- ext/standard/tests/math/gh22395.phpt | 3 ++- ext/standard/tests/math/hexdec.phpt | 12 ++++++++---- ext/standard/tests/math/hexdec_basic.phpt | 6 ++++++ ext/standard/tests/math/hexdec_basiclong_64bit.phpt | 8 +++++++- ext/standard/tests/math/hexdec_variation1.phpt | 8 ++++++++ ext/standard/tests/math/octdec_basic.phpt | 2 ++ ext/standard/tests/math/octdec_basiclong_64bit.phpt | 8 +++++++- 13 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 ext/standard/tests/math/base_convert_overflow.phpt diff --git a/NEWS b/NEWS index c144b3c62c23..cb70bda237ff 100644 --- a/NEWS +++ b/NEWS @@ -276,6 +276,8 @@ PHP NEWS (David Carlier) . TSRM: make CG, EG, SCNG and AG compile-time offsets. (henderkes) . Deprecate returning values from __construct() and __destruct(). (timwolla) + . base_convert, bindex, hexdec and octdec now raise a notice when they cannot + precisely convert the given number. (Sjoerd Langkemper) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt b/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt index 0da9c4fac976..f770ccbcaf20 100644 --- a/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt +++ b/Zend/tests/zend_ini/zend_ini_parse_uquantity_overflow.phpt @@ -15,8 +15,8 @@ $tests = [ 'No overflow 007' => ' -1', 'No overflow 008' => '-1 ', 'No overflow 009' => ' -1 ', - 'Subject overflow 001' => base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), - 'Subject overflow 002' => '-'.base_convert(str_repeat('1', PHP_INT_SIZE*8+1), 2, 10), + 'Subject overflow 001' => PHP_INT_MAX.'0', + 'Subject overflow 002' => PHP_INT_MIN.'0', 'Subject overflow 003' => strval(PHP_INT_MIN), 'Subject overflow 004' => '-2', 'Subject overflow 005' => '-1K', diff --git a/ext/standard/math.c b/ext/standard/math.c index 1498a1efc0b2..e13f153f63e2 100644 --- a/ext/standard/math.c +++ b/ext/standard/math.c @@ -899,6 +899,7 @@ PHPAPI void _php_math_basetozval(zend_string *str, int base, zval *ret) num = num * base + c; break; } else { + zend_error(E_NOTICE, "Input number is larger than PHP_INT_MAX, precision has been lost in conversion"); fnum = (double)num; mode = 1; } diff --git a/ext/standard/tests/math/base_convert_overflow.phpt b/ext/standard/tests/math/base_convert_overflow.phpt new file mode 100644 index 000000000000..0f74bdc76a63 --- /dev/null +++ b/ext/standard/tests/math/base_convert_overflow.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test base_convert() - overflow test +--FILE-- + +--EXPECTF-- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +string(18) "2ee03c32ad644bd1e1" \ No newline at end of file diff --git a/ext/standard/tests/math/bindec_basic.phpt b/ext/standard/tests/math/bindec_basic.phpt index 7640171fe61b..d1197ed1645c 100644 --- a/ext/standard/tests/math/bindec_basic.phpt +++ b/ext/standard/tests/math/bindec_basic.phpt @@ -50,6 +50,8 @@ int(129) int(455) int(224) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/bindec_basiclong_64bit.phpt b/ext/standard/tests/math/bindec_basiclong_64bit.phpt index b4f28a091dbb..eb648c7c535f 100644 --- a/ext/standard/tests/math/bindec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/bindec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($binLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 0111111111111111111111111111111111111111111111111111111111111111 --- int(9223372036854775807) --- testing: 1111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 01111111111111111111111111111111 --- int(2147483647) --- testing: 11111111111111111111111111111111 --- int(4294967295) --- testing: 01111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 11111111111111111111111111111111111111111111111111111111111111111 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(3.6893488147419103E+19) --- testing: 011111111111111111111111111111111 --- int(4294967295) diff --git a/ext/standard/tests/math/gh22395.phpt b/ext/standard/tests/math/gh22395.phpt index 73c2c66da199..6ccbb44d669f 100644 --- a/ext/standard/tests/math/gh22395.phpt +++ b/ext/standard/tests/math/gh22395.phpt @@ -6,6 +6,7 @@ $result = base_convert(str_repeat("1", 61), 36, 16); var_dump(strlen($result)); var_dump(substr($result, 0, 13)); ?> ---EXPECT-- +--EXPECTF-- +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d int(78) string(13) "4b61b5e0639ff" diff --git a/ext/standard/tests/math/hexdec.phpt b/ext/standard/tests/math/hexdec.phpt index ba09875efc39..1f8b57d83fd0 100644 --- a/ext/standard/tests/math/hexdec.phpt +++ b/ext/standard/tests/math/hexdec.phpt @@ -10,8 +10,8 @@ var_dump(hexdec("12345")); var_dump(hexdec("q12345")); var_dump(hexdec("12345+?!")); var_dump(hexdec("12345q")); -var_dump((float)hexdec("1234500001")); -var_dump((float)hexdec("17fffffff")); +var_dump(hexdec("12345678901234567")); +var_dump(hexdec("17fffffffffffffff")); ?> --EXPECTF-- @@ -26,5 +26,9 @@ int(74565) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d int(74565) -float(78187069441) -float(6442450943) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +float(2.0988295476557332E+19) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d +float(2.7670116110564327E+19) diff --git a/ext/standard/tests/math/hexdec_basic.phpt b/ext/standard/tests/math/hexdec_basic.phpt index 2f06b9650aed..f1fd76778bfd 100644 --- a/ext/standard/tests/math/hexdec_basic.phpt +++ b/ext/standard/tests/math/hexdec_basic.phpt @@ -32,11 +32,17 @@ for ($i = 0; $i < count($values); $i++) { --EXPECTF-- int(18433668) int(126895953) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(142929835591) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(142929835592) int(1194684) int(7904751) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/hexdec_basiclong_64bit.phpt b/ext/standard/tests/math/hexdec_basiclong_64bit.phpt index af0b448cac01..7475d982004c 100644 --- a/ext/standard/tests/math/hexdec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/hexdec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($hexLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 7fffffffffffffff --- int(9223372036854775807) --- testing: ffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 7fffffff --- int(2147483647) --- testing: ffffffff --- int(4294967295) --- testing: 7ffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.4757395258967641E+20) --- testing: ffffffffffffffffff --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(4.722366482869645E+21) --- testing: 7ffffffff --- int(34359738367) diff --git a/ext/standard/tests/math/hexdec_variation1.phpt b/ext/standard/tests/math/hexdec_variation1.phpt index 65ebbe347b9b..cf0d4e81411c 100644 --- a/ext/standard/tests/math/hexdec_variation1.phpt +++ b/ext/standard/tests/math/hexdec_variation1.phpt @@ -86,9 +86,13 @@ Deprecated: Invalid characters passed for attempted conversion, these have been int(9029) -- Iteration 5 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(285960729237) -- Iteration 6 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(285960729238) -- Iteration 7 -- @@ -102,10 +106,14 @@ Deprecated: Invalid characters passed for attempted conversion, these have been int(261) -- Iteration 9 -- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(20015998341120) -- Iteration 10 -- +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d + Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d float(1250999896553) diff --git a/ext/standard/tests/math/octdec_basic.phpt b/ext/standard/tests/math/octdec_basic.phpt index 4c3ffc8b8a91..935cb23e1d35 100644 --- a/ext/standard/tests/math/octdec_basic.phpt +++ b/ext/standard/tests/math/octdec_basic.phpt @@ -46,6 +46,8 @@ int(5349) int(342391) int(375) int(2147483647) + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2147483648) Deprecated: Invalid characters passed for attempted conversion, these have been ignored in %s on line %d diff --git a/ext/standard/tests/math/octdec_basiclong_64bit.phpt b/ext/standard/tests/math/octdec_basiclong_64bit.phpt index 06e0dd3929d2..4637893d3470 100644 --- a/ext/standard/tests/math/octdec_basiclong_64bit.phpt +++ b/ext/standard/tests/math/octdec_basiclong_64bit.phpt @@ -30,18 +30,24 @@ foreach ($octLongStrs as $strVal) { } ?> ---EXPECT-- +--EXPECTF-- --- testing: 777777777777777777777 --- int(9223372036854775807) --- testing: 1777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(1.8446744073709552E+19) --- testing: 17777777777 --- int(2147483647) --- testing: 37777777777 --- int(4294967295) --- testing: 377777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(2.3611832414348226E+21) --- testing: 17777777777777777777777777 --- + +Notice: Input number is larger than PHP_INT_MAX, precision has been lost in conversion in %s on line %d float(7.555786372591432E+22) --- testing: 377777777777 --- int(34359738367) From b0bce5f0da9397701dc9925355066df2eed091ac Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 30 Jul 2026 03:12:44 +0800 Subject: [PATCH 5/5] [skip ci] UPGRADING: add entry for #22371 --- UPGRADING | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UPGRADING b/UPGRADING index 47a9c023bf3f..599ae2290d6e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -226,6 +226,8 @@ PHP 8.6 UPGRADE NOTES UINT_MAX instead of allowing the value to overflow. . proc_open() now raises a ValueError when the $cwd argument contains NUL bytes. + . base_convert(), bindex(), hexdec() and octdec() now raise a notice when + they cannot precisely convert the given number. - Sysvshm: . shm_attach() now raises a ValueError when the $key argument is outside the