36

For single value check, which of both is preferred and why?

$string == 'The quick brown fox jumps over the lazy dog';

if(strpos($string, 'fox') !== false){
    // do the routine
}

# versus

if(preg_match('/fox/i', $string)){
    // do the routine
}
tomsseisums
  • 13,168
  • 19
  • 83
  • 145

6 Answers6

72

I would prefer the strpos over preg_match, because regexes are generally more expensive to execute.

According to the official php docs for preg_match:

Do not use preg_match() if you only want to check if one string is contained in another string. Use strpos() or strstr() instead as they will be faster.

  • 4
    Woah, there's a huge tip up in `preg_match();` documentation, but as usual, I did not notice it. Well, thanks for copy-pasting it here. And I guess that's my question answered by PHP crew themselves. – tomsseisums Jun 22 '11 at 08:10
13

When in doubt, benchmark!

Obviously we could have come up with a better benchmark than this, but just to prove the point that as it starts to scale up, strpos() is going to be quite a bit faster. (almost 2x as fast here)

EDIT I later noticed that the regex was case-insensitive. When running this again using stripos() for a more fair comparison, the result is 11 to 15, so the gap narrows but preg_match() remains a lot slower.

$str = "the quick brown fox";
$start1 = time();
for ($i = 0; $i<10000000; $i++)
{
    if (strpos($str, 'fox') !== false)
    {
        //
    }
}
$end1 = time();
echo $end1 - $start1 . "\n";

$start2 = time();
for ($i = 0; $i<10000000; $i++)
{
    if (preg_match('/fox/i', $str))
    {
        //
    }
}
$end2 = time();
echo $end2 - $start2;

// Results:
strpos() = 8sec
preg_match() = 15sec

// Results both case-insensitive (stripos()):
stripos() = 11sec
preg_match() = 15sec
Michael Berkowski
  • 267,341
  • 46
  • 444
  • 390
  • 1
    Performance question was actually optional, was more interested in usage. But thanks! – tomsseisums Jun 22 '11 at 08:06
  • 3
    Benchmarking like this is usually pointless, because you will never run that function `10000000` times, not even `100` times. And sometimes `function a` is faster then `function b` with small number of calls and viceversa. Not to mention that relying on `time` or `microtime` is bad, it uses the internal clock which might be variable. Instead, I would use [xdebug](http://xdebug.org/). – machineaddict Jul 15 '14 at 08:14
  • Then again a simple: if('fox' == $string)... is even faster. By my tests this tested (using the code above) at 3 seconds as opposed to the str_pos at 11 seconds and preg_match at 17 seconds. – Richard - Rogue Wave Limited Dec 18 '14 at 17:23
8

Never use regular expressions unless absolutely necessary. The overhead involved in starting up and deploying the regex engine on a string like this is similar to using a jackhammer instead of a regular hammer, a drill instead of a screwdriver.

You also have a greater margin of error with regex – mismatched strings, unexpected results, etc. Stick with strpos unless strpos isn't flexible enough.

glortho
  • 13,120
  • 8
  • 49
  • 45
  • 1
    Would you happen to have any numbers to backup and give context to those statements about the regex engine? – rodrigo-silveira May 26 '15 at 11:02
  • 3
    I haven't used PHP since around the time I posted this answer, but I was big into performance tweaking at the time and came to this conclusion from Xdebug profiles and from observing many poorly written expressions that were needlessly intensive. If I were to rewrite this answer now, however, I wouldn't be quite so extreme about it. Cupcake's answer and quoting of PHP documentation is a more appropriate tone, I think. – glortho May 26 '15 at 14:39
5

If you're already using preg_match and preg_replace all over the place in your code, then go on and use it once more. Why?

  1. Performance. Most of the overhead those function add is in the initial load time of the engine, if you already paid that price, make it worth it.

  2. Readability. strpos(...)!==false, while faster, is an incredibile eyesore.

    It is one of the ugliest php constructs.
    The usage of == and false in it are really kludgy and look hard to parse and frail to edit.

Shame on the core team for not having defined an alias like strcontains() for it, years ago.
Now it's well too late to do that, but it would have been nice, back then.

ZJR
  • 9,308
  • 5
  • 31
  • 38
  • 1
    I don't see a reason to stop using a function because it looks ugly/unpleasant. – tomsseisums Jun 22 '11 at 10:20
  • 4
    Actually, using `!== false` makes perfect sense when you're familiar with the type juggling PHP does internally. – Artefact2 Jun 24 '11 at 08:53
  • 1
    @ZJR I agree that it would have been nice to have a higher-level construct such as a `strcontains()` function. However, I find ***both*** the `strpos(...) !== false` and `preg_match('...', $string)` constructs to be equally ugly. –  Nov 05 '12 at 17:28
  • I don't get your point about strpos returning false is "eyesore" . Contrarly it should return false rather than 0 . It should return false rather than trigger an error. The return values are practical for any use of the client code . – Geo C. Jan 13 '14 at 22:59
  • Like the performance suggestion, actually used to ===, !=== aka javascript; – yardpenalty.com Jun 30 '16 at 19:03
3

Good Code More Important

So, if you think this kind of thing is important, keep in mind that it's a constant in Big O. To put it another way, database calls, On2 or worse activities are the only things that matter. In most cases, it's futile to spend time worrying about these low-level commands.

Not to imply that constants should be ignored; for example, I refactored code that gathered images since it did so one at a time, each taking 1 second, and it decreased the duration from 12 seconds to 1 second (using multi curl request). The idea is that built-in commands are low-level, and the code structure is more crucial.

The code below makes 10 million lower level calls, and as you can see the "savings" are negligible.

function prof_flag($str)
{
    global $prof_timing, $prof_names;
    $prof_timing[] = microtime(true);
    $prof_names[] = $str;
}

function prof_print()
{
    global $prof_timing, $prof_names;
    $size = count($prof_timing);
    for($i=0;$i<$size - 1; $i++)
{
    echo "<b>{$prof_names[$i]}</b><br>";
        echo sprintf("&nbsp;&nbsp;&nbsp;%f<br>",     $prof_timing[$i+1]-$prof_timing[$i]);
    }
    echo "<b>{$prof_names[$size-1]}</b><br>";
}


$l = 10000000;
$str = "the quick brown fox";
echo "<h3>Ran " .number_format($l,2) ." calls per command </h3>";

prof_flag("Start: stripos");

for ($i = 0; $i<$l; $i++)
    if (stripos($str, 'fox') !== false) {}


prof_flag("Start: preg_match");

for ($i = 0; $i<$l; $i++)
    if (preg_match('#fox#i', $str) === 1) {}

prof_flag("Finished");
prof_print();

Only value to this code is that it shows a cool way to record times things take to run lol

Ran 10,000,000.00 calls per command

Start: stripos
   2.217225
Start: preg_match
   3.788667
Start: ==
   0.511315
Start: ucwords lol
   2.112984
Finished
Mike Q
  • 6,716
  • 5
  • 55
  • 62
0

You can optimize the above preg_match by writing :

preg_match('/(?>fox)/', $str)

this should be more faster.

Wouter J
  • 41,455
  • 15
  • 107
  • 112