in_array vs strpos for performance in php
Asked Answered
C

4

8

I am logging in users via windows authentication and then storing that user's rights in a session variable. I use a delimited method of rights storage in a database i.e:

$rights //retrieved from database 
= 'read,edit,delete,admin'

so my question is should I;

//generate variable
$_SESSION['userrights'] = $rights ($rights is retrieved from database)

//use strpos to find if right is allowed
if (strpos($_SESSION['userrights'],"admin") !== false) { // do the function }

OR

//make array of rights
$_SESSION['userrights'] = explode(',',$rights)

//use in_array to find if right is allowed
if (in_array("admin",$_SESSION['userrights'])) { // do the function }

Bit of a OCD question as I presume the difference will be pretty much negligible for what I am doing but which would be the faster (use less resources) method?

Any answers appreciated except ones that insult my method of rights storage!

Cowling answered 12/1, 2014 at 2:45 Comment(7)
Use the second method, for sure. Checking for a specific value in an array is a much better method compared to looking for a value inside a string. Note that in_array argument order is needle,haystack, not haystack,needle as you have there.Sherwoodsherwynd
2:50AM lazy copy and pasting! appreciate the answer!Cowling
Why don't you store them as the keys in an associative array? That should be faster than either.Discernible
You mean so I could use isset($_SESSION['admin']) instead?Cowling
Yeah, or isset($_SESSION['userrights']['admin'])Discernible
That's a pretty brilliant solution! explode to $arr, and then foreach($arr as $str) { $_SESSION['userrights'][$str] = 1 } I presume would be the best method?Cowling
@Cowling I like this idea of permissions. Nice comparable with bitwise operators.Natation
B
7

strpos is the fastest way to search a text needle, per the php.net documentation for strstr():

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

Brainstorm answered 12/1, 2014 at 2:58 Comment(0)
N
18

As I often work with large datasets, I'd go with isset or !empty on an associative array and check for the key, like @Barmar suggests. Here is a quick 1M benchmark on an Intel® Core™ i3-540 (3.06 GHz)

$test = array("read", "edit", "delete", "admin");

echo "<pre>";

// --- strpos($rights,$test[$i%4]) ---

$rights = 'read,edit,delete,admin';
$mctime = microtime(true);
for($i=0; $i<=1000000; $i++) { if (strpos($rights,$test[$i%4]) !== false) { }}
echo '  strpos(... '.round(microtime(true)-$mctime,3)."s\n";

// --- in_array($test[$i%4],$rights) ---

$rights = array("read", "edit", "delete", "admin");
$mctime = microtime(true);
for($i=0; $i<=1000000; $i++) { if (in_array($test[$i%4],$rights)) { }}
echo 'in_array(... '.round(microtime(true)-$mctime,3)."s\n";

// --- !empty($rights[$test[$i%4]]) ---

$rights = array('read' => 1, 'edit' => 1, 'delete' => 1, 'admin' => 1);
$mctime = microtime(true);
for($i=0; $i<=1000000; $i++) { if (!empty($rights[$test[$i%4]])) { }}
echo '  !empty(... '.round(microtime(true)-$mctime,3)."s\n";

// --- isset($rights[$test[$i%4]]) ---

$rights = array('read' => 1, 'edit' => 1, 'delete' => 1, 'admin' => 1);
$mctime = microtime(true);
for($i=0; $i<=1000000; $i++) { if (isset($rights[$test[$i%4]])) { }}
echo '   isset(... '.round(microtime(true)-$mctime,3)."s\n\n";

echo "</pre>";

The winner is isset:

  strpos(... 0.393s
in_array(... 0.519s
  !empty(... 0.232s
   isset(... 0.209s
Natation answered 12/1, 2014 at 14:26 Comment(5)
The performance characteristics come from using an associative array instead of a list. Although your benchmark is slightly flawed, you're not including any balance, and only testing worst case scenario of the strpos and in_array (last entry in the array/string). To make it fairer, you should use a modulo 4 on $i and fetch that from an array. However, associative arrays scale better and false results is also faster with them.Ahearn
@Ahearn Thanks for your comment! Modified it a bit.Natation
@Jonny5 Thank you for going to this amount of effort to appease my ocd! I am using isset due to yours and Barmar 's answers.. much appreciatedCowling
I might be missing something but does this test take into account the time it would take to convert from the csv in the database string to the array keys? or is that considered negligible?Sclerenchyma
I'd like to know the answer to what Mike has asked above also! If you have a text blob, would it be faster to explode it into an array and then use isset() over just using strpos() straight up on the text?Bathsheeb
B
7

strpos is the fastest way to search a text needle, per the php.net documentation for strstr():

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

Brainstorm answered 12/1, 2014 at 2:58 Comment(0)
B
1

A benchmark that proofs that strpos is not the fastest method, but faster than in_array:

<?php

echo phpversion() . PHP_EOL;

// build random array
$array = array_fill(0, 10000, 16);
$array = array_map('openssl_random_pseudo_bytes', $array);
$array = array_map('bin2hex', $array);
$array_flipped = array_flip($array);
$string = implode($array);
$random_keys = array_rand($array_flipped, 10);

$loops = 10000;

$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
    strpos($string, $random_keys[ rand(0, 9) ]);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
    in_array($random_keys[ rand(0, 9) ], $array);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
    isset($array_flipped[ $random_keys[ rand(0, 9) ] ]);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < $loops; $i++) {
    $array_flipped = array_flip($array);
    isset($array_flipped[ $random_keys[ rand(0, 9) ] ]);
}
echo __LINE__ . ': ' . round(microtime(true) - $start, 5) . PHP_EOL;

?>

Result:

5.6.31
19: 1.11484
25: 1.3109
31: 0.00237
38: 13.64204

As you can see it is important not to flip an array on-the-fly to benefit from isset.

Befog answered 15/10, 2017 at 18:8 Comment(0)
R
-1

Have you considered the possibility of doing a query on the database if $_SESSION['userrights'] exists in the database? You're already doing a query to get the list of rights in your example, why not do a query for a specific $_SESSION['userrights'] and check if there are any rows returned?

Rightness answered 12/1, 2014 at 3:9 Comment(3)
I was under the impression that querying would be far more intensive that either strpos or in_array.. I already query the database and store the value in a session var so I have the values to hand. Unless I mis-understand, you are suggesting I query every time I want to check if user has a certain level of rights? (I have multiple things displaying based on whether user has one right or another)Cowling
How many times are you doing a user rights look-up on a page?Rightness
The application isn't finished yet, but I'm guessing the maximum right now would be 2/3Cowling

© 2022 - 2024 — McMap. All rights reserved.