Signature
mb_strrpos(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): int|falsePurpose
mb_strrpos() answers the question "where does this text appear for the last time?" for strings that may contain multibyte characters. It is the multibyte-aware counterpart of strrpos(). The difference is the unit of measurement: strrpos() counts bytes, while mb_strrpos() counts characters in the chosen encoding. The first character is at position 0, the second at 1, and so on, always counted from the start of the haystack.
This matters with UTF-8 text. A letter such as é occupies two bytes, so a byte offset and a character offset diverge as soon as an accented letter appears before the match.
Signature
Parameters and defaults
$haystack: the string to search.$needle: the string to look for. Since PHP 8.0.0 an empty string is accepted.$offset(default0): 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, counted in characters from the end.$encoding(defaultnull): the character encoding. When omitted ornull, the internal encoding is used. Passing"UTF-8"explicitly makes code independent of configuration.
Return value and failures
The function returns the character position of the last match as an int. If the needle is not found it returns false. An offset of 0 is a valid position, so always compare with === false instead of relying on truthiness.
Two situations throw a ValueError: an $offset greater than the length of the haystack, and (since PHP 8.0.0) an invalid $encoding name. Before 8.0.0 an invalid encoding produced a warning and false.
Example 1: characters versus bytes
The string below contains é and à, which take two bytes each in UTF-8. The two functions find the same last été, but report different numbers.
$text = "été à Paris, été à Rome";
echo json_encode([
"characters" => mb_strrpos($text, "été", 0, "UTF-8"),
"bytes" => strrpos($text, "été"),
]), "\n";{"characters":13,"bytes":16}
Use the character position with other mb_* functions such as mb_substr(). Use the byte position only with byte-oriented functions.
Example 2: offsets and a miss
With a positive offset the search is limited to the part of the string beginning at that character. With a negative offset the search ignores the tail of the string. When nothing matches, the result is false.
$s = "a-b-c-d";
echo json_encode([
"last" => mb_strrpos($s, "-", 0, "UTF-8"),
"from2" => mb_strrpos($s, "-", 2, "UTF-8"),
"from6" => mb_strrpos($s, "-", 6, "UTF-8"),
"negative" => mb_strrpos($s, "-", -3, "UTF-8"),
]), "\n";{"last":5,"from2":5,"from6":false,"negative":3}
The last hyphen is at position 5. Starting from character 6 only d remains, so there is no match. With -3 the search stops before the final characters, so the hyphen at position 3 is the last one found.
Example 3: handling false and ValueError
A robust call distinguishes three outcomes: a position, no match, and an invalid argument.
$title = "Привет, мир! Привет!";
$missing = mb_strrpos($title, "Hello", 0, "UTF-8");
try {
mb_strrpos($title, "мир", 99, "UTF-8");
$error = null;
} catch (ValueError $e) {
$error = get_class($e);
}
echo json_encode([
"position" => mb_strrpos($title, "Привет", 0, "UTF-8"),
"missing" => $missing,
"error" => $error,
], JSON_UNESCAPED_UNICODE), "\n";{"position":13,"missing":false,"error":"ValueError"}
The offset 99 is larger than the haystack, so the function throws instead of returning false.
Edge cases
- A match at the very start returns
0, which is falsy but valid. - Overlapping candidates are resolved in favor of the rightmost start position.
- Because the needle may be empty since PHP 8.0.0, validate user input yourself if an empty search term should be rejected.
- The result depends on the encoding. Reading UTF-8 bytes with a different encoding name gives different positions.
Common mistakes
- Writing
if (mb_strrpos(...)). A position of0is treated as a failure. Use!== false. - Feeding a character position into
substr(). Pair it withmb_substr()instead. - Passing the encoding as the third argument. That older form was removed in PHP 8.0.0; the third argument is always the offset.
- Assuming a too-large offset just returns
false. It throws aValueError. - Relying on the default internal encoding when the data is known to be UTF-8. Pass
"UTF-8"explicitly.
Related functions
mb_strpos()finds the first occurrence.strrpos()is the byte-based equivalent.mb_internal_encoding()gets or sets the default encoding.mb_substr()extracts text by character position.
Recap
mb_strrpos() returns the character index of the last occurrence of a needle, or false when absent. Use strict comparison, pass an explicit encoding, keep offsets within the haystack length, and combine the result only with other multibyte-aware functions.
Tested examples
$text = "été à Paris, été à Rome";
echo json_encode([
"characters" => mb_strrpos($text, "été", 0, "UTF-8"),
"bytes" => strrpos($text, "été"),
]), "\n";
{"characters":13,"bytes":16}
$s = "a-b-c-d";
echo json_encode([
"last" => mb_strrpos($s, "-", 0, "UTF-8"),
"from2" => mb_strrpos($s, "-", 2, "UTF-8"),
"from6" => mb_strrpos($s, "-", 6, "UTF-8"),
"negative" => mb_strrpos($s, "-", -3, "UTF-8"),
]), "\n";
{"last":5,"from2":5,"from6":false,"negative":3}
$title = "Привет, мир! Привет!";
$missing = mb_strrpos($title, "Hello", 0, "UTF-8");
try {
mb_strrpos($title, "мир", 99, "UTF-8");
$error = null;
} catch (ValueError $e) {
$error = get_class($e);
}
echo json_encode([
"position" => mb_strrpos($title, "Привет", 0, "UTF-8"),
"missing" => $missing,
"error" => $error,
], JSON_UNESCAPED_UNICODE), "\n";
{"position":13,"missing":false,"error":"ValueError"}