Smart Substring Matching

ID

php.smart_substring_matching

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Efficiency

Language

Php

Tags

efficiency, suspicious-comparison

Description

Reports a loose comparison or bare boolean test on the result of strpos() / stripos(). The check is scoped strictly to these search functions so it does not overlap the generic loose-comparison rule.

Rationale

strpos() and related functions return the integer offset where the needle is found, or false when it is absent. When the needle is at the very start of the string the offset is 0, which is loosely equal to both false and 0. As a result if (strpos($h, $n) != false), if (strpos($h, $n) == false) and the bare if (strpos($h, $n)) all misclassify a match at position 0 as "not found". Only a strict comparison against false distinguishes "not found" from "found at offset 0".

<?php
if (strpos($haystack, $needle) != false) { }   // FLAW — match at offset 0 looks like "not found"
if (strpos($haystack, $needle)) { }             // FLAW — bare truthiness test, same bug
if (strpos($haystack, $needle) !== false) { }   // OK — strict comparison

Remediation

Compare the result with false using the strict operators !== or === (if (strpos($h, $n) !== false)). When only a yes/no answer is needed, str_contains() (PHP 8.0+) expresses the intent directly and avoids the pitfall.