代码之家  ›  专栏  ›  技术社区  ›  matpie

如何在PHP中处理IPv6地址?

  •  32
  • matpie  · 技术社区  · 17 年前

    在稍微彻底搜索后,我注意到PHP中稍微缺少处理函数 IPv6 为了满足个人需求,我创建了一些功能来帮助过渡。

    这个 IPv6ToLong() 函数是这里提出的临时解决方案: How to store IPv6-compatible address in a relational database 。它将IP拆分为两个整数,并在数组中返回它们。

    /**
     * Convert an IPv4 address to IPv6
     *
     * @param string IP Address in dot notation (192.168.1.100)
     * @return string IPv6 formatted address or false if invalid input
     */
    function IPv4To6($Ip) {
        static $Mask = '::ffff:'; // This tells IPv6 it has an IPv4 address
        $IPv6 = (strpos($Ip, '::') === 0);
        $IPv4 = (strpos($Ip, '.') > 0);
    
        if (!$IPv4 && !$IPv6) return false;
        if ($IPv6 && $IPv4) $Ip = substr($Ip, strrpos($Ip, ':')+1); // Strip IPv4 Compatibility notation
        elseif (!$IPv4) return $Ip; // Seems to be IPv6 already?
        $Ip = array_pad(explode('.', $Ip), 4, 0);
        if (count($Ip) > 4) return false;
        for ($i = 0; $i < 4; $i++) if ($Ip[$i] > 255) return false;
    
        $Part7 = base_convert(($Ip[0] * 256) + $Ip[1], 10, 16);
        $Part8 = base_convert(($Ip[2] * 256) + $Ip[3], 10, 16);
        return $Mask.$Part7.':'.$Part8;
    }
    
    /**
     * Replace '::' with appropriate number of ':0'
     */
    function ExpandIPv6Notation($Ip) {
        if (strpos($Ip, '::') !== false)
            $Ip = str_replace('::', str_repeat(':0', 8 - substr_count($Ip, ':')).':', $Ip);
        if (strpos($Ip, ':') === 0) $Ip = '0'.$Ip;
        return $Ip;
    }
    
    /**
     * Convert IPv6 address to an integer
     *
     * Optionally split in to two parts.
     *
     * @see https://stackoverflow.com/questions/420680/
     */
    function IPv6ToLong($Ip, $DatabaseParts= 2) {
        $Ip = ExpandIPv6Notation($Ip);
        $Parts = explode(':', $Ip);
        $Ip = array('', '');
        for ($i = 0; $i < 4; $i++) $Ip[0] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
        for ($i = 4; $i < 8; $i++) $Ip[1] .= str_pad(base_convert($Parts[$i], 16, 2), 16, 0, STR_PAD_LEFT);
    
        if ($DatabaseParts == 2)
                return array(base_convert($Ip[0], 2, 10), base_convert($Ip[1], 2, 10));
        else    return base_convert($Ip[0], 2, 10) + base_convert($Ip[1], 2, 10);
    }
    

    对于这些函数,我通常通过首先调用此函数来实现它们:

    /**
     * Attempt to find the client's IP Address
     *
     * @param bool Should the IP be converted using ip2long?
     * @return string|long The IP Address
     */
    function GetRealRemoteIp($ForDatabase= false, $DatabaseParts= 2) {
        $Ip = '0.0.0.0';
        // [snip: deleted some dangerous code not relevant to question. @webb]
        if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] != '')
            $Ip = $_SERVER['REMOTE_ADDR'];
        if (($CommaPos = strpos($Ip, ',')) > 0)
            $Ip = substr($Ip, 0, ($CommaPos - 1));
    
        $Ip = IPv4To6($Ip);
        return ($ForDatabase ? IPv6ToLong($Ip, $DatabaseParts) : $Ip);
    }
    

    请告诉我,我是在重新发明轮子,还是做错了什么。

    此实现将IPv4转换为IPv6。它不接触的任何IPv6地址。

    5 回复  |  直到 4 年前
    推荐文章