Multibyte strings7 min readExamples run on PHP 8.5.10

mb_strrichr()

Learn how mb_strrichr() returns the part of a string around the last case-insensitive match, with PHP 8.4 parameters, return values, examples and pitfalls.

Signature

mb_strrichr(
    string $haystack,
    string $needle,
    bool $before_needle = false,
    ?string $encoding = null
): string|false
Available since PHP 5.2.07.48.08.18.28.38.48.5All versions →

Purpose

mb_strrichr() searches a string for the last occurrence of a needle, ignoring case, and returns a slice of the haystack built around that match. It is the multibyte-aware, case-insensitive member of a small family: mb_strrchr() is the case-sensitive version, and mb_stristr() is the case-insensitive version that looks for the first match.

Use it when you want to cut a string at its final marker, such as the last separator in a path-like label or the last tag in a text, and the marker may appear in mixed case or contain non-ASCII letters.

Signature

Parameters and defaults

  • haystack: the string to search.
  • needle: the text to look for. Although the manual describes it as a character, it can be a longer string. Since PHP 8.0.0 an empty string is accepted as a needle.
  • before_needle: defaults to false. With false, the result starts at the last match and runs to the end of the haystack, and the match is included. With true, the result is everything from the start of the haystack up to the last match, and the match itself is not included.
  • encoding: the character encoding, defaulting to null. When it is null (or omitted), PHP uses the internal character encoding. Passing 'UTF-8' explicitly makes your code independent of that setting.

Return value and failure cases

The function returns a string containing the selected portion of the haystack. If the needle is not found it returns false, so you must compare the result with false using === instead of relying on truthiness.

Since PHP 8.0.0 an invalid encoding name throws a ValueError. Before 8.0.0 this produced an E_WARNING instead. A ValueError is a programming error, so it is normally fixed rather than caught.

Example 1: the last match, returned with the needle

The needle ALPHA differs in case from both occurrences in the haystack. The function picks the later one and returns it together with everything after it.

Example
<?php
$text = 'Alpha-BETA-alpha-Gamma';
$tail = mb_strrichr($text, 'ALPHA', false, 'UTF-8');
echo json_encode($tail, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
"alpha-Gamma"

The text returned keeps its original casing from the haystack, not the casing you typed in the needle.

Example 2: taking the part before the match

Setting before_needle to true flips the result to the leading portion.

Example
<?php
$text = 'Alpha-BETA-alpha-Gamma';
$head = mb_strrichr($text, 'ALPHA', true, 'UTF-8');
echo json_encode($head, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
"Alpha-BETA-"

Notice that the trailing hyphen is part of the result, because it comes before the matched text, while the matched alpha is excluded.

Example 3: no match and non-ASCII letters

This example shows both the false return and a case-insensitive match on accented letters.

Example
<?php
$result = [
    'missing' => mb_strrichr('abc', 'X', false, 'UTF-8'),
    'unicode' => mb_strrichr('Ünï-ÜNÏ-end', 'ünï', false, 'UTF-8'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
{"missing":false,"unicode":"ÜNÏ-end"}

A byte-based function such as strrchr() does not fold the case of Ü and ü, because those letters occupy several bytes in UTF-8. The multibyte function compares whole characters.

Edge cases

  • Only the last match counts. Earlier occurrences are ignored, even if they are closer to the start.
  • Overlapping text. The search finds the last position where the needle matches, so choose needles that are specific enough for your data.
  • Empty needle. It has been allowed since PHP 8.0.0. Because the result for this case is easy to misread, test it with your own data before depending on it.
  • Encoding mismatch. If the string is UTF-8 but the encoding argument says something else, the characters are interpreted incorrectly. Pass the real encoding.
  • Case rules. Case-insensitivity follows the character data of the chosen encoding. Characters whose case conversion is unusual can behave differently from a simple ASCII mental model, so do not assume more than the manual states.

Common mistakes

  1. Testing the result with if ($result). A valid result such as "0" is falsy in PHP. Use $result !== false.
  2. Expecting the first match. For the first match, use mb_stristr() instead.
  3. Expecting case-sensitive behavior. If case matters, use mb_strrchr().
  4. Forgetting that the needle is included. With the default before_needle = false, the returned string begins with the match. With true, it does not contain the match.
  5. Relying on the default encoding. The internal encoding may differ between environments. Passing 'UTF-8' makes the call predictable.
  6. Using it only to check existence. If you only need to know whether a string appears, a position function is more direct than slicing text you do not need.
  • mb_strrchr(): last occurrence, case-sensitive.
  • mb_stristr(): first occurrence, case-insensitive.
  • mb_strripos(): last position, case-insensitive, returning an offset instead of a substring.

Recap

mb_strrichr() finds the last case-insensitive match of a needle in a multibyte-aware way. By default it returns the match and everything after it; with before_needle set to true it returns everything before the match. It returns false when nothing matches, so check with ===, and pass an explicit encoding to keep behavior consistent.

Tested examples

Last case-insensitive match returned with the rest of the string
<?php
$text = 'Alpha-BETA-alpha-Gamma';
$tail = mb_strrichr($text, 'ALPHA', false, 'UTF-8');
echo json_encode($tail, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
"alpha-Gamma"
Using before_needle to get the leading part
<?php
$text = 'Alpha-BETA-alpha-Gamma';
$head = mb_strrichr($text, 'ALPHA', true, 'UTF-8');
echo json_encode($head, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
"Alpha-BETA-"
No match returns false; accented letters match regardless of case
<?php
$result = [
    'missing' => mb_strrichr('abc', 'X', false, 'UTF-8'),
    'unicode' => mb_strrichr('Ünï-ÜNÏ-end', 'ünï', false, 'UTF-8'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n";
Output
{"missing":false,"unicode":"ÜNÏ-end"}