mb_stripos

Finds the character position of the first case-insensitive match of a substring in a string.

Signature
mb_stripos(string $haystack, string $needle, int $offset = 0, ?string $encoding = null): int|false
  • Added in PHP 5.2.0

Parameters

ParameterTypeDescription
$haystackstringThe string to search in.
$needlestringThe string to search for. May be empty as of PHP 8.0.
$offsetoptionalint= 0Character position to start searching from. Negative counts back from the end of the haystack.
$encodingoptional?string= nullCharacter encoding. Null uses the internal encoding.

Return value

int|falseThe character position of the first match, counted from 0, or false if the needle is not found.

Errors

Throws a ValueError if offset lies outside the haystack, or if encoding is not a valid encoding.

Changelog

  • PHP 8.0.0needle now accepts an empty string.
  • PHP 8.0.0encoding is nullable now.
  • PHP 7.1.0Support for negative offsets was added.

mb_stripos() is mb_strpos() with case folding applied to both operands first. Two things make it different from plain stripos(): the returned position counts characters, not bytes, and the case folding is Unicode-aware, so É matches é, Ü matches ü and Greek or Cyrillic pairs fold correctly. stripos() only folds ASCII A–Z.

When to reach for it

Use it when you need the position of a case-insensitive match in text that may be non-ASCII — slicing a header off a message, finding where a keyword starts so you can highlight it, or splitting a line at the first occurrence of a marker. If you only need to know whether the needle is present, mb_stristr() !== false or a lowercased str_contains() reads better.

What trips people up

The classic bug is the falsy zero. A match at the very start returns 0, which if (!mb_stripos(...)) treats exactly like "not found". Always compare with !== false.

The other trap is mixing units: the position you get back is a character index, so feed it to mb_substr(), never to substr(). Doing the latter silently produces garbage the moment an accent appears earlier in the string.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Locate a case-insensitive keyword in a subject line so you can slice off the prefix
  • Highlight a search term in accented text by finding its offset and rebuilding the string
  • Detect and strip a case-varying protocol or label prefix from user input

Frequently asked questions

What is the difference between mb_stripos and stripos?

mb_stripos returns a character offset and folds case using Unicode rules, so É matches é. stripos returns a byte offset and only folds ASCII letters, which makes it wrong for accented or non-Latin text.

Why does if (mb_stripos($h, $n)) fail when the match is at the start?

Because a match at position 0 returns int(0), which PHP treats as false in a boolean test. Compare the result with !== false instead.

Can I pass the result of mb_stripos to substr?

No. mb_stripos returns a character index, so pair it with mb_substr. Mixing it with the byte-based substr produces corrupt output as soon as the string contains a multibyte character.

More examples

Slicing with the returned offset

mb_stripos returns a character index. Feeding it to mb_substr gives 'Grüße aus München'; feeding the same number to substr starts one byte too early because 'ü' earlier in the line... in fact the byte string is longer, so substr starts inside the wrong region and prints a mangled result.

Example · php
<?php
mb_internal_encoding('UTF-8');
$line = 'Betreff: Grüße aus München';
$pos  = mb_stripos($line, 'grüße');

if ($pos !== false) {
    echo mb_substr($line, $pos), "\n";   // correct
    echo substr($line, $pos), "\n";      // wrong: mixes chars and bytes
}

Searching from an offset

The first call finds the capital R at position 5. Starting at 6 skips it and finds the lowercase r at 9. A negative offset of -5 starts the search five characters from the end, which still catches that same r at 9.

Example · php
<?php
mb_internal_encoding('UTF-8');
$h = 'Café Ristretto';
var_dump(mb_stripos($h, 'r'));      // first r
var_dump(mb_stripos($h, 'r', 6));   // skip past it
var_dump(mb_stripos($h, 'r', -5));  // last 5 characters only

Discussion

  • Be the first to comment on this lesson.