Strings7 min readExamples run on PHP 8.5.10

stristr()

Learn how PHP 8.4 stristr() finds a substring case-insensitively and returns the rest of the string, or the part before the match, with examples and pitfalls.

Signature

stristr(string $haystack, string $needle, bool $before_needle = false): string|false

Purpose

stristr() searches a string for the first occurrence of another string, ignoring letter case, and returns a slice of the original. By default the slice starts at the match and runs to the end of the haystack. With the optional third argument it returns everything before the match instead. It is the case-insensitive counterpart of strstr().

The returned text keeps the casing of the original haystack, not the casing of the needle you typed. That makes it handy for splitting values such as email addresses, labels or log lines when you do not control capitalization.

Signature

Parameters and defaults

  • $haystack: the string to search in.
  • $needle: the string to look for. Both haystack and needle are compared case-insensitively. Since PHP 8.0.0 an empty needle is accepted, and passing an integer as the needle is no longer supported, so cast numbers to strings yourself.
  • $before_needle: defaults to false. When true, the function returns the part of the haystack before the first match, and the needle itself is excluded.

Return value and failure cases

The function returns a string when a match is found and false when it is not. It does not raise a warning for a missing needle, so you must handle false yourself. The function is binary-safe.

Since PHP 8.2.0, case folding no longer depends on the locale set with setlocale(). Only ASCII letters are folded; non-ASCII bytes are compared by their byte value. So a matches A, but a multibyte letter such as é will not match É.

Example 1: splitting at the first match

The first call returns the tail starting at the first e or E. The second returns what comes before it.

Example
<?php
$email = 'USER@EXAMPLE.com';
echo json_encode([
    stristr($email, 'e'),
    stristr($email, 'e', true),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["ER@EXAMPLE.com","US"]

Example 2: handling a missing needle

When nothing matches, you get false. Compare with === rather than relying on truthiness.

Example
<?php
$text = 'Hello World!';
$result = stristr($text, 'earth');
var_dump($result);
echo json_encode(stristr($text, 'WORLD')), PHP_EOL;
Output
bool(false)
"World!"

Notice that the second call searched for WORLD but returned World!, the original casing.

Example 3: prefixes, tails and the empty needle

Example
<?php
$subject = 'Order-0042: Paid';
$status = stristr($subject, ': paid');
$prefix = stristr($subject, ': PAID', true);
$empty = stristr($subject, '');
echo json_encode(['status' => $status, 'prefix' => $prefix, 'empty' => $empty]), PHP_EOL;
Output
{"status":": Paid","prefix":"Order-0042","empty":"Order-0042: Paid"}

An empty needle matches at the very start, so the whole haystack comes back (or an empty string when $before_needle is true).

Edge cases

  • Match at the start with $before_needle = true: the result is an empty string, which is a valid match but is falsy.
  • Falsy results: a match can return "0" or "". Both are falsy in a boolean context, so if (stristr(...)) can wrongly report no match. Use !== false.
  • Only the first occurrence counts. Later matches are ignored. If you need the last occurrence, look at strrchr(), which is case-sensitive.
  • Non-ASCII text: because only ASCII folding is performed, use multibyte-aware functions when you need case-insensitive matching of accented or non-Latin letters.

Common mistakes

  1. Using truthiness to test for a match. Always write stristr($h, $n) !== false.
  2. Using stristr() only to test presence. If you need just a position, stripos() is more direct. If you only need a yes-or-no answer, str_contains() is case-sensitive, so combine it with lowercasing only for ASCII data, or use stripos() !== false.
  3. Expecting the needle's casing in the result. The slice always comes from the haystack.
  4. Passing an integer needle. This no longer works as a character code in PHP 8. Convert to a string explicitly.
  5. Assuming locale-aware folding. From PHP 8.2.0 the locale is ignored.
  6. Forgetting that the needle is included by default. The returned tail begins with the matched text; only $before_needle = true removes it, and then you get the head instead, not the tail.
  • strstr(): the case-sensitive version.
  • stripos(): returns the position of a case-insensitive match instead of a substring.
  • strrchr(): finds the last occurrence of a character.
  • strpbrk(): searches for any character from a set.
  • preg_match(): pattern-based matching when plain substring search is not enough.

Recap

stristr() finds the first case-insensitive occurrence of a needle and returns either the tail from that point or, with $before_needle, the head before it. It returns false when there is no match, keeps the haystack's original casing, folds only ASCII letters in PHP 8.2 and later, and should always be checked with a strict comparison.

Tested examples

Splitting an email at the first case-insensitive match
<?php
$email = 'USER@EXAMPLE.com';
echo json_encode([
    stristr($email, 'e'),
    stristr($email, 'e', true),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
Output
["ER@EXAMPLE.com","US"]
Handling a missing needle and preserved casing
<?php
$text = 'Hello World!';
$result = stristr($text, 'earth');
var_dump($result);
echo json_encode(stristr($text, 'WORLD')), PHP_EOL;
Output
bool(false)
"World!"
Prefixes, tails and the empty needle
<?php
$subject = 'Order-0042: Paid';
$status = stristr($subject, ': paid');
$prefix = stristr($subject, ': PAID', true);
$empty = stristr($subject, '');
echo json_encode(['status' => $status, 'prefix' => $prefix, 'empty' => $empty]), PHP_EOL;
Output
{"status":": Paid","prefix":"Order-0042","empty":"Order-0042: Paid"}