在PHP中,为了提高性能,应该使用in_array还是strpos?

8
我通过Windows身份验证登录用户,然后将该用户的权限存储在会话变量中。我使用数据库中的分隔符方法进行权限存储,例如:
$rights //retrieved from database 
= 'read,edit,delete,admin'

所以我的问题是,我应该怎么做?
//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 }

或者

//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 }

这是一个有点强迫症的问题,因为我认为对于我的工作来说差异几乎可以忽略不计,但哪种方法会更快(使用更少的资源)?

欢迎任何回答,除了侮辱我的权利存储方法的回答!


3
毫无疑问,使用第二种方法。在数组中检查特定值比在字符串中查找值要好得多。请注意,'in_array'函数的参数顺序应该是“待查找的值,目标数组”,而不是你所写的那样,“目标数组,待查找的值”。 - jszobody
凌晨2点50分,懒得打字只好复制粘贴!感谢您的回答! - DJB
2
为什么不将它们存储为关联数组中的键?这应该比其他方法更快。 - Barmar
2
是的,或者 isset($_SESSION['userrights']['admin']) - Barmar
@DJB 我喜欢这个权限的想法:http://discdev.com/2010/07/16/using-binary-numbers-for-permissions/。与位运算符相关的漂亮比较:https://dev59.com/bWPVa4cB1Zd3GeqP7Jvq#10504868。 - Jonny 5
显示剩余2条评论
4个回答

17

由于我经常使用大型数据集,我会选择在关联数组上使用isset!empty并检查键,就像@Barmar建议的那样。以下是在Intel® Core™ i3-540(3.06 GHz)上进行的快速1M基准测试。

$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>";

获胜者是 isset:

  strpos(... 0.393s
in_array(... 0.519s
  !empty(... 0.232s
   isset(... 0.209s

1
性能特点源于使用关联数组而不是列表。尽管你的基准测试略有缺陷,因为你没有包括任何平衡,并且只测试了strposin_array(数组/字符串中的最后一个条目)的最坏情况。为使其更公平,您应在 $i 上使用模 4 并从数组中获取它。然而,关联数组具有更好的可伸缩性,而且误差也更小。 - Aidiakapi
@Aidiakapi 感谢你的评论!对它进行了一些修改。 - Jonny 5
2
@Jonny5 感谢您付出这么多的努力来满足我的强迫症!我正在使用isset,感谢您和Barmar的回答..非常感激。 - DJB
1
我可能会错过什么,但是这个测试是否考虑了从数据库字符串转换为数组键所需的时间?还是认为这是微不足道的? - Mike
我也想知道Mike上面提出的问题的答案!如果你有一个文本块,将其分解为数组然后使用isset()是否比直接在文本上使用strpos()更快? - Andrew Schultz

7

strpos是搜索文本中的关键词的最快方法,根据php.net对strstr()的文档所述:

如果您只想确定特定的关键词是否出现在文本中,请使用更快且占用更少内存的函数strpos()1


1

一个基准测试证明了strpos不是最快的方法,但比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;

?>

结果:

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

正如您所看到的,不要即时翻转数组以从 isset 中受益,这是非常重要的。

-1

你有没有考虑过在数据库中查询$_SESSION['userrights']是否存在?你已经在示例中执行了一个获取权限列表的查询,为什么不针对特定的$_SESSION['userrights']执行一个查询,并检查是否返回任何行?


我原本以为查询会比strpos或in_array更加密集。我已经查询了数据库并将值存储在会话变量中,因此我手头有这些值。除非我误解了,您是在建议我每次想要检查用户是否具有某个权限时都进行查询吗?(我有多个基于用户是否具有一种或另一种权限而显示的内容) - DJB
你在页面上进行用户权限查找的次数有多少次? - Hayden
应用程序还没有完成,但我猜最多只有2/3。 - DJB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接