PHP Conference Japan 2024

ip2long

(PHP 4, PHP 5, PHP 7, PHP 8)

ip2long將包含(IPv4)網際網路協定點分位址的字串轉換成長整數

描述

ip2long(string $ip): int|false

函數 ip2long() 從網際網路標準格式(點分字串)表示法產生 IPv4 網際網路網路位址的長整數表示法。

ip2long() 也適用於不完整的 IP 位址。 請閱讀 » http://ps-2.kev009.com/wisclibrary/aix52/usr/share/man/info/en_US/a_doc_lib/libs/commtrf2/inet_addr.htm 以取得更多資訊。

參數

ip

標準格式位址。

回傳值

如果 ip 無效,則返回長整數或 false

範例

範例 #1 ip2long() 範例

<?php
$ip
= gethostbyname('www.example.com');
$out = "以下 URL 等效:<br />\n";
$out .= 'http://www.example.com/, http://' . $ip . '/, 和 http://' . sprintf("%u", ip2long($ip)) . "/<br />\n";
echo
$out;
?>

範例 #2 顯示 IP 位址

第二個範例示範如何使用 printf() 函數列印轉換後的位址

<?php
$ip
= gethostbyname('www.example.com');
$long = ip2long($ip);

if (
$long == -1 || $long === FALSE) {
echo
'無效的 IP,請重試';
} else {
echo
$ip . "\n"; // 192.0.34.166
echo $long . "\n"; // 32 位元系統上的 3221234342(由於整數溢位而為 -1073732954)
printf("%u\n", ip2long($ip)); // 3221234342
}
?>

註釋

注意:

由於 PHP 的 int 類型為帶符號,而且許多 IP 位址在 32 位元架構上會產生負整數,因此您需要使用 sprintf()printf() 的「%u」格式化程式來取得無符號 IP 位址的字串表示法。

注意:

在 32 位元系統上,由於整數值溢位,ip2long() 將針對 IP 255.255.255.255 回傳 -1

參見

  • long2ip() - 將長整數位址轉換為(IPv4)網際網路標準點分格式的字串
  • sprintf() - 返回格式化的字串

新增筆記

使用者提供的註釋 25 筆註釋

joe at joeceresini dot com
15 年前
將網路遮罩(例如:255.255.255.240)轉換為 CIDR 遮罩(例如:/28)的快速方法

<?php
function mask2cidr($mask){
$long = ip2long($mask);
$base = ip2long('255.255.255.255');
return
32-log(($long ^ $base)+1,2);

/* xor 運算會產生反向遮罩,
以 2 為底的對數(該值 + 1)將傳回遮罩中關閉的位元數,並且從 32 減去將產生 CIDR 表示法 */

}
?>
Mike B
7 年前
<?php
/*
假設有一個 IP 位址和子網路遮罩,
透過使用位元運算子來判斷子網路位址、廣播位址和萬用字元遮罩

ref: https://php.dev.org.tw/manual/en/language.operators.bitwise.php
*/

$ip='10.10.10.7';
$mask='255.255.255.0';
$wcmask=long2ip( ~ip2long($mask) );
$subnet=long2ip( ip2long($ip) & ip2long($mask) );
$bcast=long2ip( ip2long($ip) | ip2long($wcmask) );
echo
"給定的位址是 $ip,遮罩是 $mask,\n" .
"子網路是 $subnet,廣播位址是 $bcast \n" .
"萬用字元遮罩是 $wcmask";

/*
給定的位址是 10.10.10.7,遮罩是 255.255.255.0,
子網路是 10.10.10.0,廣播位址是 10.10.10.255
萬用字元遮罩是 0.0.0.255
*/

?>
php dot net at kenman dot net
16 年前
給 nate 的建議,他認為在 MySQL 資料庫中沒有理由使用 IP 的無符號版本

我認為這會取決於您的應用程式,但就我個人而言,我發現將 IP 儲存為無符號是有用的,因為 MySQL 有 2 個原生函數 INET_ATON() 和 INET_NTOA(),它們的功能與 ip2long()/long2ip() 相同,_除了_它們會產生無符號的對應項。因此,如果您願意,您可以執行

