老实说,除非您的 CIDR 范围非常大,并且您在同一进程中检查大量 IP,否则您可能不会看到性能提升的多少。但是,如果这是您正在查看的场景,那么您可以考虑通过预处理您的范围和 IP(执行一次 ip2long() 调用并存储分离的掩码/子网以进行比较来尝试压缩一些性能)。
例如,这就是你今天的做法,我假设:
<?php
// Original style
$ranges = array(
"192.168.0.1/32",
"192.168.0.1/26",
"192.168.0.1/24",
"192.168.0.1/16",
"127.0.0.1/24",
"10.0.0.1/32",
"10.0.0.1/24"
);
// Run the check
$start = microtime(true);
find_cidr("10.0.0.42", $ranges);
find_cidr("192.168.0.12", $ranges);
find_cidr("10.0.0.1", $ranges);
$end = microtime(true);
echo "Ran 3 find routines in " . ($end - $start) . " seconds!\n";
function find_cidr($ip, $ranges)
{
foreach($ranges as $range)
{
if(cidr_match($ip, $range))
{
echo "IP {$ip} found in range {$range}!\n";
break;
}
}
}
function cidr_match($ip, $range){
list ($subnet, $bits) = explode('/', $range);
$ip = ip2long($ip);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
$subnet &= $mask; // in case the supplied subnet was not correctly aligned
return ($ip & $mask) == $subnet;
}
在我的机器上,运行时间大约为 0.0005 - 0.001 秒(针对少数范围检查 3 个 IP)。
如果我写一些东西来预处理范围:
<?php
// Slightly-optimized style
$ranges = array(
"192.168.0.1/32",
"192.168.0.1/26",
"192.168.0.1/24",
"192.168.0.1/16",
"127.0.0.1/24",
"10.0.0.1/32",
"10.0.0.1/24"
);
$matcher = new BulkCIDRMatch($ranges);
$start = microtime(true);
$matcher->FindCIDR("10.0.0.42");
$matcher->FindCIDR("192.168.0.12");
$matcher->FindCIDR("10.0.0.1");
$end = microtime(true);
echo "Ran 3 find routines in " . ($end - $start) . " seconds!\n";
class BulkCIDRMatch
{
private $_preparedRanges = array();
public function __construct($ranges)
{
foreach($ranges as $range)
{
list ($subnet, $bits) = explode('/', $range);
$subnet = ip2long($subnet);
$mask = -1 << (32 - $bits);
$subnet &= $mask; // in case the supplied subnet was not correctly aligned
$this->_preparedRanges[$range] = array($mask,$subnet);
}
}
public function FindCIDR($ip)
{
$result = $this->_FindCIDR(ip2long($ip));
if($result !== null)
{
echo "IP {$ip} found in range {$result}!\n";
}
return $result;
}
private function _FindCIDR($iplong)
{
foreach($this->_preparedRanges as $range => $details)
{
if(($iplong & $details[0]) == $details[1])
{
return $range;
}
}
// No match
return null;
}
}
...然后我看到更快的 CHECKING 但是在初始化类并处理和存储所有范围时,开始时的开销会稍多一些。因此,如果我对少数范围仅使用 3 个 IP 对 OVERALL 运行进行计时,那么“优化”方式实际上会慢一些。但是,如果我针对 10,000 个 CIDR 运行 1,000 个 IP,则“优化”方式将比原始方式有更显着的改进(以额外的内存使用为代价来存储预处理的范围数据)。
所以这完全取决于音量和你想要做什么。
也就是说,如果您担心 0.001 秒的性能太慢,那么 PHP 可能不适合用于您的检查。或者至少您可能想考虑编写一个自定义扩展,以便更多的处理在 C 中完成。
编辑:要回答有关查找要检查的“可能”范围的原始问题(在对其字符串形式进行任何类型的转换之前),尝试这可能不是一件非常可靠的事情。范围可以跨越它们的初始八位位组,因此如果您开始比较这些值(例如“我正在查看 192.168.1.0,所以我只会查看从 192 开始的范围”),您不仅会招致每个条目的字符串比较的性能开销(这会减慢整体查找速度),但您可能会错过有效范围。