/commands/php/str-contains-check-substring
php check if a string contains a substring (str_contains, strpos)
- tool
- PHP
- versions
- str_contains: PHP 8.0+, strpos: PHP 4+
- os
- any
On PHP 8.0 and later, str_contains($haystack, $needle) returns true if $needle occurs anywhere in $haystack; it is case-sensitive. On PHP 7 and older use strpos($haystack, $needle) !== false — with !==, because strpos returns 0 when the match is at the very start, and 0 is falsy. For a case-insensitive test, use stripos(...) !== false.
Examples
The example runs PHP 8.4 compiled to WebAssembly (php-wasm) through a small php wrapper around its CLI:
npm init -y >/dev/null
npm install --silent --no-audit --no-fund @php-wasm/node@3.1.56 @php-wasm/universal@3.1.56 >/dev/null 2>&1
cat > php.mjs <<'EOF'
import fs from "node:fs";
import { PHP } from "@php-wasm/universal";
import { loadNodeRuntime } from "@php-wasm/node";
const php = new PHP(await loadNodeRuntime("8.4", { emscriptenOptions: { processId: 1 } }));
php.mkdir("/app");
for (const f of fs.readdirSync(".").filter((f) => f.endsWith(".php"))) php.writeFile(`/app/${f}`, fs.readFileSync(f));
php.chdir("/app");
const r = await php.cli(["php", ...process.argv.slice(2)]);
process.stdout.write(await r.stdoutText);
process.stdout.write(await r.stderrText);
process.exit(await r.exitCode);
EOF
cat > contains.php <<'EOF'
<?php
$s = "Error: file not found";
$show = fn ($v) => var_export($v, true);
echo "str_contains 'file': ", $show(str_contains($s, "file")), "\n";
echo "str_contains 'error': ", $show(str_contains($s, "error")), "\n";
echo "stripos 'error': ", $show(stripos($s, "error") !== false), "\n";
echo "strpos 'Error': ", $show(strpos($s, "Error")), "\n";
echo "if (strpos(...)) bug: ", (strpos($s, "Error") ? "found" : "not found"), "\n";
echo "strpos !== false: ", $show(strpos($s, "Error") !== false), "\n";
echo "empty needle: ", $show(str_contains($s, "")), "\n";
echo "starts / ends with: ", $show(str_starts_with($s, "Error")), " / ", $show(str_ends_with($s, "found")), "\n";
echo "mb_stripos 'ÄRGER': ", $show(mb_stripos("Kein Ärger", "ÄRGER") !== false), "\n";
EOF
node php.mjs contains.phpOutput
str_contains 'file': true str_contains 'error': false stripos 'error': true strpos 'Error': 0 if (strpos(...)) bug: not found strpos !== false: true empty needle: true starts / ends with: true / true mb_stripos 'ÄRGER': true
The line marked "bug" is the classic mistake: strpos finds Error at position 0, the if treats 0 as false and reports "not found".
Which function to use
| Code | Case | Returns |
|---|---|---|
| str_contains($s, $n) | Sensitive | bool (PHP 8.0+) |
| strpos($s, $n) !== false | Sensitive | bool, works on every PHP version |
| stripos($s, $n) !== false | Insensitive (ASCII letters) | bool |
| mb_stripos($s, $n) !== false | Insensitive, multibyte aware | bool (needs the mbstring extension) |
| str_starts_with($s, $n) / str_ends_with($s, $n) | Sensitive | bool (PHP 8.0+) |
| preg_match("/err(or)?/i", $s) === 1 | As the pattern says | Use for patterns, not for plain substrings |
Pitfalls
== falseor a plainif.strposreturns0for a match at the start; always compare with!== false.- PHP 7. Calling
str_containson PHP 7.4 or older fails with Call to undefined function str_contains(). Upgrade, usestrpos, or add the polyfill packagesymfony/polyfill-php80. - Multibyte text.
striposfolds only ASCII letters, soÄandädo not match; usemb_stripos.str_containsitself is byte-based and works correctly on UTF-8 for case-sensitive checks. - Argument order. The haystack comes first, the needle second, for all of these functions.
Confirm it worked
var_dump(str_contains(...)) prints bool(true) or bool(false); test a needle at position 0, as above, to make sure the check does not fall into the strpos trap.
FAQ
Is str_contains case-sensitive?
Yes. str_contains("Hello", "hello") is false. For a case-insensitive check use stripos($haystack, $needle) !== false, or mb_stripos for multibyte text.
Why does str_contains return true for an empty needle?
Every string contains the empty string, so str_contains($s, "") is always true, as the manual documents. Check that the needle is not empty first if it comes from user input.
How do I check the start or end of a string?
PHP 8.0 also added str_starts_with() and str_ends_with(), which return a bool like str_contains.