Signature
mb_strripos(
string $haystack,
string $needle,
int $offset = 0,
?string $encoding = null
): int|falsePurpose
mb_strripos() returns the position of the last occurrence of a substring inside another string, ignoring letter case. It is the multibyte-aware counterpart of strripos(). Positions are counted in characters, not bytes, and the first character is at position 0.
Use it when text may contain non-ASCII letters such as accented Latin, Greek or Cyrillic characters and you need to find the final match, for example the last mention of a name in a title, or the last separator in a user-supplied label.
Signature
Parameters and defaults
$haystack: the string to search in.$needle: the substring to look for. Since PHP 8.0 an empty string is accepted.$offset: defaults to0. A positive value starts the search that many characters into the haystack. A negative value stops the search at a point before the end of the haystack, so matches near the end are ignored.$encoding: defaults tonull, which means the current internal encoding is used. Passing'UTF-8'explicitly makes your code independent of that setting.
Return value and failure cases
The function returns an int character position when a match is found, or false when the needle does not occur. Position 0 is a valid result, so always compare with !==.
Two situations throw a ValueError:
$offsetis greater than the length of the haystack, measured in characters.$encodingis not a valid encoding name. Before PHP 8.0 this produced a warning instead.
Example 1: find and slice from the last match
The haystack contains three spellings of the same word. The search ignores case and reports the last one. The position can be passed straight to mb_substr(), because both functions count characters.
$text = 'PHP is fun. php is flexible. Php is everywhere.';
$position = mb_strripos($text, 'php', 0, 'UTF-8');
echo json_encode($position), "\n";
if ($position !== false) {
echo mb_substr($text, $position, null, 'UTF-8'), "\n";
}29 Php is everywhere.
Example 2: character positions versus byte positions
In UTF-8, É and é each use two bytes. The multibyte function folds their case and counts characters. The plain strripos() function counts bytes and does not treat É as a case variant of é, so it matches an earlier occurrence and reports a byte offset.
$text = 'Élan, élan, ÉLAN';
echo json_encode([
'mb_strripos' => mb_strripos($text, 'élan', 0, 'UTF-8'),
'strripos' => strripos($text, 'élan'),
]), "\n";{"mb_strripos":12,"strripos":7}
The two numbers differ in both meaning and value. Only the first is a character index suitable for the other mb_* functions.
Example 3: offsets and the ValueError
A positive offset skips the beginning of the haystack. If the last match starts before the offset, the result is false. An offset beyond the string length throws.
$text = 'abcABCabc';
echo json_encode([
'whole' => mb_strripos($text, 'ABC', 0, 'UTF-8'),
'from3' => mb_strripos($text, 'abc', 3, 'UTF-8'),
'from7' => mb_strripos($text, 'abc', 7, 'UTF-8'),
]), "\n";
try {
mb_strripos($text, 'abc', 20, 'UTF-8');
} catch (ValueError $e) {
echo get_class($e), "\n";
}{"whole":6,"from3":6,"from7":false}
ValueError
The text has nine characters. The last abc begins at position 6, so starting at 3 still finds it, while starting at 7 leaves too little text for a match.
Edge cases
- A match at the very start returns
0, which is falsy but not a failure. - Case-insensitive matching follows Unicode case folding provided by mbstring, so it can behave differently from ASCII-only comparisons.
- Negative offsets limit where the search may end. Test them with your own data rather than assuming they behave like a substring slice.
- An offset equal to the haystack length is allowed, but a non-empty needle cannot be found there.
- Empty needles are accepted since PHP 8.0. Treat that as an unusual input and validate it first if your logic depends on the result.
Common mistakes
- Using
if (mb_strripos(...)). A result of0is a successful match at the first character. Use!== false. - Mixing byte and character functions. Feeding a result from
mb_strripos()tosubstr()cuts multibyte text in the wrong place. Pair it withmb_substr(). - Relying on the default encoding. If the internal encoding differs from the data, positions are wrong. Pass
'UTF-8'when your strings are UTF-8. - Expecting case-sensitive matching. Use
mb_strrpos()whenaandAmust be different. - Forgetting validation of offsets. User-controlled offsets larger than the string length raise
ValueError.
Related functions
mb_strrpos(): last occurrence, case-sensitive.strripos(): last occurrence, case-insensitive, byte-based.strrpos(): last occurrence, case-sensitive, byte-based.mb_substr(): extract text using character positions.
Recap
mb_strripos() finds the last occurrence of a needle regardless of letter case and reports a zero-based character index, or false when there is no match. Pass an explicit encoding, compare strictly against false, keep offsets within the string length, and use other mb_* functions when working with the returned position.
Tested examples
$text = 'PHP is fun. php is flexible. Php is everywhere.';
$position = mb_strripos($text, 'php', 0, 'UTF-8');
echo json_encode($position), "\n";
if ($position !== false) {
echo mb_substr($text, $position, null, 'UTF-8'), "\n";
}
29 Php is everywhere.
$text = 'Élan, élan, ÉLAN';
echo json_encode([
'mb_strripos' => mb_strripos($text, 'élan', 0, 'UTF-8'),
'strripos' => strripos($text, 'élan'),
]), "\n";
{"mb_strripos":12,"strripos":7}
$text = 'abcABCabc';
echo json_encode([
'whole' => mb_strripos($text, 'ABC', 0, 'UTF-8'),
'from3' => mb_strripos($text, 'abc', 3, 'UTF-8'),
'from7' => mb_strripos($text, 'abc', 7, 'UTF-8'),
]), "\n";
try {
mb_strripos($text, 'abc', 20, 'UTF-8');
} catch (ValueError $e) {
echo get_class($e), "\n";
}
{"whole":6,"from3":6,"from7":false}
ValueError