mb_strpos

Finds the character position of the first occurrence of a substring in a multibyte string.

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

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 occurrence, counted from 0, or false if the needle is not found.

Errors

Throws a ValueError if offset is greater than the length of 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_strpos() is the character-counting version of strpos(). Both find the first occurrence of a needle; the difference is the unit of the answer. In 'Bäckerei Straße' the word Straße starts at character 9 but at byte 10, because ä costs two bytes. Whichever number you get, you must pair it with a matching slicer: character offsets go to mb_substr(), byte offsets go to substr().

When to reach for it

Use it when you need the location of something so you can cut, measure or highlight around it: splitting a line at the first colon, finding where a token begins to wrap it in markup, or checking that a prefix sits at position 0. If you only want a yes/no answer, str_contains() is clearer and does not have the falsy-zero problem.

What trips people up

Two things. First, the classic: a match at the start returns 0, so if (mb_strpos(...)) reports "not found" for the most obvious match there is. Use !== false.

Second, an offset past the end of the haystack throws a ValueError rather than returning false, which turns a sloppy loop over successive matches into a fatal error.

Example

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

When to use it

  • Find where a delimiter sits so you can split a line into a key and a value
  • Locate a search term in accented text to wrap it in highlight markup
  • Walk through every occurrence of a token by feeding the previous position back as the offset

Frequently asked questions

What is the difference between strpos and mb_strpos?

strpos returns a byte offset and mb_strpos returns a character offset. They agree only for ASCII, and mixing a character offset into substr() or a byte offset into mb_substr() corrupts the result.

Why does mb_strpos return 0 and my if statement treat it as not found?

0 is a valid position — it means the needle is at the very start — but PHP evaluates it as falsy. Compare the return value with !== false rather than testing it directly.

Is mb_strpos case-sensitive?

Yes. Use mb_stripos for a case-insensitive search, which also folds accented and non-Latin case pairs correctly.

More examples

Splitting a line at the first delimiter

mb_strpos gives a character index, so both mb_substr calls line up correctly even though 'ß' and 'ö' are two bytes each. Swapping in substr() here would cut in the wrong place and split a character.

Example · php
<?php
mb_internal_encoding('UTF-8');
$line = 'Straße: Königsallee 42';
$pos  = mb_strpos($line, ':');

if ($pos !== false) {
    echo 'key:   ', mb_substr($line, 0, $pos), "\n";
    echo 'value: ', ltrim(mb_substr($line, $pos + 1)), "\n";
}

Finding every occurrence

Advancing by mb_strlen('año') rather than by the byte length keeps the offsets in character units, producing 0, 4 and 8. Note the loop increments before searching again — passing an offset past the end of the string would throw a ValueError.

Example · php
<?php
mb_internal_encoding('UTF-8');
$s = 'año año año';
$pos = 0;
$found = [];
while (($pos = mb_strpos($s, 'año', $pos)) !== false) {
    $found[] = $pos;
    $pos += 3;
}
print_r($found);

Discussion

  • Be the first to comment on this lesson.