strpos

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

Signature
strpos(string $haystack, string $needle, int $offset = 0): int|false
  • Added in PHP 4.0

Parameters

ParameterTypeDescription
$haystackstringThe string to search in.
$needlestringThe substring to search for.
$offsetoptionalint= 0If specified, search will start this number of characters counted from the beginning of the string. If negative, the search starts that many characters from the end, but the returned position is still relative to the beginning.

Return value

int|falseThe position of where the needle exists relative to the beginning of the haystack string (independent of offset), or false if the needle was not found.

Errors

Throws a ValueError if offset is not contained in haystack.

Changelog

  • PHP 8.0.0needle now accepts an empty string.
  • PHP 8.0.0Passing an int as needle is no longer supported.
  • PHP 7.1.0Support for negative offsets has been added.

strpos() returns the zero-based byte offset of the first occurrence of needle inside haystack, or false when it is not there. It is case sensitive and binary safe.

When to reach for it

Use it when you need the position — to slice with substr(), to know how far into a line a delimiter sits, or to walk through repeated matches by feeding the previous result back as offset.

If you only want a yes/no answer, use str_contains() (PHP 8.0+). It reads better and sidesteps the classic bug below. For prefix and suffix tests use str_starts_with() and str_ends_with().

What trips people up

A match at the very beginning returns 0, which is loosely equal to false. Writing if (strpos($s, $n) == false) or if (!strpos($s, $n)) reports "not found" for a string that starts with the needle. Always compare with !== false.

Positions are in bytes. In UTF-8 text the returned offset is a byte offset, which is exactly what substr() wants but not what a user counting characters would say; use mb_strpos() for character positions.

Since PHP 7.1 a negative offset starts the search that many bytes from the end, but the returned position is still measured from the beginning. An offset outside the string throws a ValueError.

Example

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

When to use it

  • Split a line into key and value at the first colon or equals sign.
  • Check whether a log line contains an error marker before parsing it further.
  • Walk every occurrence of a token by passing the previous position + 1 as the offset.

Frequently asked questions

Why does strpos return 0 instead of false?

0 means the needle was found at the very first byte. Because 0 == false in PHP, you must compare the result with !== false rather than using == or a plain if.

What is the difference between strpos and str_contains?

str_contains returns a plain bool and is the right choice when you only need to know whether the substring is present. strpos returns the position, which you need for slicing or looping.

Is strpos case sensitive?

Yes. Use stripos() for a case-insensitive search, or mb_stripos() when the text is multibyte and you need character positions.

More examples

The === false trap

Both messages print. The loose comparison wrongly reports the needle as missing because it matched at position 0, while the strict comparison and str_contains() both get it right. This is the single most common strpos bug.

Example · php
<?php
$email = '[email protected]';

if (strpos($email, 'user') == false) {      // wrong: 0 == false
    echo "loose check says: NOT found\n";
}

if (strpos($email, 'user') !== false) {     // right
    echo "strict check says: found\n";
}

var_dump(str_contains($email, 'user'));     // bool(true)

Looping over every match

The loop prints dashes at 1, 3 and 5; incrementing $pos each round is what stops it finding the same match forever. The final call uses a negative offset to start the search three bytes from the end, and still reports the position measured from the beginning.

Example · php
<?php
$s = 'a-b-c-d';
$pos = 0;

while (($pos = strpos($s, '-', $pos)) !== false) {
    echo 'dash at ', $pos, PHP_EOL;
    $pos++;
}

var_dump(strpos($s, '-', -3)); // int(5) — search the last 3 bytes only

Discussion

  • Be the first to comment on this lesson.