The grapheme_* functions work on graphemes, the characters a person actually sees on screen, and one grapheme can be made of several Unicode code points. Until now these functions ignored the locale, the language and region settings that shape how text is compared. This RFC from Yuya Hamada adds a $locale parameter so they can follow a specific language's rules.

Show me

Turkish is a good example. In Turkish, the dotted capital İ matches a lowercase i, but in English it doesn't:

var_dump(grapheme_stripos("i", "\u{0130}", 0, "tr_TR")); // 0
var_dump(grapheme_stripos("i", "\u{0130}", 0, "en_US")); // false

The new parameter goes last, with an empty string as the default:

function grapheme_stripos(
    string $haystack,
    string $needle,
    int $offset = 0,
    string $locale = ""
): int|false {}

Which functions change

These functions get the new $locale parameter:

  • grapheme_strpos() and grapheme_stripos()
  • grapheme_strrpos() and grapheme_strripos()
  • grapheme_strstr() and grapheme_stristr()
  • grapheme_substr()
  • grapheme_levenshtein()

The locale string follows LDML, the Unicode standard for locale identifiers, and it can carry extra settings. For example, ja_JP-u-ks-identic asks for an identical match, which changes how some Chinese, Japanese and Korean characters compare:

$nabe = '邊';
$nabe_E0101 = "邊\u{E0101}";
var_dump(grapheme_strpos($nabe, $nabe_E0101)); // 0
var_dump(grapheme_strpos($nabe, $nabe_E0101, locale: "ja_JP-u-ks-identic")); // false

An earlier version also added a $strength parameter. It was dropped to keep the API smaller, since the locale string can already set it. The case-insensitive grapheme_stri* functions keep their current matching strength.

If the locale isn't valid, the function returns false, and you can get the reason from intl_get_error_code() and intl_get_error_message().

What it means for existing code

Nothing breaks. The new parameter has a default value, so your current calls behave the same way.

The vote

It passed 7 to 0, clearing the two-thirds majority it needed. Voting closed on August 9, 2025. The RFC targeted PHP 8.5, and the page marks it as implemented.