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

在PHP中使用IPv6地址

  •  30
  • matpie  · 技术社区  · 16 年前

    在彻底搜索之后,我注意到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';
        if (isset($_SERVER['HTTP_CLIENT_IP']) && $_SERVER['HTTP_CLIENT_IP'] != '')
            $Ip = $_SERVER['HTTP_CLIENT_IP'];
        elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && $_SERVER['HTTP_X_FORWARDED_FOR'] != '')
            $Ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        elseif (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);
    }
    

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

    5 回复  |  直到 7 年前
        1
  •  19
  •   user42092 user42092    16 年前

    怎么样 inet_ntop() varbinary(16) 储存它。

        2
  •  1
  •   Ross    16 年前

    PHP.net的 Filter extension 包含 some constants 用于匹配IPv4和IPv6地址,这可能有助于检查地址。但我还没有看到任何转换实用程序。

        3
  •  1
  •   Fredrik    15 年前

    您还可以将地址存储在mysql中的二进制文件(16)中,因此您应该可以选择从IPv6ToLong()以二进制文件形式输出地址。

    这确实需要在PHP中以本机方式添加,特别是当许多支持IPv6的web服务器报告::FFFF:1.2.3.4作为客户端IP时,它与ip2long不兼容,并且会破坏很多东西。

        4
  •  1
  •   datahell    14 年前

    下面是一个使用filter_var(PHP>=5.2)的替代函数

    function IPv4To6($ip) {
     if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === true) {
      if (strpos($ip, '.') > 0) {
       $ip = substr($ip, strrpos($ip, ':')+1);
      } else { //native ipv6
       return $ip;
      }
     }
     $is_v4 = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
     if (!$is_v4) { return false; }
     $iparr = array_pad(explode('.', $ip), 4, 0);
        $Part7 = base_convert(($iparr[0] * 256) + $iparr[1], 10, 16);
        $Part8 = base_convert(($iparr[2] * 256) + $iparr[3], 10, 16);
        return '::ffff:'.$Part7.':'.$Part8;
    }
    
        5
  •  1
  •   Samuel Liew cicero lopes    6 年前

    dtr_pton dtr_ntop 可与IPv4和IPv6一起使用。它将在可打印和二进制之间来回转换它们。

    第一个功能, dtr\u pton 将检查提供的参数是有效的IPv4还是有效的IPv6。根据结果,可以抛出异常,或者返回IP的二进制表示。通过使用此函数,您可以对结果执行AND'ing或or'ing(对于子网/whathaveyou)。我建议您将这些存储在数据库中作为 VARBINARY(39) VARCHAR(39)

    /**
    * dtr_pton
    *
    * Converts a printable IP into an unpacked binary string
    *
    * @author Mike Mackintosh - mike@bakeryphp.com
    * @param string $ip
    * @return string $bin
    */
    function dtr_pton( $ip ){
    
        if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
            return current( unpack( "A4", inet_pton( $ip ) ) );
        }
        elseif(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
            return current( unpack( "A16", inet_pton( $ip ) ) );
        }
    
        throw new \Exception("Please supply a valid IPv4 or IPv6 address");
    
        return false;
    }
    

    第二个功能, dtr_ntop 将IP的二进制表示形式转换回可打印的IP地址。

    /**
    * dtr_ntop
    *
    * Converts an unpacked binary string into a printable IP
    *
    * @author Mike Mackintosh - mike@bakeryphp.com
    * @param string $str
    * @return string $ip
    */
    function dtr_ntop( $str ){
        if( strlen( $str ) == 16 OR strlen( $str ) == 4 ){
            return inet_ntop( pack( "A".strlen( $str ) , $str ) );
        }
    
        throw new \Exception( "Please provide a 4 or 16 byte string" );
    
        return false;
    }
    

    found on StackOverflow

    function expand($ip){
        $hex = unpack("H*hex", inet_pton($ip));         
        $ip = substr(preg_replace("/([A-f0-9]{4})/", "$1:", $hex['hex']), 0, -1);
    
        return $ip;
    }
    

    另外,关于这个主题的好文章可以在我的博客上找到 HighOnPHP: 5 Tips for Working With IPv6 in PHP . 本文在一个面向对象类中使用了上面描述的一些方法,可以在 GitHub: mikemackintosh\dTR-IP