Multibyte strings7 min readExamples run on PHP 8.5.10

mb_stristr()

Learn how mb_stristr() returns part of a string from the first case-insensitive match of a needle, with UTF-8 examples, edge cases and common mistakes.

Signature

mb_stristr(
    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_stristr() looks for the first occurrence of a needle inside a haystack, ignoring letter case, and returns a slice of the haystack. It is the multibyte-aware counterpart of stristr(). It reads the strings as characters in a given encoding, such as UTF-8, instead of as raw bytes. Use it when you want to cut a string at a marker without caring whether the user typed Example, EXAMPLE or example, and the text may contain accented or other non-ASCII letters.

The function returns text, not a position. If you only need to know where the match starts, mb_stripos() is the better tool. If you want a yes/no answer, compare the result with false or use a dedicated containment check.

Signature

Parameters and defaults

  • $haystack: the string to search in.
  • $needle: the string to find. The match ignores case. Since PHP 8.0.0 an empty needle is accepted.
  • $before_needle: defaults to false. With false, the result starts at the first match and runs to the end of the haystack, so the needle is included. With true, the result is everything before the first match, and the needle is excluded.
  • $encoding: defaults to null, which means the current internal character encoding is used. Passing 'UTF-8' explicitly makes your code independent of that setting. Since PHP 8.0.0 the parameter is nullable.

Note that the returned text keeps the original casing of the haystack, not the casing of the needle you searched for.

Return value and failures

The function returns a string when the needle is found and false when it is not. An invalid encoding name throws a ValueError in PHP 8, where older versions emitted a warning. Because the return type is string|false, always check the result strictly.

Example 1: splitting an email address

The search term @example matches @Example even though the case differs. The first call keeps the match, the second stops before it.

Example
<?php
$email = 'Ana.Lopez@Example.COM';
echo json_encode([
    'from' => mb_stristr($email, '@example', false, 'UTF-8'),
    'before' => mb_stristr($email, '@EXAMPLE', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"from":"@Example.COM","before":"Ana.Lopez"}

Example 2: accented letters

The multibyte version compares characters, so BRÛ and brû both match the Brû in the haystack. The returned slice keeps the haystack's own letters.

Example
<?php
$dessert = 'Crème Brûlée';
echo json_encode([
    mb_stristr($dessert, 'BRÛ', false, 'UTF-8'),
    mb_stristr($dessert, 'brû', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["Brûlée","Crème "]

Example 3: falsy strings, misses and empty needles

This example shows why strict comparison matters. The match for 0 returns the string "0", which is a valid result but is falsy in PHP. A miss returns false. An empty needle matches at the very start, so the whole haystack comes back.

Example
<?php
$found = mb_stristr('Item 0', '0', false, 'UTF-8');
$missing = mb_stristr('Item 0', 'z', false, 'UTF-8');
$empty = mb_stristr('Hello', '', false, 'UTF-8');
echo json_encode([
    'found' => $found,
    'foundIsFalse' => $found === false,
    'foundTruthy' => (bool) $found,
    'missing' => $missing,
    'empty' => $empty,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"found":"0","foundIsFalse":false,"foundTruthy":false,"missing":false,"empty":"Hello"}

Edge cases

  • Only the first match counts. If the needle appears several times, everything is measured from the first occurrence.
  • Empty needle. It is accepted in PHP 8 and matches at the beginning of the haystack.
  • Encoding mismatch. If the bytes are not valid in the chosen encoding, results can be unreliable. Decide on an encoding for your data and pass it consistently.
  • Invalid encoding name. This throws a ValueError, so a typo in the argument is not silently ignored.
  • Case rules are character-based. The comparison follows the case mapping of the encoding handling in the mbstring extension, so test the specific scripts you care about.

Common mistakes

  1. Truthiness checks. Writing if (mb_stristr($text, $needle)) fails for results such as "0". Use !== false.
  2. Expecting a position. The return value is a substring. Use mb_stripos() for an index.
  3. Using stristr() on UTF-8 text. The non-multibyte function works on bytes and is not designed for non-ASCII case folding.
  4. Assuming the needle's casing is returned. The slice always comes from the haystack, so it keeps its original letters.
  5. Forgetting before_needle semantics. true excludes the needle, which is handy for the prefix but drops the marker itself.
  6. Relying on the default encoding. The internal encoding can differ between configurations, so pass 'UTF-8' when you mean UTF-8.
  • mb_strstr(): the case-sensitive multibyte version.
  • stristr(): case-insensitive search for byte strings.
  • strstr(): case-sensitive search for byte strings.
  • mb_stripos(): finds the character position of a case-insensitive match.

Recap

mb_stristr() returns the part of a string starting at, or just before, the first case-insensitive match of a needle. It returns false when nothing matches, so check it with === false. Pass an explicit encoding, and remember that the result is a slice of the haystack, not an index.

Tested examples

Split an email address at a case-insensitive marker
<?php
$email = 'Ana.Lopez@Example.COM';
echo json_encode([
    'from' => mb_stristr($email, '@example', false, 'UTF-8'),
    'before' => mb_stristr($email, '@EXAMPLE', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"from":"@Example.COM","before":"Ana.Lopez"}
Case-insensitive match with accented UTF-8 letters
<?php
$dessert = 'Crème Brûlée';
echo json_encode([
    mb_stristr($dessert, 'BRÛ', false, 'UTF-8'),
    mb_stristr($dessert, 'brû', true, 'UTF-8'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["Brûlée","Crème "]
Falsy results, misses and an empty needle
<?php
$found = mb_stristr('Item 0', '0', false, 'UTF-8');
$missing = mb_stristr('Item 0', 'z', false, 'UTF-8');
$empty = mb_stristr('Hello', '', false, 'UTF-8');
echo json_encode([
    'found' => $found,
    'foundIsFalse' => $found === false,
    'foundTruthy' => (bool) $found,
    'missing' => $missing,
    'empty' => $empty,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
{"found":"0","foundIsFalse":false,"foundTruthy":false,"missing":false,"empty":"Hello"}