-- IANA Class-B 保留/私有
SELECT * FROM `servers`
WHERE `ip` >= INET_ATON('192.168.0.0')
AND `ip` <= INET_ATON('192.168.255.255');

在我目前的應用程式中,我發現使用 MySQL 內建函數比 PHP 對應函數更容易。

如果您好奇 ATON 和 NTOA 的名稱

ATON = address to number,又名 ip2long
NTOA = number to address,又名 long2ip
ir on ir id is at gm ai ld ot co m
17 年前
請記住,將 IP 位址以整數形式儲存在資料庫中(而不是以十進制格式的 15 字元字串,或以十六進制格式的 8 字元字串),速度會快上數百倍。

以典型的 MySQL 資料庫在數千(或數百萬!)列上搜尋 IP 位址的情況為例;您不是對每個條目執行字串比較,就是執行整數方程式。如果您的索引建立正確,使用 INT 而不是 VARCHAR,您的查找速度實際上應該會快 100 倍。

另請注意,傳遞給資料庫時,整數不需要跳脫字元。:)
david dot schueler at tel-billig dot de
15 年前
要從廣播位址和網路遮罩中取得網路位址,只需對其執行 AND 運算即可

<?php
// 簡單範例
$bcast = ip2long("192.168.178.255");
$smask = ip2long("255.255.255.0");
$nmask = $bcast & $smask;
echo
long2ip($nmask); // 將會輸出 192.168.178.0
?>

透過這個範例,您可以檢查給定的主機是否在您自己的本機網路中(在 Linux 上)。

<?php
/**
* 檢查客戶端 IP 是否在我們的伺服器子網路中
*
* @param string $client_ip
* @param string $server_ip
* @return boolean
*/
function clientInSameSubnet($client_ip=false,$server_ip=false) {
if (!
$client_ip)
$client_ip = $_SERVER['REMOTE_ADDR'];
if (!
$server_ip)
$server_ip = $_SERVER['SERVER_ADDR'];
// 從 ifconfig 提取廣播位址和網路遮罩
if (!($p = popen("ifconfig","r"))) return false;
$out = "";
while(!
feof($p))
$out .= fread($p,1024);
fclose($p);
// 這是因為 php.net 的註解函數不允許長行。
$match = "/^.*".$server_ip;
$match .= ".*Bcast:(\d{1,3}\.\d{1,3}i\.\d{1,3}\.\d{1,3}).*";
$match .= "Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/im";
if (!
preg_match($match,$out,$regs))
return
false;
$bcast = ip2long($regs[1]);
$smask = ip2long($regs[2]);
$ipadr = ip2long($client_ip);
$nmask = $bcast & $smask;
return ((
$ipadr & $smask) == ($nmask & $smask));
}
?>
spinyn at gmail dot com
15 年前
我只想對 kaputt 在有效匹配主機到 IP 範圍的工作中所做的寶貴貢獻添加一個註解。如果 IP 的二進位表示不包含前導零,則該腳本可以正常運作。不幸的是,decbin() 的運作方式似乎會捨棄第一個 IP 四位元組的二進位表示中的前導零。如果您嘗試匹配清單中所有可能的候選者,這是一個嚴重的問題。在這些情況下,需要將前導零加回去,才能為 0-127(或二進位等值,0-01111111)之間的第一個四位元組中的值獲得精確的匹配。

我提出的解決此問題的方法是以下函數

<?php
function addLeadingZero($ip) {
if ((
$result = (32 - strlen($ip))) > 0)
return
str_repeat("0", $result).$ip;
}
?>
one tiger one at gee mail dot comm
17 年前
我寫了一個小函數來驗證網路遮罩(我們有一個表單,其中輸入給定伺服器的網路遮罩,我想要確保它是有效的)。希望這對您有所幫助。

