str_starts_with
Checks whether a string begins with a given substring, returning true or false.
str_starts_with(string $haystack, string $needle): bool- Added in PHP 8.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$haystack | string | The string to search in. |
$needle | string | The substring to look for at the start of haystack. |
Return value
booltrue if haystack begins with needle, false otherwise.
str_starts_with answers one question and returns a clean boolean: does $haystack begin with $needle? It replaces the old, error-prone idiom strpos($h, $n) === 0 and the slightly wasteful substr($h, 0, strlen($n)) === $n.
When to reach for it
Routing and dispatch (str_starts_with($path, '/api/')), protocol checks ('https://'), header parsing ('Bearer '), filtering configuration keys by prefix, and skipping comment lines ('#') when reading a text file. Anywhere you previously wrote a strpos comparison with 0, this is clearer and cannot be miswritten.
What trips people up
It is case-sensitive. str_starts_with('HTTPS://x', 'https://') is false. There is no case-insensitive variant, so lowercase both sides first with strtolower() when the input is user-supplied.
An empty needle always returns true, including against an empty haystack — a legitimate mathematical result that surprises people validating user input, so reject blank prefixes before calling it.
It is byte-based and binary-safe: it does not normalise Unicode, so a decomposed e plus combining accent will not match a precomposed é prefix. Normalise first if that matters. It also requires PHP 8.0 or newer; on older versions fall back to strncmp($h, $n, strlen($n)) === 0.
Example
When to use it
- Route a request by checking whether the path begins with /api/ or /admin/
- Detect and strip a "Bearer " prefix from an Authorization header
- Skip comment lines starting with # while reading a configuration file
Frequently asked questions
What is the difference between str_starts_with and strpos?
str_starts_with returns a boolean and only looks at position 0, while strpos searches the whole string and returns an integer position or false. str_starts_with($h, $n) is equivalent to strpos($h, $n) === 0 but clearer and faster.
Is str_starts_with case-insensitive?
No, it is case-sensitive. Lowercase both strings with strtolower() first, or use stripos($haystack, $needle) === 0 for a case-insensitive prefix check.
What PHP version added str_starts_with?
PHP 8.0. On PHP 7 and earlier use strncmp($haystack, $needle, strlen($needle)) === 0 as a drop-in replacement.
More examples
Parsing an Authorization header
The check confirms the scheme, then substr removes exactly the prefix length. This prints the raw token; swapping in a "Basic " header would print "unsupported scheme".
<?php
$header = 'Bearer eyJhbGciOi.payload.sig';
if (str_starts_with($header, 'Bearer ')) {
$token = substr($header, strlen('Bearer '));
echo "token: $token\n";
} else {
echo "unsupported scheme\n";
}Filtering keys by prefix
Only db_host and db_port survive; app_env is dropped. ARRAY_FILTER_USE_KEY passes the key rather than the value to the callback.
<?php
$config = ['db_host' => 'local', 'db_port' => 5432, 'app_env' => 'dev'];
$db = array_filter(
$config,
static fn(string $k): bool => str_starts_with($k, 'db_'),
ARRAY_FILTER_USE_KEY
);
print_r($db);Case-insensitive prefix matching
The first check fails on the uppercase scheme. Lowercasing first, or using stripos compared strictly against 0, both return true — str_starts_with itself has no case-insensitive mode.
<?php
$url = 'HTTPS://Example.com/page';
var_dump(str_starts_with($url, 'https://'));
var_dump(str_starts_with(strtolower($url), 'https://'));
var_dump(stripos($url, 'https://') === 0);
Discussion