str_starts_with

Checks whether a string begins with a given substring, returning true or false.

Signature
str_starts_with(string $haystack, string $needle): bool
  • Added in PHP 8.0

Parameters

ParameterTypeDescription
$haystackstringThe string to search in.
$needlestringThe 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

Try it yourself
Loading editor…
Press Run to execute the code.

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".

Example · php
<?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.

Example · php
<?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.

Example · php
<?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

  • Be the first to comment on this lesson.