strpos returns the position of the needle, and position zero is falsy — so the obvious check silently fails for exactly the case where the needle is at the start of the haystack.
// wrong for 'php' in 'php is fine'
if (strpos($haystack, $needle)) { /* ... */ }
// correct, and easy to get wrong under review pressure
if (strpos($haystack, $needle) !== false) { /* ... */ }
// so wrap it once, name it, and stop thinking about it
function contains(string $haystack, string $needle): bool
{
return strpos($haystack, $needle) !== false;
}
This has been the single most common PHP bug for twenty years, and the strict comparison is not memorable enough to be reliable under review pressure — it reads as noise and gets simplified by somebody tidying up. A named function costs three lines and removes the whole category, and it gives a static analyser a bool to work with rather than int|false. The same applies to strrpos, and to array_search, which has the identical trap with index zero.