[php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6

From: Date: Thu, 01 Oct 2026 17:21:02 +0000
Subject: [php-src] PHP-8.6: Merge branch 'PHP-8.5' into PHP-8.6
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-141844@lists.php.net to get a copy of this message
Author: Weilin Du (LamentXU123) Date: 2026-10-02T01:19:44+08:00 Commit: https://github.com/php/php-src/commit/724599cee57ec84a757eba8b21ac53f3772b93b5 Raw diff: https://github.com/php/php-src/commit/724599cee57ec84a757eba8b21ac53f3772b93b5.diff Merge branch 'PHP-8.5' into PHP-8.6 * PHP-8.5: Merge branch 'PHP-8.4' into PHP-8.5 * codex/pr24029-php85: ext/intl: Fix grapheme_strstr() and grapheme_strpos() missing canonically equivalent matches (#24029) Changed paths: A ext/intl/tests/grapheme_search_empty_haystack.phpt A ext/intl/tests/grapheme_strpos_ascii_haystack.phpt A ext/intl/tests/grapheme_strstr_canonical_match.phpt M ext/intl/grapheme/grapheme_string.cpp M ext/intl/grapheme/grapheme_util.cpp Diff: diff --git a/ext/intl/grapheme/grapheme_string.cpp b/ext/intl/grapheme/grapheme_string.cpp index e2a6280bcaa6..acb7c50cb9eb 100644 --- a/ext/intl/grapheme/grapheme_string.cpp +++ b/ext/intl/grapheme/grapheme_string.cpp @@ -125,7 +125,9 @@ U_CFUNC PHP_FUNCTION(grapheme_strpos) if (found) { RETURN_LONG(found - haystack); } - RETURN_FALSE; + if (grapheme_ascii_check((unsigned char *)needle, needle_len) >= 0) { + RETURN_FALSE; + } } /* do utf16 part of the strpos */ @@ -563,7 +565,9 @@ static void strstr_common_handler(INTERNAL_FUNCTION_PARAMETERS, int f_ignore_cas Z_PARAM_PATH(locale, locale_len) ZEND_PARSE_PARAMETERS_END(); - if ( !f_ignore_case ) { + if ( !f_ignore_case + && grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 + && grapheme_ascii_check((unsigned char *)needle, needle_len) >= 0 ) { /* ASCII optimization: quick check to see if the string might be there */ found = php_memnstr(haystack, needle, needle_len, haystack + haystack_len); @@ -573,15 +577,12 @@ static void strstr_common_handler(INTERNAL_FUNCTION_PARAMETERS, int f_ignore_cas RETURN_FALSE; } - /* if it is there, and if the haystack is ascii, we are all done */ - if ( grapheme_ascii_check((unsigned char *)haystack, haystack_len) >= 0 ) { - size_t found_offset = found - haystack; + size_t found_offset = found - haystack; - if (part) { - RETURN_STRINGL(haystack, found_offset); - } else { - RETURN_STRINGL(found, haystack_len - found_offset); - } + if (part) { + RETURN_STRINGL(haystack, found_offset); + } else { + RETURN_STRINGL(found, haystack_len - found_offset); } } diff --git a/ext/intl/grapheme/grapheme_util.cpp b/ext/intl/grapheme/grapheme_util.cpp index e58510e011ca..9644df7f3591 100644 --- a/ext/intl/grapheme/grapheme_util.cpp +++ b/ext/intl/grapheme/grapheme_util.cpp @@ -114,6 +114,9 @@ U_CFUNC int32_t grapheme_strpos_utf16(char *haystack, size_t haystack_len, char if(puchar_pos) { *puchar_pos = -1; } + if (haystack_len == 0 && needle_len > 0) { + return -1; + } /* convert the strings to UTF-16. */ status = U_ZERO_ERROR; diff --git a/ext/intl/tests/grapheme_search_empty_haystack.phpt b/ext/intl/tests/grapheme_search_empty_haystack.phpt new file mode 100644 index 000000000000..6f3df10649c6 --- /dev/null +++ b/ext/intl/tests/grapheme_search_empty_haystack.phpt @@ -0,0 +1,53 @@ +--TEST-- +Grapheme searches handle an empty haystack without ICU errors +--EXTENSIONS-- +intl +--FILE-- +<?php +foreach ([0, 1] as $useExceptions) { + ini_set('intl.use_exceptions', (string) $useExceptions); + foreach (['grapheme_strpos', 'grapheme_stripos', 'grapheme_strrpos', + 'grapheme_strripos', 'grapheme_strstr', 'grapheme_stristr'] as $function) { + $results = []; + foreach (['a', "\u{00E9}", "e\u{0301}", "\r\n", ''] as $needle) { + if ($function === 'grapheme_strstr' || $function === 'grapheme_stristr') { + $results[] = [$function('', $needle), $function('', $needle, true)]; + } else { + $results[] = $function('', $needle); + } + } + echo $function, ': ', json_encode($results), ', error: ', intl_get_error_code(), "\n"; + } +} + +foreach (['grapheme_strpos', 'grapheme_stripos', 'grapheme_strrpos', 'grapheme_strripos'] as $function) { + foreach ([-1, 1] as $offset) { + try { + $function('', "\u{00E9}", $offset); + } catch (ValueError $e) { + echo $function, '(', $offset, '): ', $e::class, "\n"; + } + } +} +?> +--EXPECT-- +grapheme_strpos: [false,false,false,false,0], error: 0 +grapheme_stripos: [false,false,false,false,0], error: 0 +grapheme_strrpos: [false,false,false,false,0], error: 0 +grapheme_strripos: [false,false,false,false,0], error: 0 +grapheme_strstr: [[false,false],[false,false],[false,false],[false,false],["",""]], error: 0 +grapheme_stristr: [[false,false],[false,false],[false,false],[false,false],["",""]], error: 0 +grapheme_strpos: [false,false,false,false,0], error: 0 +grapheme_stripos: [false,false,false,false,0], error: 0 +grapheme_strrpos: [false,false,false,false,0], error: 0 +grapheme_strripos: [false,false,false,false,0], error: 0 +grapheme_strstr: [[false,false],[false,false],[false,false],[false,false],["",""]], error: 0 +grapheme_stristr: [[false,false],[false,false],[false,false],[false,false],["",""]], error: 0 +grapheme_strpos(-1): ValueError +grapheme_strpos(1): ValueError +grapheme_stripos(-1): ValueError +grapheme_stripos(1): ValueError +grapheme_strrpos(-1): ValueError +grapheme_strrpos(1): ValueError +grapheme_strripos(-1): ValueError +grapheme_strripos(1): ValueError diff --git a/ext/intl/tests/grapheme_strpos_ascii_haystack.phpt b/ext/intl/tests/grapheme_strpos_ascii_haystack.phpt new file mode 100644 index 000000000000..43d6fa6b24b4 --- /dev/null +++ b/ext/intl/tests/grapheme_strpos_ascii_haystack.phpt @@ -0,0 +1,55 @@ +--TEST-- +grapheme_strpos() uses Unicode matching for non-ASCII needles in ASCII haystacks +--EXTENSIONS-- +intl +--INI-- +intl.use_exceptions=1 +--FILE-- +<?php +foreach ([ + ['K', "\u{212A}", 0], + ['aKbK', "\u{212A}", 0], + ['aKbK', "\u{212A}", 1], + ['aKbK', "\u{212A}", 2], + ['aKbK', "\u{212A}", 3], + ['aKbK', "\u{212A}", 4], + ['aKbK', "\u{212A}", -4], + ['aKbK', "\u{212A}", -2], + ['abc', "b\u{00AD}", 0], + ['abc', "\u{00E9}", 0], + ['abc', 'b', 1], + ['abc', 'b', 2], + ['', "\u{00E9}", 0], + ['', "e\u{0301}", 0], + ['', "\r\n", 0], + ['', '', 0], + ['abc', '', 3], +] as [$haystack, $needle, $offset]) { + var_dump(grapheme_strpos($haystack, $needle, $offset)); +} + +try { + grapheme_strpos('', "\u{212A}", 1); +} catch (ValueError $e) { + echo $e->getMessage(), "\n"; +} +?> +--EXPECT-- +int(0) +int(1) +int(1) +int(3) +int(3) +bool(false) +int(1) +int(3) +int(1) +bool(false) +int(1) +bool(false) +bool(false) +bool(false) +bool(false) +int(0) +int(3) +grapheme_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack) diff --git a/ext/intl/tests/grapheme_strstr_canonical_match.phpt b/ext/intl/tests/grapheme_strstr_canonical_match.phpt new file mode 100644 index 000000000000..96d8f6334b1a --- /dev/null +++ b/ext/intl/tests/grapheme_strstr_canonical_match.phpt @@ -0,0 +1,27 @@ +--TEST-- +grapheme_strstr() finds canonically equivalent strings with different byte representations +--EXTENSIONS-- +intl +--FILE-- +<?php +foreach ([ + ["e\u{0301}x", "\u{00E9}"], + ["\u{00E9}x", "e\u{0301}"], + ["pe\u{0301}x", "\u{00E9}"], + ["pe\u{0301}x\u{00E9}", "\u{00E9}"], + ['abc', 'b'], + ['abc', 'z'], +] as [$haystack, $needle]) { + echo json_encode([ + grapheme_strstr($haystack, $needle), + grapheme_strstr($haystack, $needle, true), + ]), "\n"; +} +?> +--EXPECT-- +["e\u0301x",""] +["\u00e9x",""] +["e\u0301x","p"] +["e\u0301x\u00e9","p"] +["bc","a"] +[false,false]

« previous php.cvs (#141844) next »