Signature
strrpos(string $haystack, string $needle, int $offset = 0): int|falsePurpose
strrpos() answers the question: where does a piece of text appear for the last time inside a larger string? It is the right-to-left counterpart of strpos(), which reports the first match. Typical uses include splitting a path at its final slash, finding the last dot in a file name, or locating the last separator in a delimited value.
Signature
Parameters and defaults
$haystackis the string to search in.$needleis the substring to look for. It may be longer than one character. Since PHP 8.0.0 an empty string is accepted, and integers are no longer treated as character codes, so always pass a string.$offsetdefaults to0. A zero or positive value skips that many bytes at the start of the haystack. A negative value starts the search that many bytes from the end and works right to left, so it finds the last match at or before that point.
The returned position is always counted from the beginning of $haystack, no matter which direction the search went or what offset you supplied. Positions start at 0.
Return value and failure cases
The function returns the integer position of the last match, or false when the needle is not found. If $offset is larger than the length of the haystack, PHP throws a ValueError.
Because a match at the very start of the string gives 0, and 0 is falsy, you must compare with === or !==. Writing if (strrpos($s, 'a')) treats a match at the start the same as no match.
Example 1: split a path at the last slash
The last separator marks where the directory ends and the file name begins.
$path = 'docs/guides/php/strings.md';
$pos = strrpos($path, '/');
echo json_encode([$pos, substr($path, $pos + 1), substr($path, 0, $pos)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[15,"strings.md","docs/guides/php"]
The slash is at byte 15. Everything after it is the file name, and everything before it is the directory.
Example 2: last match, multi-character needles and misses
$text = 'banana';
$results = [
'last_a' => strrpos($text, 'a'),
'last_na' => strrpos($text, 'na'),
'missing' => strrpos($text, 'x'),
'first_char' => strrpos('apple', 'a'),
];
echo json_encode($results), "\n";{"last_a":5,"last_na":4,"missing":false,"first_char":0}
The last a in banana is at position 5, and the last na begins at position 4. A missing needle gives false. In apple the only a is at position 0, which is a real match and not a failure.
Example 3: positive and negative offsets
$foo = '0123456789a123456789b123456789c';
$results = [
strrpos($foo, '0', 0),
strrpos($foo, '0', 1),
strrpos($foo, '7', 20),
strrpos($foo, '7', 28),
strrpos($foo, '7', -5),
strrpos($foo, 'c', -2),
strrpos($foo, '9c', -2),
];
echo json_encode($results), "\n";[0,false,27,false,17,false,29]
With a positive offset, earlier bytes are skipped, so '0' is not found when the search starts at byte 1. With -5, the search starts five bytes from the end and moves left, finding the 7 at position 17. The final result shows that a multi-character needle may begin before the starting point but still extend past it: '9c' is found at position 29 even with -2.
Edge cases
- Byte positions. Offsets and results are byte-based. In UTF-8 text, a character outside ASCII uses several bytes, so the numbers may not match visible character counts. For character-based positions use
mb_strrpos(). - Case sensitivity. The search is case-sensitive. Use
strripos()for a case-insensitive version. - Overlapping matches. Because the search looks for the last start position,
strrpos('banana', 'ana')returns the later of the overlapping candidates. - Offset too large. An offset beyond the haystack length throws a
ValueError, so validate offsets that come from outside your code. - Empty needle. Accepted since PHP 8.0.0, but it is rarely a meaningful search. Check for empty input first if that matters to you.
Common mistakes
- Using a truthiness test instead of
=== false, which confuses position0with not found. - Passing the result straight to
substr()without checking forfalse. - Expecting a negative offset to count the returned position from the end. The returned position is still measured from the start.
- Assuming byte positions are character positions in multibyte text.
- Passing an integer as the needle and expecting a character code lookup. That behavior was removed in PHP 8.0.0.
Related functions
strpos()finds the first occurrence.stripos()finds the first occurrence, ignoring case.strripos()finds the last occurrence, ignoring case.strrchr()returns the part of the string from the last occurrence of a character.substr()extracts a part of a string using positions.
Recap
strrpos() returns the byte position of the last occurrence of a needle, or false if there is none. Compare the result strictly, remember that a positive offset skips bytes and a negative one searches leftward from the end, and guard against offsets larger than the string.
Tested examples
$path = 'docs/guides/php/strings.md';
$pos = strrpos($path, '/');
echo json_encode([$pos, substr($path, $pos + 1), substr($path, 0, $pos)], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[15,"strings.md","docs/guides/php"]
$text = 'banana';
$results = [
'last_a' => strrpos($text, 'a'),
'last_na' => strrpos($text, 'na'),
'missing' => strrpos($text, 'x'),
'first_char' => strrpos('apple', 'a'),
];
echo json_encode($results), "\n";{"last_a":5,"last_na":4,"missing":false,"first_char":0}
$foo = '0123456789a123456789b123456789c';
$results = [
strrpos($foo, '0', 0),
strrpos($foo, '0', 1),
strrpos($foo, '7', 20),
strrpos($foo, '7', 28),
strrpos($foo, '7', -5),
strrpos($foo, 'c', -2),
strrpos($foo, '9c', -2),
];
echo json_encode($results), "\n";[0,false,27,false,17,false,29]