stripos
Finds the position of the first case-insensitive occurrence of a substring in a string.
stripos(string $haystack, string $needle, int $offset = 0): int|false- Added in PHP 5.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$haystack | string | The string to search in. |
$needle | string | The substring to search for. |
$offsetoptional | int= 0 | Where in haystack to start searching. Negative values start that many characters from the end. |
Return value
int|falseThe zero-based position of the first case-insensitive match, or false if the needle is not found.
Errors
Throws a ValueError if offset is greater than the length of haystack.
Changelog
- PHP 8.2.0Case folding no longer depends on the locale; only ASCII case folding is performed.
- PHP 8.0.0needle may now be an empty string, which returns the offset.
- PHP 8.0.0Passing an int as needle is no longer supported; it was deprecated in 7.3.0.
- PHP 7.1.0Negative offset values are supported.
stripos is strpos with ASCII case folding: it returns the zero-based byte offset of the first match, ignoring letter case, or false when the needle is absent.
When to reach for it
Use it when you need to know where a case-insensitive match starts — to slice around it, to highlight it, or to walk every occurrence by feeding the previous position back in as $offset. If you only need a yes/no answer, str_contains(strtolower($h), strtolower($n)) or a stristr call reads better.
What trips people up
The famous trap: a match at the very start returns 0, which is falsy. if (stripos($h, $n)) silently misses matches at position 0. Always write !== false or === 0.
Since PHP 8.0 an $offset larger than the haystack throws a ValueError instead of warning and returning false, so guard offsets you compute from user input.
Folding is ASCII-only since PHP 8.2 and is no longer locale-sensitive, so É will not match é. The returned position is a byte offset, which does not line up with mb_substr() on multi-byte text — use mb_stripos() there.
Example
When to use it
- Find where a search term appears in a document so it can be wrapped in a highlight tag
- Detect a keyword in a subject line regardless of how the sender capitalised it
- Walk every case-insensitive occurrence of a term by advancing the offset in a loop
Frequently asked questions
What is the difference between stripos and strpos?
stripos ignores ASCII letter case while strpos is case-sensitive. Both return the zero-based byte position of the first match, or false when there is no match.
Why does if (stripos($a, $b)) not work?
Because a match at the start of the string returns 0, which PHP treats as false. Write stripos($a, $b) !== false instead.
How do I check case-insensitively whether a string contains a substring?
Use stripos($haystack, $needle) !== false, or str_contains(strtolower($haystack), strtolower($needle)) if you prefer a boolean-returning function.
More examples
Finding every occurrence
Prints 0: Cat, 5: cat and 21: CAT. Feeding the previous position plus the needle length back in as the offset walks the whole string; the !== false test is what makes the match at position 0 work.
<?php
$text = 'Cat, caterpillar and CATALOG';
$pos = 0;
while (($pos = stripos($text, 'cat', $pos)) !== false) {
echo $pos, ': ', substr($text, $pos, 3), "\n";
$pos += 3;
}Negative offsets
A negative offset starts the search that many bytes from the end, so -3 finds the final 'a' at index 6. strripos reaches the same result by searching backwards from the end.
<?php
$s = 'abcABCabc';
var_dump(stripos($s, 'a')); // int(0)
var_dump(stripos($s, 'a', 1)); // int(3)
var_dump(stripos($s, 'a', -3)); // int(6)
var_dump(strripos($s, 'a')); // int(6)Guarding an out-of-range offset
An offset past the end of the haystack throws a ValueError as of PHP 8.0 rather than returning false. Clamping the offset with min() keeps the call safe and returns bool(false) for the missing needle.
<?php
$s = 'short';
try {
stripos($s, 'x', 99);
} catch (ValueError $e) {
echo 'ValueError: ', $e->getMessage(), "\n";
}
var_dump(stripos($s, 'x', min(2, strlen($s))));
Discussion