From ee6f2351af8fea15b8640b3e280345e2a96ef737 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()/strrpos() over-report the offset. Convert the boundary position like the non-empty search path; sibling audit found the ASCII fast paths and the strstr() UTF-16 position consumer are unaffected. Signed-off-by: Ilia Alshanetsky --- 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 a2c65685b4ce..16684e86ebb2 100644 --- a/NEWS +++ b/NEWS @@ -28,6 +28,8 @@ PHP NEWS . Fixed Locale::parseLocale() reading past a trailing '-' or '_'. (iliaal, Xuyang Zhang) . Fixed grapheme_str_split() treating UBRK_DONE as a byte index. (iliaal) + . Fixed grapheme_strpos() and grapheme_strrpos() with an empty needle + returning UTF-16 offsets instead of grapheme offsets. (iliaal) - Opcache: . Fixed opcache.protect_memory race under ZTS. (realFlowControl) 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..c2f997140eb3 --- /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) +grapheme_strpos(): Argument #3 ($offset) must be contained in argument #1 ($haystack)