Str::startsWith
Determines whether a string begins with a given value or any value in a list.
Str::startsWith(string $haystack, string|iterable<string> $needles): bool- Added in Laravel 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$$haystack | string | The string being examined. A null haystack returns false rather than raising an error. |
$$needles | string|iterable<string> | A single prefix, or any iterable of prefixes of which one match is enough. Empty needles are skipped, and an empty list returns false. |
Return value
booltrue when the haystack begins with any non-empty needle, false otherwise.
Changelog
- PHP 8.xThe needle list accepts any iterable, not just an array, so a Collection or generator can be passed directly.
- PHP 9.xThe implementation delegates to PHP's native str_starts_with, which does not change results but does make the check faster.
Str::startsWith answers a boolean question: does this string begin with that value? The reason to use it over PHP's native str_starts_with is the second form — pass an array or any iterable and it returns true if any of them matches, which saves writing a loop or an || chain.
Typical uses are prefix routing decisions, recognising a header scheme such as Bearer , spotting an absolute URL, or classifying a key by its namespace.
What trips people up
- Empty needles never match.
Str::startsWith('anything', '')isfalse, and so is passing an empty array. Nativestr_starts_withreturnstruefor an empty needle, so swapping one for the other changes behaviour. - It is case sensitive with no flag to change that. Lowercase both sides yourself, or use
Str::iswith a pattern. - The comparison is byte-based. That is fine for UTF-8 because a valid UTF-8 prefix is also a byte prefix, but it means a needle in a different encoding silently fails.
- It is a prefix test, not a glob.
admin/*is matched literally; useStr::isfor wildcards.
Example
When to use it
- Decide whether a request path belongs to an admin or API section before applying middleware logic.
- Detect the Bearer scheme on an Authorization header before splitting off the token.
- Check whether a stored path is already absolute (http:// or https://) before prefixing a base URL.
Frequently asked questions
How is Str::startsWith different from str_starts_with?
Str::startsWith accepts an array or any iterable of prefixes and returns true if any one matches, and it returns false for an empty needle where the native function returns true. For a single non-empty prefix the two behave the same.
Is Str::startsWith case insensitive?
No, and there is no flag for it. Lowercase both operands with Str::lower first, or use Str::is with a wildcard pattern when you need looser matching.
Why does Str::startsWith return false for an empty string needle?
Empty needles are deliberately skipped so that an unset or empty variable does not match every haystack. If a needle comes from user input, validate it before relying on the result.
More examples
Classifying a path and an auth header
The first dump is true because 'admin/' matched, showing the array form replacing an || chain. The token 'abc123' is printed by slicing past the seven-character scheme. The third dump is false, the reminder that HTTP header values are not normalised for you. The last shows Str::of()->startsWith() returning a plain bool, not a fluent object.
<?php
use Illuminate\Support\Str;
$path = 'admin/users/42';
var_dump(Str::startsWith($path, ['admin/', 'api/']));
$header = 'Bearer abc123';
if (Str::startsWith($header, 'Bearer ')) {
echo Str::substr($header, 7), PHP_EOL;
}
// Case matters: a lowercase scheme is not recognised.
var_dump(Str::startsWith('bearer abc123', 'Bearer '));
// The fluent object exposes the same check.
var_dump(Str::of('admin/users')->startsWith('admin'));Middleware that skips a set of prefixes
$request->path() returns the path without a leading slash, which is why every needle here is written without one — a common off-by-one that makes the check silently never match. Passing the whole $except array in one call is the idiomatic use of the iterable form. This needs a booted application, so it is displayed rather than executed.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class LogSlowPageLoads
{
protected array $except = ['api/', 'horizon/', '_debugbar/'];
public function handle(Request $request, Closure $next)
{
if (Str::startsWith($request->path(), $this->except)) {
return $next($request);
}
// ... timing logic for real page loads
return $next($request);
}
}
Discussion