php PHP如何检查一个IP地址是否在两个IP的范围内?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11121817/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check an IP address is within a range of two IPs in PHP?
提问by guitarlass
I have an IP address and I'm given two other IP addresses which together creates an IP range. I want to check if the first IP address is within this range. How can i find that out in PHP?
我有一个 IP 地址,并且我得到了另外两个 IP 地址,它们共同创建了一个 IP 范围。我想检查第一个 IP 地址是否在此范围内。我怎样才能在 PHP 中找到它?
回答by oezi
回答by John Conde
This website offers a great guide and codeto do this (which was the first result of a Google search for this question):
这个网站提供了一个很好的指南和代码来做到这一点(这是谷歌搜索这个问题的第一个结果):
<?php
/*
* ip_in_range.php - Function to determine if an IP is located in a
* specific range as specified via several alternative
* formats.
*
* Network ranges can be specified as:
* 1. Wildcard format: 1.2.3.*
* 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0
* 3. Start-End IP format: 1.2.3.0-1.2.3.255
*
* Return value BOOLEAN : ip_in_range($ip, $range);
*
* Copyright 2008: Paul Gregg <[email protected]>
* 10 January 2008
* Version: 1.2
*
* Source website: http://www.pgregg.com/projects/php/ip_in_range/
* Version 1.2
*
* This software is Donationware - if you feel you have benefited from
* the use of this tool then please consider a donation. The value of
* which is entirely left up to your discretion.
* http://www.pgregg.com/donate/
*
* Please do not remove this header, or source attibution from this file.
*/
// decbin32
// In order to simplify working with IP addresses (in binary) and their
// netmasks, it is easier to ensure that the binary strings are padded
// with zeros out to 32 characters - IP addresses are 32 bit numbers
Function decbin32 ($dec) {
return str_pad(decbin($dec), 32, '0', STR_PAD_LEFT);
}
// ip_in_range
// This function takes 2 arguments, an IP address and a "range" in several
// different formats.
// Network ranges can be specified as:
// 1. Wildcard format: 1.2.3.*
// 2. CIDR format: 1.2.3/24 OR 1.2.3.4/255.255.255.0
// 3. Start-End IP format: 1.2.3.0-1.2.3.255
// The function will return true if the supplied IP is within the range.
// Note little validation is done on the range inputs - it expects you to
// use one of the above 3 formats.
Function ip_in_range($ip, $range) {
if (strpos($range, '/') !== false) {
// $range is in IP/NETMASK format
list($range, $netmask) = explode('/', $range, 2);
if (strpos($netmask, '.') !== false) {
// $netmask is a 255.255.0.0 format
$netmask = str_replace('*', '0', $netmask);
$netmask_dec = ip2long($netmask);
return ( (ip2long($ip) & $netmask_dec) == (ip2long($range) & $netmask_dec) );
} else {
// $netmask is a CIDR size block
// fix the range argument
$x = explode('.', $range);
while(count($x)<4) $x[] = '0';
list($a,$b,$c,$d) = $x;
$range = sprintf("%u.%u.%u.%u", empty($a)?'0':$a, empty($b)?'0':$b,empty($c)?'0':$c,empty($d)?'0':$d);
$range_dec = ip2long($range);
$ip_dec = ip2long($ip);
# Strategy 1 - Create the netmask with 'netmask' 1s and then fill it to 32 with 0s
#$netmask_dec = bindec(str_pad('', $netmask, '1') . str_pad('', 32-$netmask, '0'));
# Strategy 2 - Use math to create it
$wildcard_dec = pow(2, (32-$netmask)) - 1;
$netmask_dec = ~ $wildcard_dec;
return (($ip_dec & $netmask_dec) == ($range_dec & $netmask_dec));
}
} else {
// range might be 255.255.*.* or 1.2.3.0-1.2.3.255
if (strpos($range, '*') !==false) { // a.b.*.* format
// Just convert to A-B format by setting * to 0 for A and 255 for B
$lower = str_replace('*', '0', $range);
$upper = str_replace('*', '255', $range);
$range = "$lower-$upper";
}
if (strpos($range, '-')!==false) { // A-B format
list($lower, $upper) = explode('-', $range, 2);
$lower_dec = (float)sprintf("%u",ip2long($lower));
$upper_dec = (float)sprintf("%u",ip2long($upper));
$ip_dec = (float)sprintf("%u",ip2long($ip));
return ( ($ip_dec>=$lower_dec) && ($ip_dec<=$upper_dec) );
}
echo 'Range argument is not in 1.2.3.4/24 or 1.2.3.4/255.255.255.0 format';
return false;
}
}
?>
回答by codefreak
I found this little gistwhich has simpler/shorter solution than already mentioned here.
我发现这个小要点比这里已经提到的解决方案更简单/更短。
Second argument (range) can either be a static ip such as 127.0.0.1 or a range like 127.0.0.0/24.
第二个参数(范围)可以是静态 ip,例如 127.0.0.1 或范围,例如 127.0.0.0/24。
/**
* Check if a given ip is in a network
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
* @return boolean true if the ip is in this range / false if not.
*/
function ip_in_range( $ip, $range ) {
if ( strpos( $range, '/' ) == false ) {
$range .= '/32';
}
// $range is in IP/CIDR format eg 127.0.0.1/24
list( $range, $netmask ) = explode( '/', $range, 2 );
$range_decimal = ip2long( $range );
$ip_decimal = ip2long( $ip );
$wildcard_decimal = pow( 2, ( 32 - $netmask ) ) - 1;
$netmask_decimal = ~ $wildcard_decimal;
return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
}
回答by Bas
if(version_compare($low_ip, $ip) + version_compare($ip, $high_ip) === -2) {
echo "in range";
}
回答by Sazzad Hissain Khan
Comparing in range (Including Ipv6 support)
范围比较(包括 IPv6 支持)
The following two functions were introduced in PHP 5.1.0, inet_ptonand inet_pton. Their purpose is to convert human readable IP addresses into their packed in_addrrepresentation. Since the result is not pure binary, we need to use the unpackfunction in order to apply bitwise operators.
以下两个函数是在 PHP 5.1.0 中引入的,inet_pton和inet_pton. 它们的目的是将人类可读的 IP 地址转换为它们的打包in_addr表示。由于结果不是纯二进制,我们需要使用该unpack函数来应用按位运算符。
Both functions support IPv6 as well as IPv4. The only difference is how you unpack the address from the results. With IPv6, you will unpack with contents with A16, and with IPv4, you will unpack with A4.
这两个功能都支持 IPv6 和 IPv4。唯一的区别是您如何从结果中解压地址。使用 IPv6,您将使用 A16 解压内容,使用 IPv4,您将使用 A4 解压内容。
To put the previous in a perspective here is a little sample output to help clarify:
将前面的内容放在一个角度来看,这里有一个小示例输出,以帮助澄清:
// Our Example IP's
$ip4= "10.22.99.129";
$ip6= "fe80:1:2:3:a:bad:1dea:dad";
// ip2long examples
var_dump( ip2long($ip4) ); // int(169239425)
var_dump( ip2long($ip6) ); // bool(false)
// inet_pton examples
var_dump( inet_pton( $ip4 ) ); // string(4)
var_dump( inet_pton( $ip6 ) ); // string(16)
We demonstrate above that the inet_* family supports both IPv6 and v4. Our next step will be to translate the packed result into an unpacked variable.
我们在上面演示了 inet_* 系列支持 IPv6 和 v4。我们的下一步是将打包的结果转换为未打包的变量。
// Unpacking and Packing
$_u4 = current( unpack( "A4", inet_pton( $ip4 ) ) );
var_dump( inet_ntop( pack( "A4", $_u4 ) ) ); // string(12) "10.22.99.129"
$_u6 = current( unpack( "A16", inet_pton( $ip6 ) ) );
var_dump( inet_ntop( pack( "A16", $_u6 ) ) ); //string(25) "fe80:1:2:3:a:bad:1dea:dad"
Note : The current function returns the first index of an array. It is equivelant to saying $array[0].
注意:当前函数返回数组的第一个索引。相当于说 $array[0]。
After the unpacking and packing, we can see we achieved the same result as input. This is a simple proof of concept to ensure we are not losing any data.
在解包和打包之后,我们可以看到我们获得了与输入相同的结果。这是一个简单的概念证明,以确保我们不会丢失任何数据。
Finally use,
最后使用,
if ($ip <= $high_ip && $low_ip <= $ip) {
echo "in range";
}
Reference: php.net
参考:php.net
回答by Mike Mackintosh
I would always suggest ip2long, but sometimes you need to check networks and etc. I've built in the past a IPv4 Networking class, which can be found here on HighOnPHP.
我总是建议ip2long,但有时你需要检查网络等。我过去建立了一个 IPv4 网络类,可以在HighOnPHP上找到。
The nice thing about working with IP addressing is it's flexibility especially when using BITWISE operators. AND'ing, OR'ing and BitShifting will work like a charm.
使用 IP 寻址的好处在于它的灵活性,尤其是在使用 BITWISE 运算符时。AND'ing、OR'ing 和 BitShifting 将像魅力一样工作。
回答by user8581607
Here is my approach of the subject.
这是我对这个主题的看法。
function validateIP($whitelist, $ip) {
// e.g ::1
if($whitelist == $ip) {
return true;
}
// split each part of the IP address and set it to an array
$validated1 = explode(".", $whitelist);
$validated2 = explode(".", $ip);
// check array index to avoid undefined index errors
if(count($validated1) >= 3 && count($validated2) == 4) {
// check that each value of the array is identical with our whitelisted IP,
// except from the last part which doesn't matter
if($validated1[0] == $validated2[0] && $validated1[1] == $validated2[1] && $validated1[2] == $validated2[2]) {
return true;
}
}
return false;
}
回答by Nikolai R Kristiansen
Use the excellent rlanvin/php-ipwhich supports IPv4 and IPv6 (via the GMP extension):
使用支持 IPv4 和 IPv6的优秀rlanvin/php-ip(通过GMP 扩展):
use PhpIP\IPBlock;
$block = IPBlock::create('10.0.0.0/24');
$block->contains('10.0.0.42'); // true
See their docsfor more examples.
有关更多示例,请参阅他们的文档。
回答by Vlado
Btw, in case you need to check multiple ranges at once you can add few rows to the code in order to pass array of ranges. The second argument can be an array or string:
顺便说一句,如果您需要一次检查多个范围,您可以在代码中添加几行以传递范围数组。第二个参数可以是数组或字符串:
public static function ip_in_range($ip, $range) {
if (is_array($range)) {
foreach ($range as $r) {
return self::ip_in_range($ip, $r);
}
} else {
if ($ip === $range) { // in case you have passed a static IP, not a range
return TRUE;
}
}
// The rest of the code follows here..
// .........
}

