What's the most efficient test of whether a PHP string ends with another string?
Asked Answered
M

13

129

The standard PHP way to test whether a string $str ends with a substring $test is:

$endsWith = substr( $str, -strlen( $test ) ) == $test

Is this the fastest way?

Mechanism answered 6/3, 2009 at 17:3 Comment(3)
Similar: #834803Insufficiency
You might find s($str)->endsWith($test) or s($str)->endsWithIgnoreCase($test) helpful, as found in this standalone library.Janka
PHP 8.0 introduces new method for this job str_end_with: https://mcmap.net/q/45126/-startswith-and-endswith-functions-in-phpPilothouse
B
153

What Assaf said is correct. There is a built in function in PHP to do exactly that.

substr_compare($str, $test, strlen($str)-strlen($test), strlen($test)) === 0;

If $test is longer than $str PHP will give a warning, so you need to check for that first.

function endswith($string, $test) {
    $strlen = strlen($string);
    $testlen = strlen($test);
    if ($testlen > $strlen) return false;
    return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
}
Bertina answered 6/3, 2009 at 17:37 Comment(4)
Nice. It does seem like comparing in-place would be faster than substr(), as Assaf pointed out.Mechanism
mcrumley's answer is cool, but it should use '===' instead of '=='. '===' is more strict and usually does what you want, while '==' can lead to nasty surprises. mcrumley's third code snippet is correct, but the first two aren't. substr_compare() returns false in some error cases. In PHP, false == 0, so the code snippets would signal that the string has been found. With ===, this doesn't happen.Radioactivate
I ran into a bug today which will break the solution that you have given from PHP 5.5.11 and onwards. bugs.php.net/bug.php?id=67043Ricardoricca
3 function calls overhead doesn't make it fastest.Hooker
C
67

This method is a tiny bit more memory-expensive, but it is faster:

stripos(strrev($haystack), $reversed_needle) === 0;

This is best when you know exactly what the needle is, so you can hard-code it reversed. If you reverse the needle programmatically, it becomes slower than the earlier method.

Edit (12 years later): LOL, this is a super-old answer that I wrote when I didn't know what I was actually talking about. I'd like the think I've grown since then. @DavidHarkness is right, it is not very efficient in the negative case. Probably much faster to just iterate in reverse and bail early if you really need as much perf as possible. Also, php probably has better ways to do this now. Honestly, I haven't written php in nearly a decade, so I'll leave it up to others now.

Chemiluminescence answered 26/1, 2010 at 4:40 Comment(6)
-1 I seriously doubt this is faster, and it's tricky (cool but not usually helpful). If the haystack doesn't end with the needle, stripos will iterate the entire string in the worst case whereas substr_compare will compare at most the length of the needle. Yes, substr_compare requires calculating the length of the haystack (and much smaller needle), but this method requires that and copying it in full, and possibly converting the entire thing into lowercase to boot.Neusatz
cool approach but inefficient (as mentioned in answer itself) in many cases! Still an upvote for being weirdly creative and demonstrating that there can always be more ways of doing same thing that you may ever imagine. Cheers!Mccammon
I tested and find this method faster than the accepted answer. Even if both haystack and needle are reversed on the fly, it is faster.Bogey
@DavidHarkness Did you test to see if your doubts were warranted?Customable
@Customable No, it's not worth the time to benchmark since substr_compare() works nicely.Neusatz
Very clever, but this is a two-liner I hope, with the first line commenting to the reader what it does :)Bing
D
64
$endsWith = substr_compare( $str, $test, -strlen( $test ) ) === 0

Negative offset "starts counting from the end of the string".

Dachau answered 12/4, 2016 at 9:41 Comment(2)
IMO it's one of the best from the provided solutionsFlaggy
+200 if I could. The key insight here is that using a negative length gets the end part of the string. You could also use substr with the -ve length and do an == against $test. The other answers are poor.Mute
F
11

Here’s a simple way to check whether one string ends with another, by giving strpos an offset right where the string should be found:

function stringEndsWith($whole, $end)
{
    return (strpos($whole, $end, strlen($whole) - strlen($end)) !== false);
}

Straightforward, and I think this’ll work in PHP 4.

Fabrianne answered 9/3, 2011 at 23:49 Comment(0)
L
8

It depends on which sort of efficiency you care about.

Your version uses more memory due to the extra copy from the use of substr.

An alternative version might search the original string for the last occurrence of the substring without making a copy, but would probably be slower due to more testing.

Probably the most efficient way is to do loop char-by-char from the -sterlen(test) position till the end of the string and compare. That's the minimal amount of comparisons you can hope to do and there's hardly any extra memory used.

Lucre answered 6/3, 2009 at 17:12 Comment(0)
S
6

In PHP 8:

str_ends_with('haystack', 'stack'); // true
str_ends_with('haystack', 'K'); // false

and also:

str_starts_with('haystack', 'hay'); // true

PHP RFC: Add str_starts_with(), str_ends_with() and related functions

Solan answered 18/8, 2020 at 21:44 Comment(0)
A
5

Another way would be to use the strrpos function:

strrpos($str, $test) == strlen($str) - strlen($test)

But that’s not faster.

Arturo answered 6/3, 2009 at 17:14 Comment(0)
A
3

I hope that the below answer may be efficient and also simple:

$content = "The main string to search";
$search = "search";
//For compare the begining string with case insensitive. 
if(stripos($content, $search) === 0) echo 'Yes';
else echo 'No';

//For compare the begining string with case sensitive. 
if(strpos($content, $search) === 0) echo 'Yes';
else echo 'No';

//For compare the ending string with case insensitive. 
if(stripos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';

//For compare the ending string with case sensitive. 
if(strpos(strrev($content), strrev($search)) === 0) echo 'Yes';
else echo 'No';
Ataman answered 18/10, 2013 at 13:4 Comment(1)
Don't you think that large $string values could cause some memory problems if they should be reversed, These approaches using strrev do not look good to me in terms of efficiencParrett
R
1

Don't know if this is fast or not but for a single character test, these work, too:

(array_pop(str_split($string)) === $test) ? true : false;
($string[strlen($string)-1] === $test) ? true : false;
(strrev($string)[0] === $test) ? true : false;
Recession answered 15/7, 2016 at 10:12 Comment(1)
Even if they do: don't you think that large $string values could cause some memory problems if they should be reversed, or split to an array first? These approaches do not look good to me in terms of efficiencyParrett
S
1

easiest way to check it via regular expression

for example to check if the mail given is gmail:

echo (preg_match("/@gmail\.com$/","[email protected]"))?'true':'false';
Spicate answered 30/5, 2018 at 13:10 Comment(0)
C
0

I'm thinking the reverse functions like strrchr() would help you match the end of the string the fastest.

Condemnation answered 6/3, 2009 at 18:33 Comment(0)
C
0

This is pure PHP, without calling external functions, except for strlen.

function endsWith ($ends, $string)
{
    $strLength = strlen ($string);
    $endsLength = strlen ($ends);
    for ($i = 0; $i < $endsLength; $i++)
    {
        if ($string [$strLength - $i - 1] !== $ends [$i])
            return false;
    }
    return true;
}
Cob answered 30/12, 2014 at 18:45 Comment(0)
A
-1

for single-char needle:

if (@strrev($haystack)[0] == $needle) {
   // yes, it ends...
}
Anglim answered 19/9, 2019 at 2:6 Comment(1)
Can you add some more explanation to your answer such that others can learn from it? Why use strrev instead of substr($haystack, -1)? Especially for long string, it could take some time to reverse itParrett

© 2022 - 2024 — McMap. All rights reserved.