通过 PHP 缩短以 0 结尾的 IPv6 地址

Shortening an IPv6 address ending in 0's via PHP

我正在使用 an IPv6 class found on GitHub 进行一些 IP 操作,但我注意到缩短某些地址存在问题,通常以 0 结尾。

当我输入地址 2001::6dcd:8c74:0:0:0:0 时,结果是 2001::6dcd:8c74::::

$address = '2001::6dcd:8c74:0:0:0:0';
// Check to see if address is already compacted
if (strpos($address, '::') === FALSE) {
    $parts = explode(':', $address);
    $new_parts = array();
    $ignore = FALSE;
    $done = FALSE;

    for ($i = 0; $i < count($parts); $i++) {
        if (intval(hexdec($parts[$i])) === 0 && $ignore == FALSE && $done == FALSE) {
            $ignore = TRUE;
            $new_parts[] = '';
            if ($i == 0) {
                $new_parts = '';
            }
        } else if (intval(hexdec($parts[$i])) === 0 && $ignore == TRUE && $done == FALSE) {
            continue;
        } else if (intval(hexdec($parts[$i])) !== 0 && $ignore == TRUE) {
            $done = TRUE;
            $ignore = FALSE;
            $new_parts[] = $parts[$i];
        } else {
            $new_parts[] = $parts[$i];
        }
    }

    // Glue everything back together
    $address = implode(':', $new_parts);

}

// Remove the leading 0's
$new_address = preg_replace("/:0{1,3}/", ":", $address);

// $this->compact = $new_address;

// return $this->compact;
echo $new_address; // Outputs: 2001::6dcd:8c74::::

如果没有底部的问题行,您会得到 2001::6dcd:8c74:0:0:0:0。 现在,在替换所有前导 0 之前,该函数会在删除所有前导 0 之前检查地址是否以 :0 结尾。

if (substr($address, -2) != ':0') {
    $new_address = preg_replace("/:0{1,3}/", ":", $address);
} else {
    $new_address = $address;
}

添加了另一项检查以防止其他可能的有效 IPv6 地址格式错误。

if (isset($new_parts)) {
    if (count($new_parts) < 8 && array_pop($new_parts) == '') {
        $new_address .= ':0';
    }
}

新的完整功能如下所示:

// Check to see if address is already compacted
if (strpos($address, '::') === FALSE) {
    $parts = explode(':', $address);
    $new_parts = array();
    $ignore = FALSE;
    $done = FALSE;

    for ($i = 0; $i < count($parts); $i++) {
        if (intval(hexdec($parts[$i])) === 0 && $ignore == FALSE && $done == FALSE) {
            $ignore = TRUE;
            $new_parts[] = '';
            if ($i == 0) {
                $new_parts = '';
            }
        } else if (intval(hexdec($parts[$i])) === 0 && $ignore == TRUE && $done == FALSE) {
            continue;
        } else if (intval(hexdec($parts[$i])) !== 0 && $ignore == TRUE) {
            $done = TRUE;
            $ignore = FALSE;
            $new_parts[] = $parts[$i];
        } else {
            $new_parts[] = $parts[$i];
        }
    }

    // Glue everything back together
    $address = implode(':', $new_parts);

}
// Check to see if this ends in a shortened :0 before replacing all
// leading 0's
if (substr($address, -2) != ':0') {
    // Remove the leading 0's
    $new_address = preg_replace("/:0{1,3}/", ":", $address);
} else {
    $new_address = $address;
}
// Since new_parts isn't always set, check to see if it's set before
// trying to fix possibly broken shortened addresses ending in 0.
// (Ex: Trying to shorten 2001:19f0::0 will result in unset array)
if (isset($new_parts)) {
    // Some addresses (Ex: starting addresses for a range) can end in
    // all 0's resulting in the last value in the new parts array to be
    // an empty string.  Catch that case here and add the remaining :0
    // for a complete shortened address.
    if (count($new_parts) < 8 && array_pop($new_parts) == '') {
        $new_address .= ':0';
    }
}

// $this->compact = $new_address;

// return $this->compact;
echo $new_address; // Outputs: 2001::6dcd:8c74:0:0:0:0

这不是最干净的解决方案,根据地址的不同,其逻辑可能存在漏洞。如果我发现任何其他问题,我会更新此 question/answer。

要压缩 ipv6 地址,请使用 php 函数 inet_ntop and inet_pton 通过将 ipv6 转换为二进制并返回来进行格式化。

示例 ipv6 - 2001::6dcd:8c74:0:0:0:0

测试使用:

echo inet_ntop(inet_pton('2001::6dcd:8c74:0:0:0:0'));

输出 : 2001:0:6dcd:8c74::