PHP: How do you check whether a string starts or ends with something?
You often face the requirement of determining whether a string starts or ends
with a particular substring. For example whether a URL starts with https://,
or whether a file name ends in .php.
From PHP 8 onwards
Since PHP 8.0 there are two functions for this: str_starts_with and str_ends_with. Both are case-sensitive:
<?php
$url = 'https://www.hostinggutscheine.de/blog/';
if (str_starts_with($url, 'https://')) {
// The following line IS executed.
echo "The URL starts with 'https://'.\n";
}
if (str_ends_with($url, '/')) {
// The following line IS executed.
echo "The URL ends with a slash.\n";
}
if (str_starts_with($url, 'HTTPS://')) {
// The following line is NOT executed.
echo "The URL starts with 'HTTPS://'.\n";
}
?>
If case should not matter, convert both strings to lower case beforehand. Use the strtolower function for that:
<?php
if (str_starts_with(strtolower($url), 'https://')) {
// The following line IS executed.
echo "The URL starts with 'https://' (regardless of case).\n";
}
?>
An empty needle returns true from both functions. That is the mathematically
clean answer, since every string starts and ends with nothing:
str_starts_with('abc', '') => true
str_ends_with('abc', '') => true
Before PHP 8
If you are running an older version of PHP you have to write the two functions yourself. For the start of a string that is unproblematic:
<?php
function startsWith($haystack, $needle) {
return substr($haystack, 0, strlen($needle)) === $needle;
}
?>
For the end, however, there is a trap. This short form is very common:
<?php
function endsWith($haystack, $needle) {
// Careful, this variant is buggy!
return substr($haystack, -strlen($needle)) === $needle;
}
?>
If the needle is empty then strlen($needle) is 0 and the call reads
substr($haystack, -0). And -0 in PHP is simply 0. So substr() does not
return the empty string, it returns the whole string:
substr($url, -0) => 'https://www.hostinggutscheine.de/blog/'
In that case the function answers false, even though true would be correct.
You therefore need an explicit check:
<?php
function endsWith($haystack, $needle) {
$length = strlen($needle);
if ($length === 0) {
return true;
}
return substr($haystack, -$length) === $needle;
}
?>
Why not strpos?
You often come across the suggestion of using strpos for the start of a string:
<?php
if (strpos($haystack, $needle) === 0) {
// ...
}
?>
That works, but it is the wrong tool. strpos() searches for the substring in
the entire string, and only afterwards do you compare the position found
with 0. Whether the string begins at position 0 is however already settled
after comparing the first character.
I measured this. The haystack is 20,000 b characters followed by needle,
and we search 20,000 times for needle at the start:
str_starts_with : 0.000 s
strpos === 0 : 0.002 s
In absolute terms that is very little. The point is a different one: the work
strpos() does grows with the length of the string, the work
str_starts_with() does not. With short strings you will not notice, with large
documents you will.
What about accented characters?
The two functions compare byte by byte rather than character by character. You
still do not need the mb_* variants:
str_starts_with('Über uns', 'Ü') => true
strlen('Ü') => 2
mb_strlen('Ü') => 1
The Ü takes two bytes in UTF-8, yet the result is correct. The reason is that
UTF-8 is prefix-free: the first byte of a character can never appear as a
continuation byte of another character. A byte-wise comparison at the start or
the end of a string therefore cannot land in the middle of a character.
About Netcup (advertisement)
The German host Netcup offers, among other things, affordable and powerful web hosting packages, KVM-based root servers and dedicated servers. With our voucher codes you can save even more (6€ off your first order, 30% off all KVM-based root servers, ...).