From 838ad876fa02f6f8bae07c4c39165b25ff053190 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Thu, 3 Sep 2026 11:53:47 -0300 Subject: [PATCH 1/5] [skip ci] Document error_include_args in NEWS and UPGRADING (#23487) Not sure how backdated NEWS will be useful, but this does add the note in UPGRADING which does matter. Per https://github.com/php/php-src/pull/12276#issuecomment-5388504104 --- NEWS | 2 ++ UPGRADING | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/NEWS b/NEWS index 623a29b0d6c8..e507963f2059 100644 --- a/NEWS +++ b/NEWS @@ -663,6 +663,8 @@ PHP NEWS . 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) + . Added error_include_args INI option to make the display of function + arguments consistent in error output. (Calvin Buckley) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa) diff --git a/UPGRADING b/UPGRADING index 596db8fb4267..9a509f01e72b 100644 --- a/UPGRADING +++ b/UPGRADING @@ -913,6 +913,15 @@ PHP 8.6 UPGRADE NOTES 11. Changes to INI File Handling ======================================== +- Core: + . The error_include_args INI option has been added to make the display of + function arguments consistent in errors; it is off by default. Previously, + some functions shown a parameter in the error that was up to each call to + the internal error function. Now, all parameters as were actually passed to + the function will be displayed. This uses the same infrastructure as stack + traces, so i.e. sensitive parameters will not be displayed, and strings + will be truncated according to zend.exception_string_param_max_len. + - Mbstring: . The mbstring.detect_order INI directive now updates the internal detection order when changed at runtime via ini_set(). Previously, runtime changes From 36d8c1bf43e0ea2011f0a66eab43da7ab79d0124 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Thu, 3 Sep 2026 23:59:51 +0800 Subject: [PATCH 2/5] [skip ci] Document Uri\WhatWg\UrlBuilder as a new class --- UPGRADING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING b/UPGRADING index 9a509f01e72b..5d65ffd0427f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -819,7 +819,7 @@ PHP 8.6 UPGRADE NOTES . StreamPollHandle - URI: - . Uri\Rfc3986\UriBuilder + . Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder RFC: https://wiki.php.net/rfc/uri_followup#uri_building ======================================== From 8994d5f3c6f63fa697dad352c9f2ca821f145331 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 4 Sep 2026 00:12:27 +0800 Subject: [PATCH 3/5] [skip ci] UPGRADING: Document ldap_free_result() return type narrowing Per 4f02d15. Since similar changes to zip_entry_close and header_register_callback has already been documented, this also should be documented. --- UPGRADING | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/UPGRADING b/UPGRADING index 5d65ffd0427f..a0999d2d7d31 100644 --- a/UPGRADING +++ b/UPGRADING @@ -658,6 +658,10 @@ PHP 8.6 UPGRADE NOTES . gmp_powm() modulo-by-zero now raises a DivisionByZeroError whose message includes the function name and argument index ($modulus). +- LDAP: + . ldap_free_result() return type has been narrowed from bool to true. The + function already always returned true. + - MySQLi: . The return structure of mysqli_get_charset() no longer contains the undocumented "comment" element. The value of "charsetnr" is now set to a From cd236075a872744300ab98a63ebc7b365c6befe5 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 4 Sep 2026 00:17:35 +0800 Subject: [PATCH 4/5] [skip ci] UPGRADING: Fix the documentation of session_module_name containing NUL bytes A ValueError is indeed thrown after this case. --- UPGRADING | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADING b/UPGRADING index a0999d2d7d31..5838bd60e659 100644 --- a/UPGRADING +++ b/UPGRADING @@ -133,7 +133,7 @@ PHP 8.6 UPGRADE NOTES unchanged. Previously, NUL bytes were silently accepted: for cookie_path and cookie_domain this caused the SAPI to drop the Set-Cookie header; for cache_limiter the value was silently truncated at the NUL byte. - . A ValueError is not thrown if $name is a string containing NUL bytes in + . A ValueError is thrown if $name is a string containing NUL bytes in session_module_name(). . session_encode() now returns an empty string instead of false for empty sessions. It only returns false now when the session data could not be From 0bf872bebf511f0c69f15bf5d9b86eb7176fd0eb Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Fri, 4 Sep 2026 00:19:24 +0800 Subject: [PATCH 5/5] Zend: Optimize sorting single-element arrays (#23550) Use zend_hash_sort_ex() directly for single-element arrays to avoid unpacking packed arrays and temporarily increasing the array refcount when the comparator cannot be invoked anyway. Keep the existing renumbering behavior for sort()-style operations and add behavioral tests covering single-element sorting. --- UPGRADING | 1 + Zend/zend_hash.c | 14 ++++- Zend/zend_hash.h | 7 ++- .../tests/array/sort/sort_single_element.phpt | 57 +++++++++++++++++++ 4 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 ext/standard/tests/array/sort/sort_single_element.phpt diff --git a/UPGRADING b/UPGRADING index 5838bd60e659..feaec16c4826 100644 --- a/UPGRADING +++ b/UPGRADING @@ -1017,6 +1017,7 @@ PHP 8.6 UPGRADE NOTES . Reduced temporary allocations when iterating Phar directories. - Standard: + . Improved performance of sorting single-element arrays. . Improved performance of array_fill_keys(). . Improved performance of array_intersect(). . Improved performance of array_map() with multiple arrays passed. diff --git a/Zend/zend_hash.c b/Zend/zend_hash.c index 99406f9e4192..84751a02e7c0 100644 --- a/Zend/zend_hash.c +++ b/Zend/zend_hash.c @@ -2997,9 +2997,17 @@ static void zend_hash_sort_internal(HashTable *ht, sort_func_t sort, bucket_comp IS_CONSISTENT(ht); - if (!(ht->nNumOfElements>1) && !(renumber && ht->nNumOfElements>0)) { - /* Doesn't require sorting */ - return; + if (ht->nNumOfElements <= 1) { + if (!renumber || ht->nNumOfElements == 0) { + /* Doesn't require sorting */ + return; + } + if (sort == zend_sort && HT_IS_PACKED(ht) && HT_IS_WITHOUT_HOLES(ht)) { + /* The single element already has the expected index. */ + ht->nInternalPointer = 0; + ht->nNextFreeElement = 1; + return; + } } if (HT_IS_PACKED(ht)) { diff --git a/Zend/zend_hash.h b/Zend/zend_hash.h index 1181bee29fae..c7cdbe95866e 100644 --- a/Zend/zend_hash.h +++ b/Zend/zend_hash.h @@ -312,7 +312,12 @@ static zend_always_inline void ZEND_FASTCALL zend_hash_sort(HashTable *ht, bucke * trigger user code. It will ensure the user code cannot free the array during * sorting. */ static zend_always_inline void zend_array_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber) { - zend_array_sort_ex(ht, zend_sort, compare_func, renumber); + /* zend_sort() cannot invoke the comparator for at most one element. */ + if (ht->nNumOfElements <= 1) { + zend_hash_sort_ex(ht, zend_sort, compare_func, renumber); + } else { + zend_array_sort_ex(ht, zend_sort, compare_func, renumber); + } } static zend_always_inline uint32_t zend_hash_num_elements(const HashTable *ht) { diff --git a/ext/standard/tests/array/sort/sort_single_element.phpt b/ext/standard/tests/array/sort/sort_single_element.phpt new file mode 100644 index 000000000000..d7d63e98c733 --- /dev/null +++ b/ext/standard/tests/array/sort/sort_single_element.phpt @@ -0,0 +1,57 @@ +--TEST-- +Sorting single-element arrays does not invoke the comparison function +--FILE-- + $b; +}; + +$array = [42]; +// Keep the array packed and without holes, but leave the next free index at 11. +$array[10] = 99; +unset($array[10]); +next($array); +var_dump(usort($array, $compare)); +var_dump($array, key($array)); +$array[] = 43; +var_dump($array); + +$array = ['answer' => 42]; +next($array); +var_dump(usort($array, $compare)); +var_dump($array, key($array)); +$array[] = 43; +var_dump($array); + +var_dump($calls); + +?> +--EXPECT-- +bool(true) +array(1) { + [0]=> + int(42) +} +int(0) +array(2) { + [0]=> + int(42) + [1]=> + int(43) +} +bool(true) +array(1) { + [0]=> + int(42) +} +int(0) +array(2) { + [0]=> + int(42) + [1]=> + int(43) +} +int(0)