Commit 1adadf07e52 for php.net

commit 1adadf07e5223b43612afe25e082fe33dd456830
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Aug 24 11:12:35 2026 -0400

    [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.

    Closes GH-23519

diff --git a/NEWS b/NEWS
index 927ebe121e8..ee0cf1b9c1d 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 501b9dfb221..f0c79785483 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 00000000000..caa545c882c
--- /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--
+<?php
+
+ini_set("intl.error_level", E_WARNING);
+
+var_dump(grapheme_strpos("😀x", ""));
+var_dump(grapheme_strpos("😀x", "", 0));
+var_dump(grapheme_strpos("😀x", "", 1));
+var_dump(grapheme_stripos("😀x", "", 1));
+var_dump(grapheme_strpos("😀x", "", -1));
+var_dump(grapheme_strrpos("😀x", ""));
+var_dump(grapheme_strrpos("😀x", "", 1));
+var_dump(grapheme_strripos("😀x", "", 1));
+var_dump(grapheme_strrpos("😀x", "", -1));
+try {
+    var_dump(grapheme_strpos("😀x", "", 5));
+} catch (Throwable $e) {
+    echo $e::class, ': ', $e->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)