From 922baf65564cc3b05c6da0be37a2dbf548a8f238 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Mon, 24 Aug 2026 11:12:35 -0400 Subject: [PATCH] [intl] Fix empty-needle grapheme_strpos offsets grapheme_strpos_utf16() returned raw UTF-16 code-unit positions for an empty needle instead of grapheme counts, so multi-code-unit graphemes made strpos() and strrpos() over-report the offset. Convert the boundary position like the non-empty search path; the ASCII fast paths coincide and grapheme_strstr() consumes the separate raw UTF-16 out-parameter, which stays correct. --- NEWS | 2 ++ ext/intl/grapheme/grapheme_util.c | 6 +++- .../grapheme_empty_offset_multibyte.phpt | 36 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 ext/intl/tests/grapheme_empty_offset_multibyte.phpt diff --git a/NEWS b/NEWS index 608bacc29109..5254f40504c2 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,8 @@ PHP NEWS . Fixed bug GH-19320 (FPM UID and GID overflow). (Pratik Bhujel) - Intl: + . Fixed grapheme_strpos() and grapheme_strrpos() with an empty needle + returning UTF-16 offsets instead of grapheme offsets. (Ilia Alshanetsky) . Fixed a memory leak when dumping IntlCalendar instances. (Ilia Alshanetsky) . Fixed a memory leak when iterating IntlBreakIterator::getPartsIterator() results. (iliaal) diff --git a/ext/intl/grapheme/grapheme_util.c b/ext/intl/grapheme/grapheme_util.c index 501b9dfb221d..f0c79785483f 100644 --- a/ext/intl/grapheme/grapheme_util.c +++ b/ext/intl/grapheme/grapheme_util.c @@ -131,7 +131,11 @@ int32_t grapheme_strpos_utf16(char *haystack, size_t haystack_len, char *needle, ret_pos = -1; goto finish; } - ret_pos = last && offset >= 0 ? uhaystack_len : offset_pos; + if (last && offset >= 0) { + ret_pos = grapheme_count_graphemes(bi, uhaystack, uhaystack_len); + } else { + ret_pos = grapheme_count_graphemes(bi, uhaystack, offset_pos); + } goto finish; } diff --git a/ext/intl/tests/grapheme_empty_offset_multibyte.phpt b/ext/intl/tests/grapheme_empty_offset_multibyte.phpt new file mode 100644 index 000000000000..caa545c882c8 --- /dev/null +++ b/ext/intl/tests/grapheme_empty_offset_multibyte.phpt @@ -0,0 +1,36 @@ +--TEST-- +grapheme_strpos() family with empty needle and offset on multi-code-unit graphemes +--EXTENSIONS-- +intl +--FILE-- +getMessage(), "\n"; +} + +?> +--EXPECT-- +int(0) +int(0) +int(1) +int(1) +int(1) +int(2) +int(2) +int(2) +int(1) +ValueError: grapheme_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)