mlocati/ip-lib

处理IPv4、IPv6地址和范围

资助包维护!
mlocati
其他

安装次数: 2,954,735

依赖者: 28

建议者: 0

安全: 0

星级: 259

关注者: 10

分支: 39

开放问题: 1

1.18.0 2022-01-13 18:05 UTC

This package is auto-updated.

Last update: 2024-09-08 18:00:48 UTC


README

Tests Coverage Status Scrutinizer Code Quality Packagist Downloads Open in Gitpod

IPLib - 处理IPv4、IPv6和IP范围

简介

IPLib是一个现代、符合PSR规范的、测试驱动的IP地址和子网操作库。它实现了处理IPv4和IPv6地址以及IP范围(子网),在CIDR格式(如::1/128127.0.0.1/32)和模式格式(如::*:*127.0.*.*)中的原语。

要求

IPLib的要求非常基本,如下

  • 支持任何大于5.3.3的PHP版本(PHP 5.3.x5.4.x5.5.x5.6.x7.x8.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.1fc00::1
  • \IPLib\Range\Type::T_PUBLIC 如果地址供公共使用(例如 104.25.25.332001: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 的字段中,并将范围边界保存在名为 rangeFromrangeTo 的两个字段中。

当您想要检查地址是否在已保存的范围内时,只需使用地址的 getComparableString 方法,并检查它是否位于字段 rangeFromrangeTo 之间,并检查存储的 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_atoninet_addr实现可以接受八进制和/或十六进制的IPv4地址。请注意,这一点不适用于inet_ptonip2long函数,以及Musl实现(用于Alpine Linux)的inet_atoninet_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_atoninet_addr实现接受1到4个数字的IPv4地址

请注意,这一点不适用于inet_ptonip2long函数,以及Musl实现(用于Alpine Linux)的inet_atoninet_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中设置为提供的值
    • GPG_AUTO_ULTIMATE_TRUST(可选)
      • 如果值设置为yesYES,则你的GPG_KEY将被自动视为最终可信
  • 激活Intelliphense许可证密钥

    • INTELEPHENSE_LICENSEKEY
      • 创建~/intelephense/licence.txt并将包含提供的值
      • 这将在每次创建或重新启动工作区时为你激活Intelliphense

你真的想表示感谢吗?

你可以提供一份月度咖啡或一份一次性咖啡 😉