<?php
// 網路遮罩驗證器 //
function checkNetmask($ip) {
if (!
ip2long($ip)) {
return
false;
} elseif(
strlen(decbin(ip2long($ip))) != 32 && ip2long($ip) != 0) {
return
false;
} elseif(
ereg('01',decbin(ip2long($ip))) || !ereg('0',decbin(ip2long($ip)))) {
return
false;
} else {
return
true;
}
}
?>
Karl Rixon
10 年前
手冊指出「ip2long() 也適用於不完整的 IP 位址」,但是這取決於系統,因此不能依賴它。例如,在我的系統上,ip2long() 將為不完整的位址傳回 FALSE。

<?php
var_dump
(ip2long("255.255")); // bool(false)
?>

這是因為如果可用,ip2long 將使用 inet_pton,這不支援不完整的位址。如果您的系統上沒有 inet_pton,則將使用 inet_addr,並且不完整的位址將如所述的那樣運作。
jpmarcotte at gmail dot com
16 年前
在使用下方 jbothe 的程式碼和某些「$mask = 0xFFFFFFFF << (32 - $bits)」類型的程式碼的組合時,我在 64 位元機器上遇到了一些後續計算的錯誤。

請記住,當您分析要視為 32 位元寬的數字(例如 IP 位址)時,您可能需要截斷它們。在不依賴其他程式庫的情況下,只要在任何可能在 64 位元機器上以不同結果結尾的計算後面加上「& 0xFFFFFFFF」,就很簡單。

雖然在許多情況下,似乎直接使用「~0 << ...」來進行初始移位以建立網路遮罩,而不是「0xFFFFFFFF << ...」可能會更簡單。我不知道這是否能保證進一步的運算會如預期般運作。
Aleksey Kuznetsov
12 年前
不幸的是,sprintf('%u', ...) 的效能很低,並且會傳回整數值的字串表示,而不是整數。
以下是我用來將 IP 轉換為與 mySQL 相容的帶正負號整數的函數

function ip2int($ip) {
if (!$r = ip2long($ip)) return 0; // 我們想要 0 而不是 false,即使在 IP 不正確的情況下
if ($r > 2147483647)
$r-= 4294967296;
return $r; // ok
}
replay111 at tlen dot pl
12 年前
嗨,
根據上面的範例,我混合了 IPFilter 類別和 netMatch 函數,這為我提供了包括 CIDR IP 格式的 IP4 檢查完整類別。

<?php
class IP4Filter {

private static
$_IP_TYPE_SINGLE = 'single';
private static
$_IP_TYPE_WILDCARD = 'wildcard';
private static
$_IP_TYPE_MASK = 'mask';
private static
$_IP_TYPE_CIDR = 'CIDR';
private static
$_IP_TYPE_SECTION = 'section';
private
$_allowed_ips = array();

public function
__construct($allowed_ips) {
$this->_allowed_ips = $allowed_ips;
}

public function
check($ip, $allowed_ips = null) {
$allowed_ips = $allowed_ips ? $allowed_ips : $this->_allowed_ips;

foreach (
$allowed_ips as $allowed_ip) {
$type = $this->_judge_ip_type($allowed_ip);
$sub_rst = call_user_func(array($this, '_sub_checker_' . $type), $allowed_ip, $ip);

if (
$sub_rst) {
return
true;
}
}

return
false;
}

private function
_judge_ip_type($ip) {
if (
strpos($ip, '*')) {
return
self :: $_IP_TYPE_WILDCARD;
}

if (
strpos($ip, '/')) {
$tmp = explode('/', $ip);
if (
strpos($tmp[1], '.')) {
return
self :: $_IP_TYPE_MASK;
} else {
return
self :: $_IP_TYPE_CIDR;
}
}

if (
strpos($ip, '-')) {
return
self :: $_IP_TYPE_SECTION;
}

if (
ip2long($ip)) {
return
self :: $_IP_TYPE_SINGLE;
}

return
false;
}

private function
_sub_checker_single($allowed_ip, $ip) {
return (
ip2long($allowed_ip) == ip2long($ip));
}

private function
_sub_checker_wildcard($allowed_ip, $ip) {
$allowed_ip_arr = explode('.', $allowed_ip);
$ip_arr = explode('.', $ip);
for (
$i = 0; $i < count($allowed_ip_arr); $i++) {
if (
$allowed_ip_arr[$i] == '*') {
return
true;
} else {
if (
false == ($allowed_ip_arr[$i] == $ip_arr[$i])) {
return
false;
}
}
}
}

private function
_sub_checker_mask($allowed_ip, $ip) {
list(
$allowed_ip_ip, $allowed_ip_mask) = explode('/', $allowed_ip);
$begin = (ip2long($allowed_ip_ip) & ip2long($allowed_ip_mask)) + 1;
$end = (ip2long($allowed_ip_ip) | (~ ip2long($allowed_ip_mask))) + 1;
$ip = ip2long($ip);
return (
$ip >= $begin && $ip <= $end);
}

private function
_sub_checker_section($allowed_ip, $ip) {
list(
$begin, $end) = explode('-', $allowed_ip);
$begin = ip2long($begin);
$end = ip2long($end);
$ip = ip2long($ip);
return (
$ip >= $begin && $ip <= $end);
}

private function
_sub_checker_CIDR($CIDR, $IP) {
list (
$net, $mask) = explode('/', $CIDR);
return (
ip2long($IP) & ~((1 << (32 - $mask)) - 1) ) == ip2long($net);
}

}
?>

