90

I noticed a lot of developers are using both strstr and strpos to check for a substring existence. Is one of them preferred and why ?

Alix Axel
  • 151,645
  • 95
  • 393
  • 500
johnlemon
  • 20,761
  • 42
  • 119
  • 178

5 Answers5

131

From the PHP online manual:

If you only want to determine if a particular needle occurs within haystack, use the faster and less memory intensive function strpos() instead.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 13
    +1, You may use strpos or stripos. and don't forget to check the warnings on the php doc about using === FALSE; – fedmich Sep 23 '12 at 00:09
  • 8
    To elaborate on fedmich's comment: I always use `if(strpos($haystack,$needle) !== false) { // do something }`, never `if(strpos($haystack,$needle)) { // do bad things }`. `strpos` will return 0 if the `$needle` is at the very beginning of `$haystack`, and 0 is considered equal to false. `(0 == false)` evaluates to true. `(0 === false)` evaluates to false. – Buttle Butkus Apr 22 '14 at 00:38
  • 1
    People coming from C may think about using the *strchr* function, but in PHP it's actually an alias for *strstr*, so *strpos* is a better choice. – Déjà vu Jul 27 '14 at 10:58
43

Here are some other answers (+benchmarks) I got to my question, which is almost the same (I didn't realize yours when asking).


In the meantime I also made my own benchmark test, which I ran 1000000 times for each relevant functions (strstr(), strpos(), stristr() and stripos()).
Here's the code:

<?php

function getmicrotime() {
    list($usec, $sec) = explode(" ", microtime());
    return ((float) $usec + (float) $sec);
}

$mystring = 'blahblahblah';  
$findme = 'bla';  

echo 'strstr & strpos TEST:<pre>';
$time_start = getmicrotime();
for($i=0; $i<1000000; $i++) strstr($mystring, $findme);
$time_needed_strstr = getmicrotime() - $time_start;
echo 'strstr():            ',
    round( $time_needed_strstr , 8 ). PHP_EOL;

$time_start = getmicrotime();
for($i=0; $i<1000000; $i++) stristr($mystring, $findme);
$time_needed_stristr = getmicrotime() - $time_start;
echo 'stristr():           ',
    round( $time_needed_stristr , 8 ) . PHP_EOL;

$time_start = getmicrotime();
for($i=0; $i<1000000; $i++) strpos($mystring, $findme) !== false;
$time_needed_strpos = getmicrotime() - $time_start;
echo 'strpos() !== false:  ',
    round( $time_needed_strpos , 8 ) . PHP_EOL;

$time_start = getmicrotime();
for($i=0; $i<1000000; $i++) stripos($mystring, $findme) !== false;
$time_needed_stripos = getmicrotime() - $time_start;
echo 'stripos() !== false: ',
    round( $time_needed_stripos , 8 ) . PHP_EOL;

echo PHP_EOL;

echo 'time_needed_stristr - time_needed_strstr: ',
     round( $time_needed_stristr - $time_needed_strstr , 8) . PHP_EOL;
echo 'time_needed_stripos - time_needed_strpos: ',
     round( $time_needed_stripos - $time_needed_strpos , 8) . PHP_EOL;

echo PHP_EOL;

echo 'time_needed_strstr  - time_needed_strpos:  ',
     round( $time_needed_strstr - $time_needed_strpos , 8) . PHP_EOL;
echo 'time_needed_stristr - time_needed_stripos: ',
     round( $time_needed_stristr - $time_needed_stripos , 8) . PHP_EOL;

echo '</pre>';

?>

And here is the first output, which shows that strpos() is the winner:

strstr & strpos TEST:
strstr():            2.39144707
stristr():           3.65685797
strpos() !== false:  2.39055395
stripos() !== false: 3.54681897

time_needed_stristr - time_needed_strstr: 1.2654109
time_needed_stripos - time_needed_strpos: 1.15626502

time_needed_strstr  - time_needed_strpos:  0.00089312
time_needed_stristr - time_needed_stripos: 0.110039 

The next one is similar to the first output (strpos() is the winner again):

strstr & strpos TEST:
strstr():            2.39969015
stristr():           3.60772395
strpos() !== false:  2.38610101
stripos() !== false: 3.34951186

time_needed_stristr - time_needed_strstr: 1.2080338
time_needed_stripos - time_needed_strpos: 0.96341085

time_needed_strstr  - time_needed_strpos:  0.01358914
time_needed_stristr - time_needed_stripos: 0.25821209

Below is another one, which is more interesting, because in this case, strstr() is the winner:

strstr & strpos TEST:
strstr():            2.35499191
stristr():           3.60589004
strpos() !== false:  2.37646604
stripos() !== false: 3.51773095

time_needed_stristr - time_needed_strstr: 1.25089812
time_needed_stripos - time_needed_strpos: 1.14126492

time_needed_strstr  - time_needed_strpos:  -0.02147412
time_needed_stristr - time_needed_stripos: 0.08815908

This means it can really depend on "environmental circumstances", which are sometimes hard to influence, and can change the result of "micro optimization tasks" like this, in case you are just checking whether a string exists in another one or not.

BUT I think in most cases, strpos() is the winner in comparison to strstr().

I hope this test was useful for someone.

Community
  • 1
  • 1
Sk8erPeter
  • 6,899
  • 9
  • 48
  • 67
  • 3
    While this benchmark is usefull it does not measure memory consumption, also does not take into account long strings, like kbytes or mbytes. –  Sep 12 '13 at 09:20
  • Huh? @user133408 Long strings and larger byte strings would even take longer. – NiCk Newman Aug 16 '15 at 05:03
8

Many developers use strpos for micro optimization purposes.

Using strstr also only works if the resulting string cannot be interpreted as false in boolean context.

Overcome by events: PHP8 introduced str_contains, the "right tool for the job" (with shims available for older setups). Which does exactly what everyone has been glamoring for, but without the interpreter-level comparison and syntactic overhead.

mario
  • 144,265
  • 20
  • 237
  • 291
  • 14
    It is not micro optimisation, it's called _using the right function for the job_. If I want the string's position, I call `strpos()`. If I wanted the substring after that position, I call `strstr()`. – Alnitak Apr 28 '11 at 15:06
  • 1
    @Alnitak: What I was saying. If you want to check for the presence of a string, then there's a function for that. If you actually do need the position, then there's another. -- When you probe for the position without actually needing the position, then that's hardly "using the right function for the job". The intention is clearly to optimize micro seconds away. (Isn't that what you cited?) – mario Apr 28 '11 at 15:36
  • 1
    @mario but there is no function whose _only_ purpose is checking whether a substring exists. The _position_ of the substring (if found) is free information once you've actually found it. OTOH, `strstr` does _more_ than is required, which is why it's slower. – Alnitak Apr 28 '11 at 15:41
  • @Alnitak: Mind you, not news. You seem very adamant about pointing out the performance difference, and only that. That's a tell tale sign of micro optimization. **It doesn't make a blip in the profiler**. Where it does make a difference is in code readability. – mario Apr 28 '11 at 15:44
  • @mario actually I would care only very slightly about the performance. I do care very much about using the right function for the job ;-) – Alnitak Apr 28 '11 at 15:47
  • You mean the function that returns the position that you do not need? – mario Apr 28 '11 at 15:49
  • @mario like I said, that's free information - you cannot prove existence without incidentally calculating position. There _isn't_ a PHP function that _only_ tests for existence. – Alnitak Apr 28 '11 at 15:50
  • Oh, there is one. But you will get even more upset when I mention `ereg` or `preg_match` - because they are *measurably* slower. (Notice the "measurably". You have been complaining only because I pointed out that using strpos is a "micro optimization".) – mario Apr 28 '11 at 15:55
  • now you're being silly. `preg_match's` job isn't searching for a string within a string, it's matching a string against a regexp. – Alnitak Apr 28 '11 at 16:38
  • It's not forbidden to use a fixed string as regex. I have no idea about the system implementation of `ereg("test",$str)`, but I doubt the search algorithm itself could be slower than PHPs naive binary string search. (To bring the discussion back to your primary concern.) – mario Apr 28 '11 at 16:48
  • @mario: And let us all use `ereg`, iei! Really, regular expressions are my most favorite tool PHP offers, but a) `ereg` is deprecated and b) Using a regex to match against a string is 1. overkill and 2. often involves much more code, because you need to deal with escaping and 3. you may easily forget 2. I don't really see your point why `strstr` should be "better" than `strpos`. `strpos` is clearly more appropriate for the job, already for the reason you mentioned. – NikiC Apr 28 '11 at 23:12
  • @nikic: `ereg` was the shorter example over `preg_match` regarding a "PHP function that *only* tests for existence". It's not useful for the case at hand. Obviously. -- Nevertheless, the OP wasn't about the "fastest" function, but about the "preferred". `strpos` falls flat without the supporting !== strong boolean check. `strstr` is more convenient in other settings and when you can overlook the minuscle speed difference. (e.g. as simpler callback in an array_map construct) – mario Apr 28 '11 at 23:17
  • @mario: Really, I don't want to argue about that. It's probably a quite philosophical question. I use `strpos` because I regard it the "right function for the job" as you called it. Reasoning is simple: If a string is a substring of another string, it must occur at some position in this string. And this is what `strpos` tells you. You will notice that in many other languages you find substrings similarly. One example that comes to mind is JavaScripts `indexOf`. – NikiC Apr 28 '11 at 23:25
  • @mario: Damned, I said I don't want to argue and now I started ... :( – NikiC Apr 28 '11 at 23:26
  • @nikic: Actually I also just use `strpos`. Here I'm just trying to pretend I knew better and used the more readable syntax most of the time. -- The language comparison is an interesting point. Python for example has `.find` and `.index`. And the latter has no ambigious results, but gives an exception. (which otoh is a bit overkill) – mario Apr 28 '11 at 23:33
  • `(bool) strstr('Hell0 w0rld', $search);` will return `false` with `$search = ' '` or `$search = '0'` and this is already not was is expected. – xpoback Jun 01 '22 at 09:12
0

strpos() detects where in the haystack a particular needle lies. stristr() tests whether the needle is anywhere in the haystack

therefor strpos() is faster and less memory consuming

a reason for strstr(): if your needle is at the beginning of a string, strpos returns 0 (so have to check it with === false)

Flask
  • 4,966
  • 1
  • 20
  • 39
  • 6
    that's a completely bogus explanation - `strstr()` returns everything before or after the needle, so it first has to do the equivalent of `strpos()` _and then create that substring_. That's where the performance hit is. – Alnitak Apr 28 '11 at 15:03
-2

I prefer strstr() for readability and easy coding.. strpos() !==false is confusing a bit..

T.Todua
  • 53,146
  • 19
  • 236
  • 237