DH
4 min read

`strpos` in PHP: Stop Getting Bitten by the Zero-Position Bug

Why `strpos` returns false AND 0, and how to check correctly. A production-hardened guide to the classic PHP footgun.

phpsecurity

strpos in PHP: Stop Getting Bitten by the Zero-Position Bug

strpos in PHP is deceptively simple on the surface, quietly destructive when misused. It's a classic footgun, and I still see the mistake in code reviews. Let's sort it out properly.


What strpos Does

strpos finds the position of the first occurrence of a substring within a string. The signature is straightforward:

strpos(string $haystack, string $needle, int $offset = 0): int|false

It returns the integer position of the first match, starting from zero. If no match is found, it returns false.

Simple example:

$text = 'Hello, world';
$pos = strpos($text, 'world');
var_dump($pos); // int(7)

The Bug

The natural instinct is to write:

if (strpos($text, 'world')) {
echo 'Found it';
}

This fails when the substring appears at position zero — the very start of the string. In that case, strpos returns 0, which is falsy in PHP:

$text = 'world domination';
if (strpos($text, 'world')) {
echo 'Found it'; // Never executes — strpos returns 0
}

The substring was found. strpos returned 0. But your condition was false.


The Fix: Strict Comparison with !== false

The correct pattern is to compare strictly against false:

$text = 'world domination';
if (strpos($text, 'world') !== false) {
echo 'Found it'; // Correctly executes
}

Use !== false (strict, type-safe) rather than != false (loose). With strict comparison, 0 (integer) and false (boolean) are distinct — which is what you need.


Practical Example: Detecting Hashtags

A common real-world use:

function containsHashtag(string $text): bool
{
return strpos($text, '#') !== false;
}

$post = '#php is still relevant in 2024';
if (containsHashtag($post)) {
echo 'Post contains a hashtag';
}

Wrapping the check in a typed function keeps calling code clean and removes the !== false noise from every call site.


PHP 8+: str_contains for Simple Checks

If you're on PHP 8 or above, use str_contains for simple existence checks:

if (str_contains($text, '#')) {
echo 'Contains a hashtag';
}

str_contains returns a plain boolean, so there's no zero-false ambiguity. Use it when you only need to know whether the substring exists.

Use strpos when you need the position:

$pos = strpos($text, '#');
if ($pos !== false) {
$tag = substr($text, $pos);
echo "Hashtag starts at position {$pos}: {$tag}";
}

Watch Out in switch Statements

The same zero-false confusion appears in switch blocks, which use loose equality by default:

switch (strpos($text, 'world')) {
case false:
echo 'Not found';
break;
case 0:
echo 'Found at start';
break;
}

With loose comparison, 0 == false is true, so the false case matches both "not found" and "found at position zero". Restructure the logic or use match (PHP 8+), which uses strict comparison:

$result = match(true) {
strpos($text, 'world') === false => 'Not found',
strpos($text, 'world') === 0 => 'Found at start',
default => 'Found elsewhere',
};

Quick Reference

ScenarioUse
Need to know if substring exists (PHP 8+)str_contains()
Need the position of the substringstrpos() !== false
Case-insensitive searchstripos() !== false
Last occurrencestrrpos() !== false
Multibyte strings (UTF-8)mb_strpos() !== false

Summary

strpos is reliable and fast, but its return type requires strict handling. Always use !== false when checking the result in a conditional. On PHP 8+, reach for str_contains when position doesn't matter. Stay alert to loose comparison traps in switch blocks — the same zero-equals-false confusion applies there too.

Damian Hodgkiss

Damian Hodgkiss

Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.

Creating Freedom

Join me on the journey from engineer to solopreneur. Learn how to build profitable SaaS products while keeping your technical edge.

    Proven strategies

    Learn the counterintuitive ways to find and validate SaaS ideas

    Technical insights

    From choosing tech stacks to building your MVP efficiently

    Founder mindset

    Transform from engineer to entrepreneur with practical steps