Signature
stripos(string $haystack, string $needle, int $offset = 0): int|falsePurpose
stripos() answers the question "where does this text first appear, ignoring upper and lower case?" It scans a string called the haystack for another string called the needle and returns the zero-based position of the first match. It is the case-insensitive sibling of strpos(). Typical uses include checking whether a message mentions a keyword, locating a marker such as ERROR in a log line, or finding where to cut a string.
Signature
Parameters and defaults
$haystack: the string to search in.$needle: the string to look for. Since PHP 8.0 an empty string is accepted, and an integer is no longer treated as a character code. If you hold a number, cast it to a string yourself.$offset: where the search begins. The default is0, the start of the string. A positive value skips that many characters from the start. A negative value starts that many characters back from the end of the haystack.
Return value and failure cases
On success you get an int: the position of the match counted from the beginning of the haystack. The position is not relative to $offset, so searching from offset 4 and finding a match at index 6 returns 6. Positions start at 0, not 1.
If the needle does not occur, the function returns false. If $offset is larger than the length of the haystack, PHP throws a ValueError.
Because a match at the very start is 0 and 0 is falsy, you must test the result with === or !==. A plain if (stripos(...)) treats a match at the start as a failure.
Example 1: basic search
The search ignores case in both the haystack and the needle. A missing needle produces false, which json_encode() prints as false.
$text = 'PHP Makes Web Pages';
$result = [
'lower' => stripos($text, 'makes'),
'upper' => stripos($text, 'WEB'),
'missing' => stripos($text, 'python'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;{"lower":4,"upper":10,"missing":false}
Example 2: testing the result safely
A helper that checks whether a string begins with a word shows why strict comparison matters. The position 0 means "found at the start", and only === 0 expresses that exactly.
function startsWithWord(string $text, string $word): bool
{
return stripos($text, $word) === 0;
}
$checks = [
startsWithWord('Hello world', 'hello'),
startsWithWord('Say hello', 'hello'),
startsWithWord('Nothing', 'hello'),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;[true,false,false]
For a simple prefix test, str_starts_with() is case-sensitive, so stripos($text, $word) === 0 is useful when case should be ignored. Be aware that it still scans the whole string when there is no match at the start.
Example 3: offsets, empty needles and errors
The string abcABCabc contains three copies of the letters, at positions 0, 3 and 6. The example shows positive offsets, negative offsets, an empty needle and an out-of-range offset.
$text = 'abcABCabc';
$result = [
'from_1' => stripos($text, 'abc', 1),
'from_4' => stripos($text, 'ABC', 4),
'negative_3' => stripos($text, 'abc', -3),
'negative_2' => stripos($text, 'abc', -2),
'empty_needle' => stripos($text, '', 3),
];
try {
stripos($text, 'a', 20);
} catch (ValueError $e) {
$result['error'] = get_class($e);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;{"from_1":3,"from_4":6,"negative_3":6,"negative_2":false,"empty_needle":3,"error":"ValueError"}
With an offset of -3 the search starts at index 6, so the last copy matches. With -2 it starts at index 7, where only bc remains, so there is no match. An empty needle matches immediately at the offset, so it returns the offset itself.
Edge cases
- ASCII-only case folding. As of PHP 8.2 the comparison no longer depends on the locale set with
setlocale(). Only ASCII letters are folded; other bytes are compared by byte value. For accented or other multibyte text, usemb_stripos(). - Byte positions. The returned index counts bytes, not visible characters. In UTF-8 text with multibyte characters before the match, the number may be larger than the character count.
- Binary safety. The function is binary-safe, so strings containing null bytes are searched correctly.
- Offset equal to length. An offset equal to the haystack length is allowed. Only a larger offset throws.
Common mistakes
- Writing
if (stripos($s, $n))or!= false. A match at position 0 is then mistaken for no match. Use!== false. - Assuming the returned position is relative to
$offset. It is always relative to the start of the haystack. - Passing an integer as the needle and expecting a character code. This has not worked since PHP 8.0; use
chr()or a string. - Using
stripos()when you only need a yes/no answer.str_contains()is case-sensitive, so for a case-insensitive boolean checkstripos($h, $n) !== falseis still the right idiom. - Using it for non-ASCII case-insensitive searches and expecting
Éto matché.
Related functions
strpos(): the case-sensitive version.strripos()andstrrpos(): find the last occurrence.mb_stripos(): multibyte-aware case-insensitive search.stristr(): returns the part of the string from the match onward.str_contains(),str_starts_with(),str_ends_with(): case-sensitive boolean checks.substr()andstr_ireplace(): commonly combined with a found position.
Recap
stripos() returns the zero-based byte position of the first case-insensitive match, or false when nothing matches. Always compare with === or !==, remember that the position is measured from the start of the haystack, and expect a ValueError for an offset beyond the string length. Use the mb_ variant for multibyte text.
Tested examples
$text = 'PHP Makes Web Pages';
$result = [
'lower' => stripos($text, 'makes'),
'upper' => stripos($text, 'WEB'),
'missing' => stripos($text, 'python'),
];
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;{"lower":4,"upper":10,"missing":false}
function startsWithWord(string $text, string $word): bool
{
return stripos($text, $word) === 0;
}
$checks = [
startsWithWord('Hello world', 'hello'),
startsWithWord('Say hello', 'hello'),
startsWithWord('Nothing', 'hello'),
];
echo json_encode($checks, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;[true,false,false]
$text = 'abcABCabc';
$result = [
'from_1' => stripos($text, 'abc', 1),
'from_4' => stripos($text, 'ABC', 4),
'negative_3' => stripos($text, 'abc', -3),
'negative_2' => stripos($text, 'abc', -2),
'empty_needle' => stripos($text, '', 3),
];
try {
stripos($text, 'a', 20);
} catch (ValueError $e) {
$result['error'] = get_class($e);
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;{"from_1":3,"from_4":6,"negative_3":6,"negative_2":false,"empty_needle":3,"error":"ValueError"}