對我來說,這段程式碼運作良好,所以我要感謝你們大家!!!
schat
5 年前
抱歉,我剛寫錯了,對不起。這才是正確的版本。謝謝。

<?php
/**
* 將 CIDR 轉換為 IP 範圍
* 來自:<https://github.com/www-schat-top>
* @param string $cidr "192.168.0.0/23"
* @return array ["192.168.0.0", "192.168.1.255"]
*/
function cidr2range($cidr){
list(
$subnet, $mask ) = explode( '/', $cidr );
return [
$subnet, long2ip(ip2long( $subnet ) | (pow(2,( 32 - $mask ))-1))];
}
?>
jwadhams1 at yahoo dot com
15 年前
我想以一種我認為更直觀的方式,基於 kaputt 和 spinyn 的貢獻進行擴展(例如,讓 sprintf 執行所有二進位轉換和填充,並讓 substr_compare 執行修剪和比較)

<?php
function ip_in_network($ip, $net_addr, $net_mask){
if(
$net_mask <= 0){ return false; }
$ip_binary_string = sprintf("%032b",ip2long($ip));
$net_binary_string = sprintf("%032b",ip2long($net_addr));
return (
substr_compare($ip_binary_string,$net_binary_string,0,$net_mask) === 0);
}

ip_in_network("192.168.2.1","192.168.2.0",24); //true
ip_in_network("192.168.6.93","192.168.0.0",16); //true
ip_in_network("1.6.6.6","128.168.2.0",1); //false
?>
rasmus at mindplay dot dk
11 年前
如同其他人指出的,此函式的回傳值在 32 位元和 64 位元平台上並不一致。

為了在 32/64 位元平台(以及 Windows)上獲得一致的行為,最好的方法是使用最低公分母:強制將 ip2long() 的回傳值轉換為帶符號的 32 位元整數,例如:

var_dump(unpack('l', pack('l', ip2long('255.255.255.0'))));

這看起來很笨,但它可以在所有平台上給你一個一致的帶符號整數值。

(pack() 和 unpack() 的參數都是小寫字母 "L",而不是數字 "1",以防你在螢幕上看起來像是數字...)
oo dot para at gmail dot com
10 年前
如果您想使用此函數驗證 IP,請務必小心
應該改用 filter_var 函數來驗證 IP。

<?php
$ip
= '192.168.0355.24';
var_dump(ip2long($ip) !== false); // true (預期為 false)
var_dump(filter_var($ip, FILTER_VALIDATE_IP) !== false); // false

$ip = '192.168.355.24';
var_dump(ip2long($ip) !== false); // false
var_dump(filter_var($ip, FILTER_VALIDATE_IP) !== false); // false
?>
PandoraBox2007 at gmail dot com
13 年前
通用 ip4/ip6

<?php
// 編碼 --
function encode_ip ($ip)
{
$d = explode('.', $ip);
if (
count($d) == 4) return sprintf('%02x%02x%02x%02x', $d[0], $d[1], $d[2], $d[3]);

$d = explode(':', preg_replace('/(^:)|(:$)/', '', $ip));
$res = '';
foreach (
$d as $x)
$res .= sprintf('%0'. ($x == '' ? (9 - count($d)) * 4 : 4) .'s', $x);
return
$res;
}

// 解碼
function decode_ip($int_ip)
{
function
hexhex($value) { return dechex(hexdec($value)); };

if (
strlen($int_ip) == 32) {
$int_ip = substr(chunk_split($int_ip, 4, ':'), 0, 39);
$int_ip = ':'. implode(':', array_map("hexhex", explode(':',$int_ip))) .':';
preg_match_all("/(:0)+/", $int_ip, $zeros);
if (
count($zeros[0]) > 0) {
$match = '';
foreach(
$zeros[0] as $zero)
if (
strlen($zero) > strlen($match))
$match = $zero;
$int_ip = preg_replace('/'. $match .'/', ':', $int_ip, 1);
}
return
preg_replace('/(^:([^:]))|(([^:]):$)/', '$2$4', $int_ip);
}
$hexipbang = explode('.', chunk_split($int_ip, 2, '.'));
return
hexdec($hexipbang[0]). '.' . hexdec($hexipbang[1]) . '.' . hexdec($hexipbang[2]) . '.' . hexdec($hexipbang[3]);
}
?>

DB
`user_ip` varchar(32) DEFAULT NULL
f dot wiessner at smart-weblications dot net
15 年前
這裡有一些可以運作的 ip2long6 和 long2ip6 函數 - 請記住,這需要 php gmp-lib

<?php

$ipv6
= "2001:4860:a005::68";

function
ip2long6($ipv6) {
$ip_n = inet_pton($ipv6);
$bits = 15; // 16 x 8 位元 = 128位元
while ($bits >= 0) {
$bin = sprintf("%08b",(ord($ip_n[$bits])));
$ipv6long = $bin.$ipv6long;
$bits--;
}
return
gmp_strval(gmp_init($ipv6long,2),10);
}

function
long2ip6($ipv6long) {

$bin = gmp_strval(gmp_init($ipv6long,10),2);
if (
strlen($bin) < 128) {
$pad = 128 - strlen($bin);
for (
$i = 1; $i <= $pad; $i++) {
$bin = "0".$bin;
}
}
$bits = 0;
while (
$bits <= 7) {
$bin_part = substr($bin,($bits*16),16);
$ipv6 .= dechex(bindec($bin_part)).":";
$bits++;
}
// 壓縮

return inet_ntop(inet_pton(substr($ipv6,0,-1)));
}

print
$ipv6long = ip2long6($ipv6)."\n";
print
$ipv6 = long2ip6($ipv6long)."\n";

?>

輸出結果

42541956150894553250710573749450571880
2001:4860:a005::68
Ian B
17 年前
注意:ip2long() 不應該用於 CIDR 計算。
相反地,您應該使用如下的程式碼:

<?php
/* 從資料庫中的封鎖中取得基底和位元 */
list($base, $bits) = explode('/', $CIDR);

/* 現在將其拆分為其類別 */
list($a, $b, $c, $d) = explode('.', $base);

/* 現在進行一些位元平移/切換以轉換為整數 */
$i = ($a << 24) + ($b << 16) + ($c << 8) + $d;
$mask = $bits == 0 ? 0 : (~0 << (32 - $bits));

/* 這是我們的最小整數 */
$low = $i & $mask;

/* 這是我們的最大整數 */
$high = $i | (~$mask & 0xFFFFFFFF);

