limepie/ip-lib

处理 IPv4、IPv6 地址和范围

维护者

详细信息

github.com/yejune/ip-lib

主页

源代码

资助包维护!
mlocati
其他

dev-main 2024-05-13 20:59 UTC

This package is auto-updated.

Last update: 2024-09-25 19:39:47 UTC


README

Tests Coverage Status Scrutinizer Code Quality Packagist Downloads Open in Gitpod

IPLib - 处理 IPv4、IPv6 和 IP 范围

介绍

IPLib 是一个现代、PSR- compliant 的、测试驱动的 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 时,您可能希望地址的完整(展开)表示形式。在这种情况下,只需为 toString 方法使用一个 true 参数即可

// 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 或从 IPv6 转换为 IPv4 地址

$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函数,以及用于Alpine Linux的Musl实现(用于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函数,以及用于Alpine Linux的Musl实现(用于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_MATCH_GIT_TO_EMAIL(可选)
      • 将你的git用户.email在~/.gitconfig中设置为提供的值
    • GPG_AUTO_ULTIMATE_TRUST(可选)
      • 如果设置为yesYES,则你的GPG_KEY将被自动完全信任
  • 激活Intelliphense许可证密钥

    • INTELEPHENSE_LICENSEKEY
      • 创建~/intelephense/licence.txt并将包含提供的值
      • 这将使每次创建或重启工作区时都能激活Intelliphense

你真的想表示感谢吗?

你可以给我一杯月咖啡或一杯一次性咖啡 😉