Signature
mb_strrchr(
string $haystack,
string $needle,
bool $before_needle = false,
?string $encoding = null
): string|falsePurpose
mb_strrchr() looks for the last occurrence of a needle inside a haystack and returns a slice of the haystack built around that position. It is the multibyte-aware relative of strrchr(). Because it understands character encodings, it never cuts a character such as é or → in half, which a byte-oriented search can do when the data is UTF-8.
Typical uses are taking the last segment of a delimited value, such as a file name after the final slash, or taking everything before the final delimiter, such as the directory part.
Signature
Parameters and defaults
$haystack: the string to search.$needle: the string to find. Despite the word "character" in the function's short description, the needle is a string, so a multi-character delimiter such as::is searched as a whole. Since PHP 8.0 an empty needle is accepted.$before_needle: defaults tofalse. Withfalseyou get the text from the last match to the end of the haystack, and the match itself is included. Withtrueyou get the text from the start of the haystack up to the last match, and the match is excluded.$encoding: defaults tonull, which means the current internal encoding is used. Passing'UTF-8'explicitly makes your code independent of configuration.
Return value and failure cases
The function returns a string with the requested portion. If the needle is not found it returns false, not an empty string. Since PHP 8.0 an invalid encoding name throws a ValueError; before 8.0 it produced a warning instead.
Example: tail and head around the last slash
The first example splits a path at its last /. The default mode keeps the slash at the start of the result, and true returns what comes before it.
$path = 'docs/café/menu/été.txt';
echo mb_strrchr($path, '/', false, 'UTF-8'), "\n";
echo mb_strrchr($path, '/', true, 'UTF-8'), "\n";/été.txt docs/café/menu
Example: a missing needle returns false
When nothing matches, the result is the boolean false. A multibyte needle such as an arrow works like any other string.
$result = mb_strrchr('naïve', 'z', false, 'UTF-8');
var_dump($result);
$tag = mb_strrchr('a→b→c', '→', false, 'UTF-8');
echo json_encode($tag, JSON_UNESCAPED_UNICODE), "\n";bool(false) "→c"
Example: a multi-character delimiter
The needle can be longer than one character. Only the final :: counts, so earlier delimiters stay in the head part.
$text = 'one::två::tre';
echo json_encode([
'tail' => mb_strrchr($text, '::', false, 'UTF-8'),
'head' => mb_strrchr($text, '::', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE), "\n";{"tail":"::tre","head":"one::två"}
Edge cases
- Match included or excluded. With the default
before_needle = falsethe needle is the first thing in the result. If you want only the text after the delimiter, you must remove the delimiter yourself, for example withmb_substr()andmb_strlen($needle, 'UTF-8'). - Only the last match matters. If the needle occurs many times, earlier occurrences are ignored in the tail result and kept in the head result.
- Case sensitivity. The search is case-sensitive. For a case-insensitive variant use
mb_strrichr(). - Empty haystack. Searching an empty string for a non-empty needle yields
false. - Encoding mismatch. If the string is stored in an encoding different from the one you pass, the match boundaries can be wrong. Be consistent about the encoding of your data.
- Invalid encoding name. A name PHP does not recognize raises
ValueErrorin PHP 8, so wrap user-supplied encoding names in validation or atryblock.
Common mistakes
- Testing the result with a loose check. An
if (mb_strrchr(...))test treats a result such as"0"as false. Compare with=== falseinstead. - Expecting an empty string when nothing is found. The not-found value is
false, which can silently turn into""when concatenated or echoed. - Forgetting that the needle is kept. Beginners often expect
/file.txtto arrive asfile.txt. - Mixing up the flag.
truemeans "give me the part before the needle", not "search from the end"; the search always goes for the last occurrence. - Relying on the internal encoding. If the default encoding differs between environments, behavior can differ too. Pass
'UTF-8'when your data is UTF-8. - Using the byte function on multibyte text.
strrchr()operates on bytes, so with non-ASCII needles it is the wrong tool when character boundaries matter.
Related functions
strrchr(): the byte-based version.mb_strstr(): finds the first occurrence and returns a portion of the string.mb_strrichr(): the case-insensitive last-occurrence variant.mb_strrpos(): returns the character position of the last match instead of a slice.mb_substr(): extracts a slice by character offset, useful for trimming the delimiter.
Recap
mb_strrchr() finds the last occurrence of a needle in a multibyte string and returns either the tail starting at the match or, with before_needle = true, the head before it. It returns false when there is no match, so compare strictly. Pass an explicit encoding, remember that the needle is included in the tail, and reach for the related functions when you need case-insensitivity or a numeric position.
Tested examples
$path = 'docs/café/menu/été.txt';
echo mb_strrchr($path, '/', false, 'UTF-8'), "\n";
echo mb_strrchr($path, '/', true, 'UTF-8'), "\n";
/été.txt docs/café/menu
$result = mb_strrchr('naïve', 'z', false, 'UTF-8');
var_dump($result);
$tag = mb_strrchr('a→b→c', '→', false, 'UTF-8');
echo json_encode($tag, JSON_UNESCAPED_UNICODE), "\n";
bool(false) "→c"
$text = 'one::två::tre';
echo json_encode([
'tail' => mb_strrchr($text, '::', false, 'UTF-8'),
'head' => mb_strrchr($text, '::', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE), "\n";
{"tail":"::tre","head":"one::två"}