mlocati / ip-lib
处理IPv4、IPv6地址和范围
Requires
- php: >=5.3.3
Requires (Dev)
- ext-pdo_sqlite: *
- phpunit/phpunit: ^4.8 || ^5.7 || ^6.5 || ^7.5 || ^8.5 || ^9.5
README
IPLib - 处理IPv4、IPv6和IP范围
简介
IPLib是一个现代、符合PSR规范的、测试驱动的IP地址和子网操作库。它实现了处理IPv4和IPv6地址以及IP范围(子网),在CIDR格式(如::1/128
或127.0.0.1/32
)和模式格式(如::*:*
或127.0.*.*
)中的原语。
要求
IPLib的要求非常基本,如下
- 支持任何大于5.3.3的PHP版本(PHP 5.3.x、5.4.x、5.5.x、5.6.x、7.x和8.x都完全支持)。
- 无外部依赖
- 无需特殊的PHP配置(是的,即使PHP没有构建IPv6支持,它也将始终工作!)。
安装
手动安装
下载最新版本,解压并添加以下行到我们的PHP文件中
require_once 'path/to/iplib/ip-lib.php';
使用Composer安装
只需运行
composer require mlocati/ip-lib
或将以下行添加到您的composer.json
文件中
"require": { "mlocati/ip-lib": "^1" }
示例用法
解析地址
解析IPv4地址
$address = \IPLib\Address\IPv4::parseString('127.0.0.1');
解析IPv6地址
$address = \IPLib\Address\IPv6::parseString('::1');
解析任何格式(IPv4或IPv6)的地址
$address = \IPLib\Factory::parseAddressString('::1'); $address = \IPLib\Factory::parseAddressString('127.0.0.1');
获取下一个/上一个地址
$address = \IPLib\Factory::parseAddressString('::1'); // This will print :: echo (string) $address->getPreviousAddress(); // This will print ::2 echo (string) $address->getNextAddress();
获取指定偏移量的地址
对于地址
$address = \IPLib\Factory::parseAddressString('::1'); // This will print ::1 echo (string) $address->getAddressAtOffset(0); // This will print ::2 echo (string) $address->getAddressAtOffset(1); // This will print ::3 echo (string) $address->getAddressAtOffset(2); // This will print ::3e9 echo (string) $address->getAddressAtOffset(1000); // This will print :: echo (string) $address->getAddressAtOffset(-1); // This will print NULL echo var_dump($address->getAddressAtOffset(-2));
对于范围
$range = \IPLib\Factory::parseRangeString('::ff00/120'); // This will print ::ff00 echo (string) $range->getAddressAtOffset(0); // This will print ::ff10 echo (string) $range->getAddressAtOffset(16); // This will print ::ff64 echo (string) $range->getAddressAtOffset(100); // This will print NULL because the address ::1:0 is out of the range var_dump($range->getAddressAtOffset(256)); // This will print ::ffff echo (string) $range->getAddressAtOffset(-1); // This will print ::fff0 echo (string) $range->getAddressAtOffset(-16); // This will print ::ff00 echo (string) $range->getAddressAtOffset(-256); // This will print NULL because the address ::feff is out of the range var_dump($range->getAddressAtOffset(-257));
解析IP地址范围
解析子网(CIDR)范围
$range = \IPLib\Range\Subnet::parseString('127.0.0.1/24'); $range = \IPLib\Range\Subnet::parseString('::1/128');
解析模式(星号表示法)范围
$range = \IPLib\Range\Pattern::parseString('127.0.0.*'); $range = \IPLib\Range\Pattern::parseString('::*');
将地址解析为范围
$range = \IPLib\Range\Single::parseString('127.0.0.1'); $range = \IPLib\Range\Single::parseString('::1');
解析任何格式的范围
$range = \IPLib\Factory::parseRangeString('127.0.0.*'); $range = \IPLib\Factory::parseRangeString('::1/128'); $range = \IPLib\Factory::parseRangeString('::');
从边界检索范围
您可以计算包含两个地址的最小范围
$range = \IPLib\Factory::getRangeFromBoundaries('192.168.0.1', '192.168.255.255'); // This will print 192.168.0.0/16 echo (string) $range;
您还可以计算一个范围列表,该列表精确地描述了两个地址之间的所有地址
$ranges = \IPLib\Factory::getRangesFromBoundaries('192.168.0.0', '192.168.0.5'); // This will print 192.168.0.0/30 192.168.0.4/31 echo implode(' ', $ranges);
检索范围的边界
$range = \IPLib\Factory::parseRangeString('127.0.0.*'); // This will print 127.0.0.0 echo (string) $range->getStartAddress(); // This will print 127.0.0.255 echo (string) $range->getEndAddress();
格式化地址和范围
IP地址和范围都具有一个toString
方法,您可以使用它来检索文本表示形式
// This will print 127.0.0.1 echo \IPLib\Factory::parseAddressString('127.0.0.1')->toString(); // This will print 127.0.0.1 echo \IPLib\Factory::parseAddressString('127.000.000.001')->toString(); // This will print ::1 echo \IPLib\Factory::parseAddressString('::1')->toString(); // This will print ::1 echo \IPLib\Factory::parseAddressString('0:0::1')->toString(); // This will print ::1/64 echo \IPLib\Factory::parseRangeString('0:0::1/64')->toString();
当处理IPv6时,您可能想要地址的完整(展开)表示形式。在这种情况下,只需将一个true
参数传递给toString
方法即可
// This will print 0000:0000:0000:0000:0000:0000:0000:0000 echo \IPLib\Factory::parseAddressString('::')->toString(true); // This will print 0000:0000:0000:0000:0000:0000:0000:0001 echo \IPLib\Factory::parseAddressString('::1')->toString(true); // This will print 0fff:0000:0000:0000:0000:0000:0000:0000 echo \IPLib\Factory::parseAddressString('fff::')->toString(true); // This will print 0000:0000:0000:0000:0000:0000:0000:0000 echo \IPLib\Factory::parseAddressString('::0:0')->toString(true); // This will print 0001:0002:0003:0004:0005:0006:0007:0008 echo \IPLib\Factory::parseAddressString('1:2:3:4:5:6:7:8')->toString(true); // This will print 0000:0000:0000:0000:0000:0000:0000:0001/64 echo \IPLib\Factory::parseRangeString('0:0::1/64')->toString();
地址和范围对象实现了__toString()
方法,该方法调用toString
方法。因此,如果您想获取对象的字符串(短)表示形式,可以执行以下任何一种操作
$address = \IPLib\Address\IPv6::parseString('::1'); // All these will print ::1 echo $address->toString(); echo $address->toString(false); echo (string) $address;
检查地址是否包含在范围内
所有范围类型都提供了一个contains
方法,所有IP地址类型都提供了一个matches
方法:您可以调用它们来检查地址是否包含在范围内
$address = \IPLib\Factory::parseAddressString('1:2:3:4:5:6:7:8'); $range = \IPLib\Factory::parseRangeString('0:0::1/64'); $contained = $address->matches($range); // that's equivalent to $contained = $range->contains($address);
请注意,如果地址是IPv4而范围是IPv6(或反之),则结果始终为false
。
检查一个范围是否包含另一个范围
所有范围类型都提供了一个containsRange
方法:您可以调用它们来检查一个地址范围是否完全包含另一个范围
$range1 = \IPLib\Factory::parseRangeString('0:0::1/64'); $range2 = \IPLib\Factory::parseRangeString('0:0::1/65'); $contained = $range1->containsRange($range2);
获取IP地址的类型
如果您想知道地址是否位于私有网络中,或者它是一个公共IP,或者您想了解任何其他内容,可以使用getRangeType
方法
$address = \IPLib\Factory::parseAddressString('::'); $type = $address->getRangeType(); $typeName = \IPLib\Range\Type::getName($type);
范围类型的最显著值是
\IPLib\Range\Type::T_UNSPECIFIED
如果地址全为零(0.0.0.0
或::
)\IPLib\Range\Type::T_LOOPBACK
如果地址是本地主机(通常为127.0.0.1
或::1
)\IPLib\Range\Type::T_PRIVATENETWORK
如果地址在本地网络中(例如192.168.0.1
或fc00::1
)\IPLib\Range\Type::T_PUBLIC
如果地址供公共使用(例如104.25.25.33
或2001:503:ba3e::2:30
)
获取IP地址范围的类型
如果您想了解地址范围的类型,可以使用 getRangeType
方法
$range = \IPLib\Factory::parseRangeString('2000:0::1/64'); // $type will contain the value of \IPLib\Range\Type::T_PUBLIC $type = $range->getRangeType(); // This will print Public address echo \IPLib\Range\Type::getName($type);
请注意,如果范围跨越多个范围类型,您将获得 NULL 作为范围类型
$range = \IPLib\Factory::parseRangeString('::/127'); // $type will contain null $type = $range->getRangeType(); // This will print Unknown type echo \IPLib\Range\Type::getName($type);
转换IP地址
此库支持使用 6to4 表示法 或 IPv4-mapped 表示法 将IPv4地址转换为IPv6地址,反之亦然
$ipv4 = \IPLib\Factory::parseAddressString('1.2.3.4'); // 6to4 notation $ipv6 = $ipv4->toIPv6(); // This will print 2002:102:304:: echo (string) $ipv6; // This will print 1.2.3.4 echo $ipv6->toIPv4(); // IPv4-mapped notation $ipv6_6to4 = $ipv4->toIPv6IPv4Mapped(); // This will print ::ffff:1.2.3.4 echo (string) $ipv6_6to4; // This will print 1.2.3.4 echo $ipv6_6to4->toIPv4();
转换IP范围
此库支持以模式格式(例如 192.168.*.*
)和CIDR/子网格式(例如 192.168.0.0/16
)表示的IPv4/IPv6范围,并提供在两种格式之间进行转换的方法
// This will print ::*:*:*:* echo \IPLib\Factory::parseRangeString('::/64')->asPattern()->toString(); // This will print 1:2::/96 echo \IPLib\Factory::parseRangeString('1:2::*:*')->asSubnet()->toString(); // This will print 192.168.0.0/24 echo \IPLib\Factory::parseRangeString('192.168.0.*')->asSubnet()->toString(); // This will print 10.*.*.* echo \IPLib\Factory::parseRangeString('10.0.0.0/8')->asPattern()->toString();
请注意,所有范围类型都实现了 asPattern()
和 asSubnet()
方法。
获取IPv4范围的子网掩码
您可以使用 getSubnetMask()
获取IPv4范围的子网掩码
// This will print 255.255.255.0 echo \IPLib\Factory::parseRangeString('192.168.0.*')->getSubnetMask()->toString(); // This will print 255.255.255.252 echo \IPLib\Factory::parseRangeString('192.168.0.12/30')->getSubnetMask()->toString();
获取范围大小
您可以使用 getSize()
获取该IP范围包含的地址数量
// This will print 256 echo \IPLib\Factory::parseRangeString('192.168.0.*')->getSize(); // This will print 4 echo \IPLib\Factory::parseRangeString('192.168.0.12/30')->getSize(); // This will print 1 echo \IPLib\Factory::parseRangeString('192.168.0.1')->getSize();
获取反向DNS查找地址
要执行反向DNS查询,您需要使用IP地址的特殊格式。
您可以使用IP地址实例的 getReverseDNSLookupName()
方法轻松检索它
$ipv4 = \IPLib\Factory::parseAddressString('1.2.3.255'); $ipv6 = \IPLib\Factory::parseAddressString('1234:abcd::cafe:babe'); // This will print 255.3.2.1.in-addr.arpa echo $ipv4->getReverseDNSLookupName(); // This will print e.b.a.b.e.f.a.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa echo $ipv6->getReverseDNSLookupName();
要解析反向DNS查找格式中的地址,您可以在解析字符串时使用 IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS
标志
$ipv4 = \IPLib\Factory::parseAddressString('255.3.2.1.in-addr.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS); $ipv6 = \IPLib\Factory::parseAddressString('e.b.a.b.e.f.a.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.c.b.a.4.3.2.1.ip6.arpa', \IPLib\ParseStringFlag::ADDRESS_MAYBE_RDNS); // This will print 1.2.3.255 echo $ipv4->toString(); // This will print 1234:abcd::cafe:babe echo $ipv6->toString();
您还可以为IP范围使用 getReverseDNSLookupName()
。在这种情况下,结果是字符串数组
$range = \IPLib\Factory::parseRangeString('10.155.16.0/22'); /* * This will print: * array ( * 0 => '16.155.10.in-addr.arpa', * 1 => '17.155.10.in-addr.arpa', * 2 => '18.155.10.in-addr.arpa', * 3 => '19.155.10.in-addr.arpa', * ) */ var_export($range->getReverseDNSLookupName());
使用数据库
此包提供了一项出色的功能:您可以将地址范围存储在数据库表中,并通过简单的查询检查地址是否包含在已保存的范围内。
要保存范围,您需要存储地址类型(对于IPv4它是 4
,对于IPv6它是 6
),以及表示范围起始和结束的两个值。这些方法是
$range->getAddressType(); $range->getComparableStartString(); $range->getComparableEndString();
假设您已将类型保存在名为 addressType
的字段中,并将范围边界保存在名为 rangeFrom
和 rangeTo
的两个字段中。
当您想要检查地址是否在已保存的范围内时,只需使用地址的 getComparableString
方法,并检查它是否位于字段 rangeFrom
和 rangeTo
之间,并检查存储的 addressType
是否与您想要检查的地址实例的类型相同。
以下是一个示例代码
/* * Let's assume that: * - $pdo is a PDO instance * - $range is a range object * - $address is an address object */ // Save the $range object $insertQuery = $pdo->prepare(' insert into ranges (addressType, rangeFrom, rangeTo) values (:addressType, :rangeFrom, :rangeTo) '); $insertQuery->execute(array( ':addressType' => $range->getAddressType(), ':rangeFrom' => $range->getComparableStartString(), ':rangeTo' => $range->getComparableEndString(), )); // Retrieve the saved ranges where an address $address falls: $searchQuery = $pdo->prepare(' select * from ranges where addressType = :addressType and :address between rangeFrom and rangeTo '); $searchQuery->execute(array( ':addressType' => $address->getAddressType(), ':address' => $address->getComparableString(), )); $rows = $searchQuery->fetchAll(); $searchQuery->closeCursor();
处理非标准地址和范围字符串
接受端口
如果您想接受可能包含端口的地址,可以指定 IPLib\ParseStringFlag::MAY_INCLUDE_PORT
标志
use IPLib\Factory; use IPLib\ParseStringFlag; require_once __DIR__ . '/../ip-lib.php'; // These will print NULL var_export(Factory::parseAddressString('127.0.0.1:80')); var_export(Factory::parseAddressString('[::]:80')); // This will print 127.0.0.1 echo (string) Factory::parseAddressString('127.0.0.1:80', ParseStringFlag::MAY_INCLUDE_PORT); // This will print :: echo (string) Factory::parseAddressString('[::]:80', ParseStringFlag::MAY_INCLUDE_PORT);
接受IPv6区域ID
如果您想接受可能包含区域ID的IPv6地址,可以指定 IPLib\ParseStringFlag::MAY_INCLUDE_ZONEID
标志
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print NULL var_export(Factory::parseAddressString('::%11')); // This will print :: echo (string) Factory::parseAddressString('::%11', ParseStringFlag::MAY_INCLUDE_ZONEID);
接受非十进制IPv4地址
IPv4地址通常用十进制表示,例如 192.168.0.1
。
顺便说一下,GNU(在许多Linux发行版中使用)、BSD(在Mac中使用)和Windows的inet_aton
和inet_addr
实现可以接受八进制和/或十六进制的IPv4地址。请注意,这一点不适用于inet_pton
和ip2long
函数,以及Musl实现(用于Alpine Linux)的inet_aton
和inet_addr
。
例如,以下地址都与192.168.0.1
等效
0xC0.0xA8.0x0.0x01
(仅十六进制)0300.0250.00.01
(仅八进制)192.0250.0.0x01
(十进制、八进制和十六进制数字)
(试试看:如果你访问http://0177.0.0.0x1
,你的浏览器将尝试访问http://127.0.0.1
)。
如果你想要接受这种非十进制语法,可以使用IPLib\ParseStringFlag::IPV4_MAYBE_NON_DECIMAL
标志
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print NULL var_export(Factory::parseAddressString('0177.0.0.0x1')); // This will print 127.0.0.1 var_export((string) Factory::parseAddressString('0177.0.0.0x1', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL)); // This will print NULL var_export(Factory::parseRangeString('0177.0.0.0x1/32')); // This will print 127.0.0.1/32 var_export((string) Factory::parseRangeString('0177.0.0.0x1/32', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
请注意,IPV4_MAYBE_NON_DECIMAL
标志也可能影响解析十进制数字
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print 127.0.0.10 since the last digit is assumed to be decimal var_export((string) Factory::parseAddressString('127.0.0.010')); // This will print 127.0.0.8 since the last digit is assumed to be octal var_export((string) Factory::parseAddressString('127.0.0.010', ParseStringFlag::IPV4_MAYBE_NON_DECIMAL));
接受非四点分表示法的IPv4地址
IPv4地址通常用4个数字表示,例如192.168.0.1
。
顺便提一下,GNU(用于许多Linux发行版)、BSD(用于Mac)和Windows的inet_aton
和inet_addr
实现接受1到4个数字的IPv4地址。
请注意,这一点不适用于inet_pton
和ip2long
函数,以及Musl实现(用于Alpine Linux)的inet_aton
和inet_addr
。
如果你想要接受这种非十进制语法,可以使用IPLib\ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED
标志
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print NULL var_export(Factory::parseAddressString('1.2.500')); // This will print 0.0.0.0 var_export((string) Factory::parseAddressString('0', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED)); // This will print 0.0.0.1 var_export((string) Factory::parseAddressString('1', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED)); // This will print 0.0.1.244 var_export((string) Factory::parseAddressString('0.0.500', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED)); // This will print 255.255.255.255 var_export((string) Factory::parseAddressString('4294967295', ParseStringFlag::IPV4ADDRESS_MAYBE_NON_QUAD_DOTTED));
接受紧凑的IPv4子网表示法
即使没有RFC描述它,IPv4子网表示法也可以写成紧凑形式,省略额外的数字(例如,127.0.0.0/24
可以写成127/24
)。如果你想要接受这种格式,可以指定IPLib\ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT
标志
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print NULL var_export(Factory::parseRangeString('127/24')); // This will print 127.0.0.0/24 echo (string) Factory::parseRangeString('127/24', ParseStringFlag::IPV4SUBNET_MAYBE_COMPACT);
组合多个标志
当然,你可以同时使用多个IPLib\ParseStringFlag
标志
use IPLib\Factory; use IPLib\ParseStringFlag; // This will print 127.0.0.255 var_export((string) Factory::parseAddressString('127.0.0.0xff:80', ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::IPV4_MAYBE_NON_DECIMAL)); // This will print :: var_export((string) Factory::parseAddressString('[::%11]:80', ParseStringFlag::MAY_INCLUDE_PORT | ParseStringFlag::MAY_INCLUDE_ZONEID));
Gitpod环境变量
以下功能可以通过在Gitpod首选项中设置的环境变量来启用。
* 请注意,将敏感数据存储在环境变量中并不最终安全,但对于大多数开发情况应该是可以的。
-
使用GPG密钥签名Git提交
GPG_KEY_ID
(必需)- 你想要用来签名你的git提交的GPG密钥的ID
GPG_KEY
(必需)- 与
GPG_KEY_ID
相对应的Base64编码的私有GPG密钥
- 与
GPG_MATCH_GIT_TO_EMAIL
(可选)- 将你的git用户.email在
~/.gitconfig
中设置为提供的值
- 将你的git用户.email在
GPG_AUTO_ULTIMATE_TRUST
(可选)- 如果值设置为
yes
或YES
,则你的GPG_KEY
将被自动视为最终可信
- 如果值设置为
-
激活Intelliphense许可证密钥
INTELEPHENSE_LICENSEKEY
- 创建
~/intelephense/licence.txt
并将包含提供的值 - 这将在每次创建或重新启动工作区时为你激活Intelliphense
- 创建