/* 現在將我們正在檢查的 IP 位址拆分為類別 */
list($a, $b, $c, $d) = explode('.', $iptocheck);

/* 現在將我們正在檢查的 IP 位址轉換為整數 */
$check = ($a << 24) + ($b << 16) + ($c << 8) + $d;

/* 如果 IP 位址在範圍內,包含
最大/最小值,那麼它就在 CIDR 範圍內 */
if ($check >= $low && $check <= $high)
return
1;
else
return
0;
?>

這意味著您應該每次檢查 IP
位址的格式是否正確。
pink at pink dot art dot pl
12 年前
請注意,當您運行 64 位元系統時,ip2long 將會產生 64 位元的整數,這不符合 MySQL 的 INT 欄位,您可以使用 BIGINT 或 INT UNSIGNED,因為在 64 位元系統上 ip2long 永遠不會返回負整數。另請參閱 https://bugs.php.net/bug.php?id=54338
Anonymous
20 年前
我將 jbothe at hotmail dot com 的函式重新編寫為一個小型的 OO 練習,並添加了一些額外的函式。

<?php

//--------------
// IPv4 類別
class ipv4
{
var
$address;
var
$netbits;

//--------------
// 建立新的類別
function ipv4($address,$netbits)
{
$this->address = $address;
$this->netbits = $netbits;
}

//--------------
// 傳回 IP 位址
function address() { return ($this->address); }

//--------------
// 傳回網路位元數
function netbits() { return ($this->netbits); }

//--------------
// 傳回網路遮罩
function netmask()
{
return (
long2ip(ip2long("255.255.255.255")
<< (
32-$this->netbits)));
}

//--------------
// 傳回位址所在的網路
function network()
{
return (
long2ip((ip2long($this->address))
& (
ip2long($this->netmask()))));
}

//--------------
// 傳回位址所在的廣播位址
function broadcast()
{
return (
long2ip(ip2long($this->network())
| (~(
ip2long($this->netmask())))));
}

//--------------
// 傳回網路遮罩的反向遮罩
function inverse()
{
return (
long2ip(~(ip2long("255.255.255.255")
<< (
32-$this->netbits))));
}

}

$ip = new ipv4("192.168.2.1",24);
print
"位址: $ip->address()\n";
print
"網路位元數: $ip->netbits()\n";
print
"網路遮罩: $ip->netmask()\n";
print
"反向遮罩: $ip->inverse()\n";
print
"網路: $ip->network()\n";
print
"廣播位址: $ip->broadcast()\n";
?>
admin at wudimei dot com
12 年前
<?php

/**
*
* 從 CIDR (網路 ID 和遮罩長度) 取得第一個 IP 和最後一個 IP
* 我會把這個函式整合到 "Rong Framework" :)
* @author admin@wudimei.com
* @param string $cidr 56.15.0.6/16 , [網路 ID]/[遮罩長度]
* @return array $ipArray = array( 0 =>"網路的第一個 IP", 1=>"網路的最後一個 IP" );
* $ipArray 的每個元素的類型都是長整數,使用 long2ip( $ipArray[0] ) 將其轉換為 IP 字串。
* 範例:
* list( $long_startIp , $long_endIp) = getIpRange( "56.15.0.6/16" );
* echo "起始 IP:" . long2ip( $long_startIp );
* echo "<br />";
* echo "結束 IP:" . long2ip( $long_endIp );
*/

function getIpRang( $cidr) {

list(
$ip, $mask) = explode('/', $cidr);

$maskBinStr =str_repeat("1", $mask ) . str_repeat("0", 32-$mask ); // 網路遮罩二進位字串
$inverseMaskBinStr = str_repeat("0", $mask ) . str_repeat("1", 32-$mask ); // 反向遮罩

$ipLong = ip2long( $ip );
$ipMaskLong = bindec( $maskBinStr );
$inverseIpMaskLong = bindec( $inverseMaskBinStr );
$netWork = $ipLong & $ipMaskLong;

$start = $netWork+1;// 去掉網路號,忽略網路 ID (例如:192.168.1.0)

$end = ($netWork | $inverseIpMaskLong) -1 ; // 去掉廣播位址,忽略廣播 IP (例如:192.168.1.255)
return array( $start, $end );
}
?>
alex at alex-at dot ru
8 年前
以下是我將前綴轉換為遮罩以及將遮罩轉換為前綴的版本。如果您不需要預先檢查,可以移除每個函式中帶有 if/preg 的第一行。

