Skip to content

ext/intl: Fix grapheme_extract() next offset - #24160

Merged
LamentXU123 merged 1 commit into
php:PHP-8.4from
LamentXU123:grapheme
Oct 7, 2026
Merged

LamentXU123 merged 1 commit into
php:PHP-8.4from
LamentXU123:grapheme

Conversation

@LamentXU123

Copy link
Copy Markdown
Member
<?php
var_dump(grapheme_extract("\u{00E9}x", 1, GRAPHEME_EXTR_COUNT, 1, $next));
var_dump($next);
string(1) "x"
int(2)

Next should be 3 instead.

Comment thread ext/intl/grapheme/grapheme_string.c Outdated

while ( !U8_IS_SINGLE(*pstr) && !U8_IS_LEAD(*pstr) ) {
pstr++;
start++;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: your solution looks fine but would this work ?

--- a/ext/intl/grapheme/grapheme_string.c
+++ b/ext/intl/grapheme/grapheme_string.c
@@ -785,7 +785,8 @@ PHP_FUNCTION(grapheme_extract)
              }
      }

-     str_len -= (pstr - str);
+     lstart = pstr - str;
+     str_len -= lstart;

      /* if the string is all ASCII up to size+1 - or str_len whichever is first - then we are done.
              (size + 1 because the size-th character might be the beginning of a grapheme cluster)
@@ -794,7 +795,7 @@ PHP_FUNCTION(grapheme_extract)
      if ( -1 != grapheme_ascii_check((unsigned char *)pstr, MIN(size + 1, str_len)) ) {
              size_t nsize = MIN(size, str_len);
              if ( NULL != next ) {
-                     ZEND_TRY_ASSIGN_REF_LONG(next, start + nsize);
+                     ZEND_TRY_ASSIGN_REF_LONG(next, lstart + nsize);
              }
              RETURN_STRINGL(pstr, nsize);
      }
@@ -828,7 +829,7 @@ PHP_FUNCTION(grapheme_extract)
      ubrk_close(bi);

      if ( NULL != next ) {
-             ZEND_TRY_ASSIGN_REF_LONG(next, start + ret_pos);
+             ZEND_TRY_ASSIGN_REF_LONG(next, lstart + ret_pos);
      }

      RETURN_STRINGL(((char *)pstr), ret_pos);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes indeed :)

When the starting position lies inside a multibyte UTF-8 character,
grapheme_extract() advances the cursor but reports a next offset based on
the original position. Derive the adjusted offset from the cursor for both
extraction paths.

Add regression coverage for extraction modes, ASCII and Unicode tails,
and negative starting offsets, and document the fix in NEWS.

Co-authored-by: David Carlier <devnexen@gmail.com>
@LamentXU123
LamentXU123 merged commit 6aff7e3 into php:PHP-8.4 Oct 7, 2026
18 checks passed
LamentXU123 added a commit that referenced this pull request Oct 7, 2026
* PHP-8.6:
  ext/intl: Fix grapheme_extract() next offset (#24160)
ArtUkrainskiy pushed a commit to ArtUkrainskiy/php-src that referenced this pull request Oct 7, 2026
* PHP-8.4:
  ext/intl: Fix grapheme_extract() next offset (php#24160)
ArtUkrainskiy pushed a commit to ArtUkrainskiy/php-src that referenced this pull request Oct 7, 2026
* PHP-8.5:
  ext/intl: Fix grapheme_extract() next offset (php#24160)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants