1704

How can I write two functions that would take a string and return if it starts with the specified character/string or ends with it?

For example:

$str = '|apples}';
echo startsWith($str, '|'); //Returns true
echo endsWith($str, '}'); //Returns true
Timo Tijhof
10.4k6 gold badges38 silver badges53 bronze badges
asked May 7, 2009 at 12:14
5
  • 26
    See Laravel's Str class startsWith() and endsWith() for well-tested methods. Edge cases had been encountered, so the widespread use of this code is an advantage. Commented Dec 6, 2014 at 23:32
  • 1
    You might find s($str)->startsWith('|') and s($str)->endsWith('}') helpful, as found in this standalone library. Commented Jul 26, 2016 at 23:46
  • 5
    Warning: most answers here are unreliable in multi-byte encodings such as UTF-8. Commented May 15, 2017 at 14:07
  • Following up to my above comment, you may make sure to use the latest version (as of today, 5.4). Notably, startsWith() has been optimized for large haystack strings. Commented May 22, 2017 at 18:51
  • 11
    PHP 8.0 introduces new methods for this job str_starts_with and str_end_with: stackoverflow.com/a/64160081/7082164 Commented Oct 1, 2020 at 17:06

37 Answers 37

1
2
1872
Answer recommended by PHP Collective

PHP 8.0 and higher

Since PHP 8.0 you can use:

str_starts_with and
str_ends_with

Example
var_dump(str_starts_with('|apples}', '|'));
var_dump(str_ends_with('|apples}', '}'));

PHP before 8.0

function startsWith( $haystack, $needle ) {
 $length = strlen( $needle );
 return substr( $haystack, 0, $length ) === $needle;
}
function endsWith( $haystack, $needle ) {
 $length = strlen( $needle );
 if( !$length ) {
 return true;
 }
 return substr( $haystack, -$length ) === $needle;
}
Salman Arshad
274k85 gold badges450 silver badges540 bronze badges
answered May 7, 2009 at 12:24
Sign up to request clarification or add additional context in comments.

33 Comments

I'd say endsWith('foo', '') == false is the correct behavior. Because foo doesn't end with nothing. 'Foo' ends with 'o', 'oo' and 'Foo'.
EndsWith can be written a lot shorter: return substr($haystack, -strlen($needle))===$needle;
@RokKralj But only if $needle is not empty.
You can avoid the if altogether by passing $length as the third parameter to substr: return (substr($haystack, -$length, $length);. This handles the case of $length == 0 by returning an empty string and not the whole $haystack.
@MrHus I would recommend using multi-byte safe functions, e.g. mb_strlen and mb_substr
|
1105

PHP < 8

You can use substr_compare function to check start-with and ends-with:

function startsWith($haystack, $needle) {
 return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
function endsWith($haystack, $needle) {
 return substr_compare($haystack, $needle, -strlen($needle)) === 0;
}

This should be one of the fastest solutions on PHP 7 (benchmark script). Tested against 8KB haystacks, various length needles and full, partial and no match cases. strncmp is a touch faster for starts-with but it cannot check ends-with.

answered May 6, 2012 at 18:22

6 Comments

Please note that @DavidWallace and @FrancescoMM comments apply to an older version of this answer. The current answer uses strrpos which (should) fail immediately if needle does not match the beginning of haystack.
I don't get it. Based on php.net/manual/en/function.strrpos.php: "If the value is negative, search will instead start from that many characters from the end of the string, searching backwards." This seems to indicate that we're starting at character 0 (due to -strlength($haystack)) and searching backward from there? Doesn't that mean you're not searching anything? I also don't understand the !== false parts of this. I'm guessing this is relying on a quirk of PHP where some values are "truthy" and others "falsy" but how does that work in this case?
@Welbog: for example haystack = xxxyyy needle = yyy and using strrpos the search starts from the first x. Now we do not have a successful match here (found x instead of y) and we cannot go backward anymore (we're at start of string) the search fails immediately. About using !== false -- strrpos in the above example will return 0 or false and not other value. Likewise, strpos in the above example can return $temp (the expected position) or false. I went with !== false for consistency but you could use === 0 and === $temp in the functions respectively.
So much unnecessary work on this. Why not use strpos === 0 for the startsWith. Down voted for drastically over complicating suboptimal coding response.
@spoo it has already been established that strpos === 0 is a terrible solution if haystack is large and needle does not exist.
|
262

Updated 23-Aug-2016

Functions

function substr_startswith($haystack, $needle) {
 return substr($haystack, 0, strlen($needle)) === $needle;
}
function preg_match_startswith($haystack, $needle) {
 return preg_match('~' . preg_quote($needle, '~') . '~A', $haystack) > 0;
}
function substr_compare_startswith($haystack, $needle) {
 return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}
function strpos_startswith($haystack, $needle) {
 return strpos($haystack, $needle) === 0;
}
function strncmp_startswith($haystack, $needle) {
 return strncmp($haystack, $needle, strlen($needle)) === 0;
}
function strncmp_startswith2($haystack, $needle) {
 return $haystack[0] === $needle[0]
 ? strncmp($haystack, $needle, strlen($needle)) === 0
 : false;
}

Tests

echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
 if($i % 2500 === 0) echo '.';
 $test_cases[] = [
 random_bytes(random_int(1, 7000)),
 random_bytes(random_int(1, 3000)),
 ];
}
echo "done!\n";
$functions = ['substr_startswith', 'preg_match_startswith', 'substr_compare_startswith', 'strpos_startswith', 'strncmp_startswith', 'strncmp_startswith2'];
$results = [];
foreach($functions as $func) {
 $start = microtime(true);
 foreach($test_cases as $tc) {
 $func(...$tc);
 }
 $results[$func] = (microtime(true) - $start) * 1000;
}
asort($results);
foreach($results as $func => $time) {
 echo "$func: " . number_format($time, 1) . " ms\n";
}

Results (PHP 7.0.9)

(Sorted fastest to slowest)

strncmp_startswith2: 40.2 ms
strncmp_startswith: 42.9 ms
substr_compare_startswith: 44.5 ms
substr_startswith: 48.4 ms
strpos_startswith: 138.7 ms
preg_match_startswith: 13,152.4 ms

Results (PHP 5.3.29)

(Sorted fastest to slowest)

strncmp_startswith2: 477.9 ms
strpos_startswith: 522.1 ms
strncmp_startswith: 617.1 ms
substr_compare_startswith: 706.7 ms
substr_startswith: 756.8 ms
preg_match_startswith: 10,200.0 ms

startswith_benchmark.php

answered Aug 24, 2011 at 0:07

22 Comments

If the strings are not empty, as in your tests, this is actually somehow (20-30%) faster: function startswith5b($haystack, $needle) {return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;} I added a reply below.
@Jronny Because 110 is less than 133...??
Darn, I don't know what went to my head that time. Prolly the lack of sleep.
@mpen, I noticed not the elephant at all :(
$haystack[0] will throw a notice error if you don't test it with isset. The same for needles. But if you add tests, it will slow down its performance
|
147

All answers so far seem to do loads of unnecessary work, strlen calculations, string allocations (substr), etc. The 'strpos' and 'stripos' functions return the index of the first occurrence of $needle in $haystack:

function startsWith($haystack,$needle,$case=true)
{
 if ($case)
 return strpos($haystack, $needle, 0) === 0;
 return stripos($haystack, $needle, 0) === 0;
}
function endsWith($haystack,$needle,$case=true)
{
 $expectedPosition = strlen($haystack) - strlen($needle);
 if ($case)
 return strrpos($haystack, $needle, 0) === $expectedPosition;
 return strripos($haystack, $needle, 0) === $expectedPosition;
}
Ram Sharma
8,8157 gold badges48 silver badges59 bronze badges
answered May 13, 2009 at 21:23

14 Comments

endsWith() function has an error. Its first line should be (without the -1): $expectedPosition = strlen($haystack) - strlen($needle);
The strlen() thing is not unnecessary. In case the string doesn't start with the given needle then ur code will unnecessarily scan the whole haystack.
@Mark yea, checking just the beginning is a LOT faster, especially if you're doing something like checking MIME types (or any other place where the string is bound to be large)
@mark I did some benchmarks with 1000 char haystack and 10 or 800 char needle and strpos was 30% faster. Do your benchmarks before stating that something is faster or not...
You should strongly consider quoting the needle like strpos($haystack, "$needle", 0) if there's any chance it's not already a string (e.g., if it's coming from json_decode()). Otherwise, the [odd] default behavior of strpos() may cause unexpected results: "If needle is not a string, it is converted to an integer and applied as the ordinal value of a character."
|
54
function startsWith($haystack, $needle, $case = true) {
 if ($case) {
 return (strcmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
 }
 return (strcasecmp(substr($haystack, 0, strlen($needle)), $needle) === 0);
}
function endsWith($haystack, $needle, $case = true) {
 if ($case) {
 return (strcmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
 }
 return (strcasecmp(substr($haystack, strlen($haystack) - strlen($needle)), $needle) === 0);
}

Credit To:

Check if a string ends with another string

Check if a string begins with another string

Rubens Mariuzzo
29.4k28 gold badges125 silver badges149 bronze badges
answered May 7, 2009 at 12:15

3 Comments

strtolower is not the best way to make case insensitive functions. In some locales casing is more complex than just upper and lower.
I see complaining and no solution... If you're gonna say it's bad, then you should give an example of how it should be as well.
@WebDevHobo: that's why I added an answer myself a day before your comment. For your code strcasecmp was indeed the right thing to do.
54

PHP 8 update

PHP 8 includes new str_starts_with and str_ends_with functions that finally provide a performant and convenient solution to this problem:

$str = "beginningMiddleEnd";
if (str_starts_with($str, "beg")) echo "printed\n";
if (str_starts_with($str, "Beg")) echo "not printed\n";
if (str_ends_with($str, "End")) echo "printed\n";
if (str_ends_with($str, "end")) echo "not printed\n";

The RFC for this feature provides more information, and also a discussion of the merits and problems of obvious (and not-so-obvious) userland implementations.

Flimm
155k49 gold badges283 silver badges295 bronze badges
answered Jun 3, 2020 at 9:13

Comments

48

This question already has many answers, but in some cases you can settle for something simpler than all of them. If the string you're looking for is known (hardcoded), you can use regular expressions without any quoting etc.

Check if a string starts with 'ABC':

preg_match('/^ABC/', $myString); // "^" here means beginning of string

ends with 'ABC':

preg_match('/ABC$/', $myString); // "$" here means end of string

In my simple case, I wanted to check if a string ends with slash:

preg_match('#/$#', $myPath); // Use "#" as delimiter instead of escaping slash

The advantage: since it's very short and simple, you don't have to define a function (such as endsWith()) as shown above.

But again -- this is not a solution for every case, just this very specific one.

answered Jan 6, 2016 at 8:59

2 Comments

you don't need to hard code the string. the regex can be dynamic.
@self true, but if the string is not hardcoded, you have to escape it. Currently there are 2 answers on this question that do it. This is easy, but it complicates the code just a bit. So my point was that for very simple cases, where hardcoding is possible, you can keep it simple.
39

The regex functions above, but with the other tweaks also suggested above:

 function startsWith($needle, $haystack) {
 return preg_match('/^' . preg_quote($needle, '/') . '/', $haystack);
 }
 function endsWith($needle, $haystack) {
 return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
 }
Timo Tijhof
10.4k6 gold badges38 silver badges53 bronze badges
answered Jun 19, 2009 at 16:11

1 Comment

in php for string operations the ordering of parameters is $haystack, $needle. these functions are backwards and act like array functions where the ordering is actually $needle, $haystack.
33

Fastest endsWith() solution:

# Checks if a string ends in a string
function endsWith($haystack, $needle) {
 return substr($haystack,-strlen($needle))===$needle;
}

Benchmark:

# This answer
function endsWith($haystack, $needle) {
 return substr($haystack,-strlen($needle))===$needle;
}
# Accepted answer
function endsWith2($haystack, $needle) {
 $length = strlen($needle);
 return $length === 0 ||
 (substr($haystack, -$length) === $needle);
}
# Second most-voted answer
function endsWith3($haystack, $needle) {
 // search forward starting from end minus needle length characters
 if ($needle === '') {
 return true;
 }
 $diff = \strlen($haystack) - \strlen($needle);
 return $diff >= 0 && strpos($haystack, $needle, $diff) !== false;
}
# Regex answer
function endsWith4($haystack, $needle) {
 return preg_match('/' . preg_quote($needle, '/') . '$/', $haystack);
}
function timedebug() {
 $test = 10000000;
 $time1 = microtime(true);
 for ($i=0; $i < $test; $i++) {
 $tmp = endsWith('TestShortcode', 'Shortcode');
 }
 $time2 = microtime(true);
 $result1 = $time2 - $time1;
 for ($i=0; $i < $test; $i++) {
 $tmp = endsWith2('TestShortcode', 'Shortcode');
 }
 $time3 = microtime(true);
 $result2 = $time3 - $time2;
 for ($i=0; $i < $test; $i++) {
 $tmp = endsWith3('TestShortcode', 'Shortcode');
 }
 $time4 = microtime(true);
 $result3 = $time4 - $time3;
 for ($i=0; $i < $test; $i++) {
 $tmp = endsWith4('TestShortcode', 'Shortcode');
 }
 $time5 = microtime(true);
 $result4 = $time5 - $time4;
 echo $test.'x endsWith: '.$result1.' seconds # This answer<br>';
 echo $test.'x endsWith2: '.$result4.' seconds # Accepted answer<br>';
 echo $test.'x endsWith3: '.$result2.' seconds # Second most voted answer<br>';
 echo $test.'x endsWith4: '.$result3.' seconds # Regex answer<br>';
 exit;
}
timedebug();

Benchmark Results:

10000000x endsWith: 1.5760900974274 seconds # This answer
10000000x endsWith2: 3.7102129459381 seconds # Accepted answer
10000000x endsWith3: 1.8731069564819 seconds # Second most voted answer
10000000x endsWith4: 2.1521229743958 seconds # Regex answer
answered Jul 24, 2018 at 6:05

2 Comments

+1 for taking time to compare different solutions and actually benchmark them! you should also mention what version of PHP you used, as optimisations are done as the language evolves! I've seen dramatic improvements on string comparison functions from one PHP version to another :)
echoing @ChristopheDeliens and his request to provide the PHP version. I ran your test on 7.3.2 and got similar results FWIW.
29

If speed is important for you, try this.(I believe it is the fastest method)

Works only for strings and if $haystack is only 1 character

function startsWithChar($needle, $haystack)
{
 return ($needle === $haystack[0]);
}
function endsWithChar($needle, $haystack)
{
 return ($needle === $haystack[strlen($haystack) - 1]);
}
$str='|apples}';
echo startsWithChar('|',$str); //Returns true
echo endsWithChar('}',$str); //Returns true
echo startsWithChar('=',$str); //Returns false
echo endsWithChar('#',$str); //Returns false
answered Dec 15, 2009 at 8:53

5 Comments

this is probably the most efficient answer because not using any function as extra, just usual string...
It should likely check if string has at least one character and has the two parameter swapped
Creative. Needles which contain haystacks. BTW there is some ugly waning with: endsWithChar('','x'), but the result is correct
I like your answer, but it's quite funny,... the needle and haystack are the other way around :) ... i.e. you would search for a Needle in a Haystack, therefore, it should be: return ($needle === $haystack[0]); , but nice answer, thanks!
@HeiderSati: Great observation! That is what @Tino was talking about Creative. Needles which contain haystacks.... I didn't pay enough attention. Thanks! I fixed it. :)
23

Here are two functions that don't introduce a temporary string, which could be useful when needles are substantially big:

function startsWith($haystack, $needle)
{
 return strncmp($haystack, $needle, strlen($needle)) === 0;
}
function endsWith($haystack, $needle)
{
 return $needle === '' || substr_compare($haystack, $needle, -strlen($needle)) === 0;
}
answered Aug 2, 2013 at 7:40

4 Comments

+1 Works since PHP5.1 and IMHO best answer. But endsWidth should do return $needle==='' || substr_compare(... so it works as expected for -strlen($needle)===0 which, without the fix, makes endsWith('a','') return false
@Tino Thanks ... I feel that's a bug in substr_compare() actually, so I've added a PR to fix that :)
The call endsWith('', 'foo') triggers a Warning: "substr_compare(): The start position cannot exceed initial string length". Maybe that's another bug in substr_compare(), but to avoid it, you need a pre-check like ...|| (strlen($needle) <= strlen($haystack) && substr_compare(...) === 0);
@gx_ No need to slowdown with more code. Just use return $needle === '' || @substr_compare(.. to suppress this warning.
19

I realize this has been finished, but you may want to look at strncmp as it allows you to put the length of the string to compare against, so:

function startsWith($haystack, $needle, $case=true) {
 if ($case)
 return strncasecmp($haystack, $needle, strlen($needle)) == 0;
 else
 return strncmp($haystack, $needle, strlen($needle)) == 0;
} 
answered Sep 17, 2009 at 2:50

4 Comments

how would you do endswith with this?
@Mark - you can look at the accepted answer, but I prefer to use strncmp mainly because I think it is safer.
I mean with strncmp specifically. You can't specify an offset. That would mean your endsWith function would have to use a different method entirely.
@Mark - For endsWith I would just use strrpos (php.net/manual/en/function.strrpos.php), but, generally, anytime you go to use strcmp strncmp is probably a safer option.
15

Here's a multi-byte safe version of the accepted answer, it works fine for UTF-8 strings:

function startsWith($haystack, $needle)
{
 $length = mb_strlen($needle, 'UTF-8');
 return (mb_substr($haystack, 0, $length, 'UTF-8') === $needle);
}
function endsWith($haystack, $needle)
{
 $length = mb_strlen($needle, 'UTF-8');
 return $length === 0 ||
 (mb_substr($haystack, -$length, $length, 'UTF-8') === $needle);
}
answered Nov 10, 2017 at 13:59

5 Comments

im pretty sure this is just a waste of CPU. all you need to check, for StarstWith and EndsWith, is just checking that the bytes match, and that's exactly what the accepted answer is doing. this 1 wastes time calculating the number of utf8 characters of the needle, and where the position of the n'th utf8 character of the haystack is.. i think, without being 100% certain, this is just a waste of cpu. can you come up with an actual test case where the accepted answer fails, and this doesn't?
@hanshenrik - it could happen btw, in the very rare case when you look for a string that contains the same bytes as an UTF8 but with half of the last character missing. Like, you have unicode C5 91 (letter "ő") and you look for C5 (letter "Å") it shouldn't give you a match. On the other hand, sure, why would you search an utf haystack for a non-utf needle... But for bulletproof checks, this must be considered a possibility.
In startsWith it should be $length = mb_strlen($needle, 'UTF-8');
@ThomasKekeisen Thanks, fixed it.
The accepted (well, currently accepted) solution is already multibyte-safe. It's actually binary-safe, which is an even stronger guarantee.
12

You can use strpos and strrpos

$bStartsWith = strpos($sHaystack, $sNeedle) == 0;
$bEndsWith = strrpos($sHaystack, $sNeedle) == strlen($sHaystack)-strlen($sNeedle);
Bhavik Shah
2,3011 gold badge18 silver badges32 bronze badges
answered May 12, 2015 at 12:36

1 Comment

Should you be using triple equals here strpos($sHaystack, $sNeedle) == 0 like this strpos($sHaystack, $sNeedle) === 0? I see a bug, when false == 0 evaluates to true.
10

Focusing on startswith, if you are sure strings are not empty, adding a test on the first char, before the comparison, the strlen, etc., speeds things up a bit:

function startswith5b($haystack, $needle) {
 return ($haystack{0}==$needle{0})?strncmp($haystack, $needle, strlen($needle)) === 0:FALSE;
}

It is somehow (20%-30%) faster. Adding another char test, like $haystack{1}===$needle{1} does not seem to speedup things much, may even slow down.

=== seems faster than == Conditional operator (a)?b:c seems faster than if(a) b; else c;


For those asking "why not use strpos?" calling other solutions "unnecessary work"


strpos is fast, but it is not the right tool for this job.

To understand, here is a little simulation as an example:

Search a12345678c inside bcdefga12345678xbbbbb.....bbbbba12345678c

What the computer does "inside"?

 With strccmp, etc...
 is a===b? NO
 return false
 With strpos
 is a===b? NO -- iterating in haysack
 is a===c? NO
 is a===d? NO
 ....
 is a===g? NO
 is a===g? NO
 is a===a? YES
 is 1===1? YES -- iterating in needle
 is 2===3? YES
 is 4===4? YES
 ....
 is 8===8? YES
 is c===x? NO: oh God,
 is a===1? NO -- iterating in haysack again
 is a===2? NO
 is a===3? NO
 is a===4? NO
 ....
 is a===x? NO
 is a===b? NO
 is a===b? NO
 is a===b? NO
 is a===b? NO
 is a===b? NO
 is a===b? NO
 is a===b? NO
 ...
 ... may many times...
 ...
 is a===b? NO
 is a===a? YES -- iterating in needle again
 is 1===1? YES
 is 2===3? YES
 is 4===4? YES
 is 8===8? YES
 is c===c? YES YES YES I have found the same string! yay!
 was it at position 0? NOPE
 What you mean NO? So the string I found is useless? YEs.
 Damn.
 return false

Assuming strlen does not iterate the whole string (but even in that case) this is not convenient at all.

answered Jul 28, 2013 at 14:00

4 Comments

There's only a speed up if the first characters are different.
@Jack yes, of course, the idea is that statistically that happens, so the speedup is generrally a 20%-30% over the whole test set (including cases where it is not different). You gain a lot when they are different and loose very little when they are not. In the average you gain that 30% (varies depending on set, but mostly you gain speed on large tests)
"but it is not the right tool for this job"... Any citation?
WTF. I listed all the process below whom should I cite, more than that? Would you use a function that searches till the end of a string to tell you that the fist character is not an 'a'? Do it who cares? It's not the right tool because it is a tool for searching, not for comparing, there is no need to cite Aristoteles to state the obvious!
9

Short and easy-to-understand one-liners without regular expressions.

startsWith() is straight forward.

function startsWith($haystack, $needle) {
 return (strpos($haystack, $needle) === 0);
}

endsWith() uses the slightly fancy and slow strrev():

function endsWith($haystack, $needle) {
 return (strpos(strrev($haystack), strrev($needle)) === 0);
}
answered Jun 28, 2011 at 22:40

2 Comments

@FrancescoMM: strpos is not the "right tool"... Why? What are the "right tools" then? EDIT: I read your answer below. I thought programming is like invention using the resources you have.. So there's no right or wrong... only working or not working... performance is secondary.
"because it is a tool for searching, not for comparing?" Cit. Aristoteles
8

in short:

function startsWith($str, $needle){
 return substr($str, 0, strlen($needle)) === $needle;
}
function endsWith($str, $needle){
 $length = strlen($needle);
 return !$length || substr($str, - $length) === $needle;
}
answered Apr 19, 2012 at 20:50

Comments

8

I usually end up going with a library like underscore-php these days.

require_once("vendor/autoload.php"); //use if needed
use Underscore\Types\String; 
$str = "there is a string";
echo( String::startsWith($str, 'the') ); // 1
echo( String::endsWith($str, 'ring')); // 1 

The library is full of other handy functions.

answered Jan 23, 2015 at 3:53

Comments

8

The answer by mpen is incredibly thorough, but, unfortunately, the provided benchmark has a very important and detrimental oversight.

Because every byte in needles and haystacks is completely random, the probability that a needle-haystack pair will differ on the very first byte is 99.609375%, which means that, on average, about 99609 of the 100000 pairs will differ on the very first byte. In other words, the benchmark is heavily biased towards startswith implementations which check the first byte explicitly, as strncmp_startswith2 does.

If the test-generating loop is instead implemented as follows:

echo 'generating tests';
for($i = 0; $i < 100000; ++$i) {
 if($i % 2500 === 0) echo '.';
 $haystack_length = random_int(1, 7000);
 $haystack = random_bytes($haystack_length);
 $needle_length = random_int(1, 3000);
 $overlap_length = min(random_int(0, $needle_length), $haystack_length);
 $needle = ($needle_length > $overlap_length) ?
 substr($haystack, 0, $overlap_length) . random_bytes($needle_length - $overlap_length) :
 substr($haystack, 0, $needle_length);
 $test_cases[] = [$haystack, $needle];
}
echo " done!<br />";

the benchmark results tell a slightly different story:

strncmp_startswith: 223.0 ms
substr_startswith: 228.0 ms
substr_compare_startswith: 238.0 ms
strncmp_startswith2: 253.0 ms
strpos_startswith: 349.0 ms
preg_match_startswith: 20,828.7 ms

Of course, this benchmark may still not be perfectly unbiased, but it tests the efficiency of the algorithms when given partially matching needles as well.

answered Sep 21, 2016 at 14:31

1 Comment

This is much more biased. The probability, in an unbiased world, that two random byte strings start with the same char, is 1/256. With the same two bytes is 1/65536. Even two random texts have 1/(26*26) probability of beginning with the same two chars. More chars in common is even more lottery-like small probability, in a real world. You are, instead, creating a world in which many strings start with the same bytes, and this is much far away from reality, and biased. You may have some specific scenarios like this, like phones with prefix, but in other cases this use scenario is not useful.
7

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

$content = "The main string to search";
$search = "T";
//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';
answered Oct 18, 2013 at 13:01

Comments

6

Note:

It's 2024, we have better options how, namely str_starts_with is exactly for this job. This article is only here for two reasons, 1: to support older php versions (7.x), legacy code, wp plugins, etc, and 2: because I like it.

So here's a tiny improvement to startsWith:

function startsWith($haystack,$needle) {
 if($needle==="") return true;
 if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
 return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}

That extra line, comparing the first character of the strings, can make the false case return immediately, therefore making many of your comparisons a lot faster (7x faster when I measured). In the true case you pay virtually no price in performance for that single line so I think it's worth including. (Also, in practice, when you test many strings for a specific starting chunk, most comparisons will fail since in a typical case you're looking for something.)

NOTE: the bug in @Tino's comment below has aleady been fixed

As for strings vs integers

If you want to force string comparison (that is, you expect startsWith("1234",12) to be true), you'll need some typecasting:

function startsWith($haystack,$needle) {
 if($needle==="") return true;
 $haystack = (string)$haystack;
 $needle = (string)$needle;
 if($haystack[0]<>$needle[0]) return false; // ------------------------- speed boost!
 return (0===substr_compare($haystack,$needle,0,strlen($needle)));
}

I don't think it's necessary but it's an interesting edge case, leading to questions like "does boolean true begin with a t?" - so you decide, but make sure you decide for good.

answered Jul 26, 2016 at 20:02

5 Comments

Bug in your code: startsWith("123", "0") gives true
Yup, bad !$checking happened. Sorry! (Just wanted to illustrate the concept in line 3)
@Tino I'd say we could remove these 2 comments now, wouldn't you agree? I mean, point taken, it's fixed and it's been 2 years.
There is an answer of three years before this, adding the same speedup test at the beginning, and slightly faster. Anyway, if you want to be safe you also need to add if($haysack==="") return false; after the test for $needle, unless you test for $haysack outside the function.
@FrancescoMM – Yes, there are a few people on the planet and it's a pretty obvious idea.
5

This may work

function startsWith($haystack, $needle) {
 return substr($haystack, 0, strlen($needle)) == $needle;
}

Source: https://stackoverflow.com/a/4419658

answered Jun 4, 2013 at 8:51

Comments

4

The substr function can return false in many special cases, so here is my version, which deals with these issues:

function startsWith( $haystack, $needle ){
 return $needle === ''.substr( $haystack, 0, strlen( $needle )); // substr's false => empty string
}
function endsWith( $haystack, $needle ){
 $len = strlen( $needle );
 return $needle === ''.substr( $haystack, -$len, $len ); // ! len=0
}

Tests (true means good):

var_dump( startsWith('',''));
var_dump( startsWith('1',''));
var_dump(!startsWith('','1'));
var_dump( startsWith('1','1'));
var_dump( startsWith('1234','12'));
var_dump(!startsWith('1234','34'));
var_dump(!startsWith('12','1234'));
var_dump(!startsWith('34','1234'));
var_dump('---');
var_dump( endsWith('',''));
var_dump( endsWith('1',''));
var_dump(!endsWith('','1'));
var_dump( endsWith('1','1'));
var_dump(!endsWith('1234','12'));
var_dump( endsWith('1234','34'));
var_dump(!endsWith('12','1234'));
var_dump(!endsWith('34','1234'));

Also, the substr_compare function also worth looking. http://www.php.net/manual/en/function.substr-compare.php

answered Feb 21, 2012 at 11:52

Comments

4

Why not the following?

//How to check if a string begins with another string
$haystack = "valuehaystack";
$needle = "value";
if (strpos($haystack, $needle) === 0){
 echo "Found " . $needle . " at the beginning of " . $haystack . "!";
}

Output:

Found value at the beginning of valuehaystack!

Keep in mind, strpos will return false if the needle was not found in the haystack, and will return 0 if, and only if, needle was found at index 0 (AKA the beginning).

And here's endsWith:

$haystack = "valuehaystack";
$needle = "haystack";
//If index of the needle plus the length of the needle is the same length as the entire haystack.
if (strpos($haystack, $needle) + strlen($needle) === strlen($haystack)){
 echo "Found " . $needle . " at the end of " . $haystack . "!";
}

In this scenario there is no need for a function startsWith() as

(strpos($stringToSearch, $doesItStartWithThis) === 0)

will return true or false accurately.

It seems odd it's this simple with all the wild functions running rampant here.

Peter Mortensen
31.3k22 gold badges110 silver badges134 bronze badges
answered Jun 30, 2012 at 3:49

4 Comments

Seems odd that if you are searching for "xy" inside string "abcdefghijklmxyz" instead of just comparing "x" to "a" and returning FALSE, you look every character from "a" to "m" then end up finding "xy" inside the string, and at last you return FALSE because the position of it is not zero! This is what you are doing, and it is odd and wilder than any other rampant function here.
The simplicity is in the typing, not the logic.
It's not so much the logic, it's the possible optimization that Francsco was pointing out. Using strpos() will be slow except when it does match. strncmp() would be much better in this case.
When you're doing such low level functions, you typically want to go for the most speed-optimized solution, no matter how complex, as this will be called millions of times. Every microsecond you gain or lose here will make a very real difference. So better tweak the hell out of it (and then forget about the complexity, now that you have the function), instead of going for the looks and lose horrifying amount of time later when you don't even know what's gone wrong. Imagine checking a 2GB string that doesn't match.
4

I would do it like this

 function startWith($haystack,$needle){
 if(substr($haystack,0, strlen($needle))===$needle)
 return true;
 }
 function endWith($haystack,$needle){
 if(substr($haystack, -strlen($needle))===$needle)
 return true;
 }
answered Oct 7, 2014 at 13:27

1 Comment

Forgetting to return false if it doesn't match. Errgo incorrect as is the return value of a function should not be 'assumed', but I know what you're going after at least compared to other answers.
3

Based on James Black's answer, here is its endsWith version:

function startsWith($haystack, $needle, $case=true) {
 if ($case)
 return strncmp($haystack, $needle, strlen($needle)) == 0;
 else
 return strncasecmp($haystack, $needle, strlen($needle)) == 0;
}
function endsWith($haystack, $needle, $case=true) {
 return startsWith(strrev($haystack),strrev($needle),$case);
}

Note: I have swapped the if-else part for James Black's startsWith function, because strncasecmp is actually the case-insensitive version of strncmp.

answered Oct 30, 2009 at 0:17

2 Comments

Note that the strrev() is creative but very costly, especially if you have strings of say... 100Kb.
Use === instead of == to be sure. 0 is equal to a lot of things in PHP.
3

Many of the previous answers will work just as well. However, this is possibly as short as you can make it and have it do what you desire. You just state that you'd like it to 'return true'. So I've included solutions that returns boolean true/false and the textual true/false.

// boolean true/false
function startsWith($haystack, $needle)
{
 return strpos($haystack, $needle) === 0 ? 1 : 0;
}
function endsWith($haystack, $needle)
{
 return stripos($haystack, $needle) === 0 ? 1 : 0;
}
// textual true/false
function startsWith($haystack, $needle)
{
 return strpos($haystack, $needle) === 0 ? 'true' : 'false';
}
function endsWith($haystack, $needle)
{
 return stripos($haystack, $needle) === 0 ? 'true' : 'false';
}
answered Dec 6, 2013 at 19:45

3 Comments

True. However, Peter was asking for a function that would work with character strings. Nonetheless, I've updated my answer to appease you.
After the edit your solution now is completely obsolete. It returns 'true' and 'false' as strings, which are both true in a boolean sense. It's a good pattern for something like underhanded.xcott.com though ;)
Well, Peter just stated he wanted it to return 'true'. So I figured I'd return what he asked for. I've added both versions, just in case that isn't what he wanted.
3

No-copy and no-intern-loop:

function startsWith(string $string, string $start): bool
{
 return strrpos($string, $start, - strlen($string)) !== false;
}
function endsWith(string $string, string $end): bool
{
 return ($offset = strlen($string) - strlen($end)) >= 0 
 && strpos($string, $end, $offset) !== false;
}
hanshenrik
22.4k4 gold badges63 silver badges114 bronze badges
answered Aug 28, 2019 at 13:28

1 Comment

this should be much faster than MrHus's implementation! i might benchmark it
2

Here’s an efficient solution for PHP 4. You could get faster results if on PHP 5 by using substr_compare instead of strcasecmp(substr(...)).

function stringBeginsWith($haystack, $beginning, $caseInsensitivity = false)
{
 if ($caseInsensitivity)
 return strncasecmp($haystack, $beginning, strlen($beginning)) === 0;
 else
 return strncmp($haystack, $beginning, strlen($beginning)) === 0;
}
function stringEndsWith($haystack, $ending, $caseInsensitivity = false)
{
 if ($caseInsensitivity)
 return strcasecmp(substr($haystack, strlen($haystack) - strlen($ending)), $haystack) === 0;
 else
 return strpos($haystack, $ending, strlen($haystack) - strlen($ending)) !== false;
}
answered Apr 19, 2011 at 5:05

Comments

1

You also can use regular expressions:

function endsWith($haystack, $needle, $case=true) {
 return preg_match("/.*{$needle}$/" . (($case) ? "" : "i"), $haystack);
}
answered Dec 22, 2010 at 14:40

1 Comment

$needle should be escaped with preg_quote($needle, '/').
1
2

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.