← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

str_contains / str_starts_with / str_ends_with

php PHP 8.0+ Beginner
debt(d3/e1/b1/t3)
d3 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'default linter catches the common case' (d3), Rector and phpcs (listed in detection_hints.tools) automatically flag strpos() !== false patterns and can rewrite them.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch or single-call swap' (e1), quick_fix says replace strpos($h,$n) !== false with str_contains($h,$n) — a direct call swap, Rector can automate it.

b1 Burden Structural debt — long-term weight of choosing wrong

Closest to 'minimal commitment' (b1), this is a localised readability improvement at each call site with no architectural reach despite applying across web/cli/queue contexts.

t3 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'minor surprise' (t3), misconception notes case-sensitivity assumption — a single documented edge case devs may guess wrong about, but the function otherwise behaves as named.

About DEBT scoring →

TL;DR

PHP 8.0 added str_contains(), str_starts_with(), and str_ends_with() — replace strpos() !== false patterns with these readable alternatives.

Explanation

Before PHP 8.0: if (strpos($str, $needle) !== false) was the idiom — prone to off-by-one with === 0 vs !== false. PHP 8.0 added: str_contains(string $haystack, string $needle): bool, str_starts_with(), str_ends_with(). All return bool, handle empty strings predictably (str_contains('foo', '') === true), and are case-sensitive. For case-insensitive search still use stripos() !== false or strtolower() + str_contains(). These functions are simple, readable, and Rector can auto-migrate from strpos patterns.

Common Misconception

str_contains() is case-insensitive by default — it's case-sensitive. Use strtolower() on both sides for case-insensitive matching.

Why It Matters

str_contains() is more readable than strpos() !== false and removes a common off-by-one bug where developers write strpos() === false instead of !== false.

Common Mistakes

  • Using strpos() === false instead of !== false — logic inversion bug.
  • Not knowing str_contains()/str_starts_with() are in PHP 8.0+ only — use Rector to polyfill for 7.x.
  • Assuming case-insensitive behaviour.

Code Examples

✗ Vulnerable
// Fragile: easy to confuse === and !==
if (strpos($path, '/admin') === 0) { /* starts with */ }
if (strpos($email, '@') !== false) { /* contains */ }
✓ Fixed
if (str_starts_with($path, '/admin')) { /* clear intent */ }
if (str_contains($email, '@')) { /* readable */ }
if (str_ends_with($file, '.php')) { /* obvious */ }

// Case-insensitive:
if (str_contains(strtolower($input), 'error')) { }

Added 22 Mar 2026
Views 23
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 1 ping M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Amazonbot 9 Unknown AI 3 Perplexity 3 ChatGPT 2 Google 1 Ahrefs 1
crawler 18 pre-tracking 1
DEV INTEL Tools & Severity
🔵 Info ⚙ Fix effort: Low
⚡ Quick Fix
Replace strpos($h,$n) !== false with str_contains($h,$n). Replace strpos($h,$n) === 0 with str_starts_with($h,$n). Use Rector to automate.
📦 Applies To
PHP 8.0+ web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
strpos.*!== false|strpos.*=== 0
Auto-detectable: ✓ Yes rector phpcs
🤖 AI Agent
Confidence: High False Positives: Low ✓ Auto-fixable Fix: Low Context: Line

✓ schema.org compliant