PHP 中的 INET_ATON() 和 INET_NTOA()?
Posted
技术标签:
【中文标题】PHP 中的 INET_ATON() 和 INET_NTOA()?【英文标题】:INET_ATON() and INET_NTOA() in PHP? 【发布时间】:2011-02-14 19:25:42 【问题描述】:我想在我的数据库中存储 IP 地址,但我还需要在整个应用程序中使用它们。我阅读了有关在我的 mysql 查询中使用 INET_ATON()
和 INET_NTOA()
从 IP 地址中获取 32 位无符号整数的信息,这正是我想要的,因为它可以比使用 char(15) 更快地搜索数据库.
问题是,我找不到在 php 中做同样事情的函数。我唯一遇到的是:
http://php.net/manual/en/function.ip2long.php
所以我测试了它:
$ip = $_SERVER['REMOTE_ADDR'];
echo ip2long($ip);
它什么也不输出。在他们给出的示例中,它似乎有效,但我又不确定ip2long()
是否与INET_ATON()
做同样的事情。
有人知道可以执行此操作的 PHP 函数吗?或者甚至是在数据库中存储 IP 地址的全新解决方案?
谢谢。
【问题讨论】:
忘记补充了,我正在使用本地主机,所以也许这就是 ip2long() 没有返回任何内容的原因? 本地主机的 IPv4 仍然是 127.0.0.1,除非您使用 IPv6,否则它应该可以工作 - 检查我的答案。 【参考方案1】:ip2long()
和 long2ip()
函数应该可以正常工作。
注意:您应该将它们用于 IPv4 地址——确保在您的情况下,$_SERVER['REMOTE_ADDR']
实际上包含一个有效的 IPv4 地址(而不是一些 IPv6 的东西)。
尝试使用谷歌 IP 地址:
var_dump(ip2long('209.85.227.147'));
var_dump(long2ip(3512066963));
我得到以下输出:
int(3512066963)
string(14) "209.85.227.147"
【讨论】:
【参考方案2】:ip2long
、long2ip
和 MySQL 函数之间有一个重要区别。
PHP 的 ip2long
和 long2ip
处理有符号整数。
见http://php.net/manual/en/function.ip2long.php
"由于PHP的整数类型是有符号的,而且很多IP地址在32位架构上会产生负整数,所以需要使用sprintf()或printf()的'%u'格式化器来获取字符串表示未签名的 IP 地址。”
MySQL 的 INET_ATON()
和 INET_NTOA()
处理无符号整数
见http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_inet-aton
"要存储由 INET_ATON() 生成的值,请使用 INT UNSIGNED 列而不是带符号的 INT。如果使用带符号的列,则无法存储与第一个八位字节大于 127 的 IP 地址对应的值正确。”
您可以使用以下一些函数在两者之间工作。
如果您使用INET_ATON()
将IP 插入MySQL 数据库,您可以使用以下方法将其转换回PHP:
long2ip(sprintf("%d", $ip_address));
您可以使用以下方法将其转换为从 PHP 将其保存在数据库中:
sprintf("%u", ip2long($ip_address));
(同样重要的是,不要将$ip_address
类型转换为int
,因为如果数字大于MAX_INT
,这可能会导致问题出现问题。如果必须转换,请将其转换为@987654335 @或float
)
【讨论】:
像魅力一样工作。非常感谢您的提示。我从来没有意识到这一点。ip2long
在 64 位系统上将给出无符号结果。不过,在这种情况下,“%u”仍然会做正确的事情。【参考方案3】:
您不必在 PHP 中处理这个问题,这就是 MySQL 原生函数的用途。看这个例子:
create table iptable (
ip int(32) unsigned not null,
comment varchar(32) not null
);
insert into iptable (ip, comment) values (inet_aton('10.0.0.3'), 'This is 10.0.0.3');
select * from iptable;
+-----------+------------------+
| ip | comment |
+-----------+------------------+
| 167772163 | This is 10.0.0.3 |
+-----------+------------------+
select inet_ntoa(ip) as ip, comment from iptable;
+----------+------------------+
| ip | comment |
+----------+------------------+
| 10.0.0.3 | This is 10.0.0.3 |
+----------+------------------+
如果你想在同一个字段中同时处理 ipv4 和 ipv6,并且你使用的是 Mysql 5.6 或更高版本,你可以使用 varbinary(16) 以及函数 inet6_aton 和 inet6_ntoa。这是一个更好的例子,说明为什么应该使用 MySQL 函数而不是在 PHP 中处理二进制数据:
create table iptable2 (
ip varbinary(16) not null,
comment varchar(32) not null
);
insert into iptable2 (ip, comment) values
(inet6_aton('192.168.1.254'), 'This is router 192.168.1.254'),
(inet6_aton('::1'), 'This is ipv6 localhost ::1'),
(inet6_aton('FE80:0000:0000:0000:0202:B3FF:FE1E:8329'), 'This is some large ipv6 example')
;
select * from iptable2;
+------------------+---------------------------------+
| ip | comment |
+------------------+---------------------------------+
| +¿?¦ | This is router 192.168.1.254 |
| ? | This is ipv6 localhost ::1 |
| ¦Ç ??¦ ¦?â) | This is some large ipv6 example |
+------------------+---------------------------------+
select inet6_ntoa(ip) as ip, comment from iptable2;
+--------------------------+---------------------------------+
| ip | comment |
+--------------------------+---------------------------------+
| 192.168.1.254 | This is router 192.168.1.254 |
| ::1 | This is ipv6 localhost ::1 |
| fe80::202:b3ff:fe1e:8329 | This is some large ipv6 example |
+--------------------------+---------------------------------+
您可以看到,通过这样做,您实际上可以避免评估不同格式的 ipv6 地址,因为 MySQL 会将它们转换为二进制并返回到最简单的表达式。
我知道这个问题已经有 2 年多了,但我想让这些信息对遇到的其他人有用。
HTH
弗朗西斯科·萨拉博佐
【讨论】:
如果你必须用准备好的语句插入它们怎么办?不能给mysql函数绑定参数,INET_ATON(?)
,所以必须用php
@the_nuts 你在说什么?是的你可以。自己试试吧。【参考方案4】:
对于 IPv4 和 IPv6 支持,请使用 inet_pton()
和 inet_ntop()
,它们自 PHP 5.1+ 起可用,并且完全模仿等效的 MySQL 函数。
否则只使用ip2long()
和long2ip()
。
【讨论】:
我无法模仿这个。inet_ntop( $ip )
正是我在 PHP 中将 varbinary 值(来自 MYSQL)转换为可读 IP 地址所需的。谢谢。【参考方案5】:
这里是 PHP 替代函数(在您的程序中简单复制/粘贴) -
function inet_aton($ip)
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) return 0;
return sprintf("%u", ip2long($ip));
function inet_ntoa($num)
$num = trim($num);
if ($num == "0") return "0.0.0.0";
return long2ip(-(4294967295 - ($num - 1)));
【讨论】:
【参考方案6】:ip2long 等价于 inet_aton()。
ip2long 仅适用于 IPv4。我怀疑您的系统正在使用 IPv6 进行环回。尝试打印 REMOTE_ADDR。
【讨论】:
啊,我明白为什么它现在不起作用了,$_SERVER['REMOTE_ADDR']
返回:::1
。可能是因为我在本地主机上。有没有办法防止这种情况?还是我必须检查它是否返回::1
?
::1 是用于环回的 IPv6。您需要禁用 IPv6。对于 Linux,请遵循以下说明:blog.taragana.com/index.php/archive/…以上是关于PHP 中的 INET_ATON() 和 INET_NTOA()?的主要内容,如果未能解决你的问题,请参考以下文章
usleep() 和 inet_aton() 定义的奇怪行为
由 Python2 和 Python3 中 socket.inet_aton() 实现不同引发的血案