函式 prefix2Mask($prefix)
{
if (!preg_match('#^(?:0*[0-2]?[0-9]|3[0-2])$#', $prefix)) return false; # 錯誤的前綴
return long2ip(0xffffffff << (32 - $prefix));
}

函式 mask2Prefix($mask)
{
if (!preg_match('#^(?:(?:[0-1][0-9][0-9]|2[0-4][0-9]|25[0-5]|[0-9][0-9]|[0-9])\.){3}'.
'(?:[0-1][0-9][0-9]|2[0-4][0-9]|25[0-5]|[0-9][0-9]|[0-9])$#', $mask))
return false; # 錯誤的遮罩
$m2p = str_pad(substr(decbin(ip2long($mask)), -32, 32), 32, '0', STR_PAD_LEFT);
if (!preg_match('#^(1*)0*$#', $m2p, $m)) return false; # 錯誤的遮罩
return strlen($m[1]);
}
laacz at php dot net
17 年前
只是為了幫您節省一些時間。

請注意,IP 位址中的八位元組會被視為數字。因此,'10.0.0.11' 不等於 '10.0.0.011'。'011' 是八進位數字 (以 8 為底),因此會轉換為 '9'。您甚至可以更進一步看到 '10.0.0.0xa' 也有效 (等於 '10.0.0.16')。

不過,這不是 PHP 的問題。
chrisp-phpnet at inventivedingo dot com
16 年前
我在我的 lighttpd 網頁伺服器上使用 REMOTE_ADDR 呼叫此函式時遇到問題。結果發現此伺服器上已安裝 IPv6,因此即使 REMOTE_ADDR 是 IPv4 位址,也會使用 IPv6 的 IPv4 相容模式來格式化。例如,10.0.0.1 會變成 ::ffff:10.0.0.1,這會導致 iplong 將該位址報告為無效,而不是正確剖析它。

正確的修復方法當然是更新我的基礎架構,使其與 IPv6 正確相容;但在我特定情況下,這會涉及大量的重新設計。因此,在此期間,我使用這個快速且簡單的 hack 來解決這個問題

<?php
$ip
= htmlspecialchars($_SERVER['REMOTE_ADDR']);
if (
strpos($ip, '::') === 0) {
$ip = substr($ip, strrpos($ip, ':')+1);
}
$host = ip2long($ip);
?>

很醜陋但功能正常。
ken at expitrans dot com
19 年前
以下是所有各種註解的合併形式,以及一個更好(且正確)的網路比對函式。

<?php

function net_match($network, $ip) {
// 判斷網路位址,格式為 192.168.17.1/16 或
// 127.0.0.1/255.255.255.255 或 10.0.0.1 是否與給定的 IP 相符
$ip_arr = explode('/', $network);
$network_long = ip2long($ip_arr[0]);

$x = ip2long($ip_arr[1]);
$mask = long2ip($x) == $ip_arr[1] ? $x : 0xffffffff << (32 - $ip_arr[1]);
$ip_long = ip2long($ip);

// echo ">".$ip_arr[1]."> ".decbin($mask)."\n";
return ($ip_long & $mask) == ($network_long & $mask);
}

echo
net_match('192.168.17.1/16', '192.168.15.1')."\n"; // 回傳 true
echo net_match('127.0.0.1/255.255.255.255', '127.0.0.2')."\n"; // 回傳 false
echo net_match('10.0.0.1', '10.0.0.1')."\n"; // 回傳 true

?>
To Top