PHP – IPv6通用类v1.0,为IPv6做好准备

本IPv6类共12个函数或方法:
addr([addr]) 获取或格式化IPv6地址
realip() 穿过代理获取真实IP地址
cut(addr) 压缩IPv6地址
fill(addr) 标准IPv6格式
ip2bin(addr) 打包IPv6地址为二进制流,16字节宽
ip2bin(bin) 解二进制流为IPv6地址格式
ip426(addr) 把一个标准IPv4地址转换为IPv6地址
type(addr) 检测IP地址类型
ipv4_check(addr) 检测IPv4地址是否合法
ipv6_check(addr) 检测IPv6地址是否合法
ip2long(addr) 自定义的ip2long函数,修复了PHP自带函数错误处理含前导0的IPv4地址的问题

wan_ip(addr) 检测IP地址是否为公网可用地址,以IANA官网为准


<?php
/* PHP-IPv6 V1.0.
 * Copyright (c) 2010 Mr.Bin <bin_jly@163.com>
 */
class ipv6 {
    function addr($addr=null) {
        // 常规获取IPv6地址或格式化IP地址为IPv6格式
        !$addr && ($addr = $_SERVER['REMOTE_ADDR']);
        $type = self::type($addr);
        if ( $type === 6 && self::ipv6_check($addr) ) return $addr;
        elseif ( $type === 4 ) return self::ip426($addr);
        else return 'Unknown';
    }
 
    function realip() {
        /* 穿过代理获取真实IP地址
         * 返回值为数组,array[0]为真实IP,array[1]为代理IP(可能为空)
         * 若array[0]和array[1]相等,则实际真实IP可能无法获取(高度匿名?)
         */
        $is_proxy = false;
        if ( $_SERVER['HTTP_X_FORWARDED_FOR'] ) {
            $is_proxy = true;
            $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
            foreach($ips as $ip){
                if ( !self::wan_ip($ip) ) $ip = false;
                else break;
            }
        }
        if ( !$ip && $_SERVER['HTTP_CLIENT_IP'] ) {
            $is_proxy = true;
            $ip = $_SERVER['HTTP_CLIENT_IP'];
            if ( !self::wan_ip($ip) ) $ip = false;
        }
        if ( $is_proxy ) {
            $proxy = $_SERVER['REMOTE_ADDR'];
            if (!$ip) $ip = $proxy;
        } else {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
        return explode(',', $ip.','.$proxy);
    }
 
    function cut($addr) {
        // 压缩IPv6地址
        if (!self::ipv6_check($addr)) return $addr;
        $addr = self::fill($addr);
        $arr = explode(':',$addr);
        foreach ($arr as $a) {
            $arr2[] = preg_replace('/^0{1,3}(\w+)/','\1',$a);
        }
        $addr = join(':',$arr2);
        $olen = strlen($addr);
        for($i=6;$i>0;$i--){
            // 初步压缩
            $addr = preg_replace('/:(0\:){'.$i.'}/','::',$addr,1);
            if (strlen($addr) < $olen ) break;
        }
        $addr = preg_replace('/^0\:\:/','::',$addr);
        $addr = preg_replace('/\:\:0$/','::',$addr);
        return $addr;
    }
 
    function fill($addr) {
        // 标准IPv6格式
        if (!self::ipv6_check($addr)) return $addr;
        $addr = self::_fix_v4($addr);
        $arr = explode(':',$addr);
        foreach ($arr as $a) {
            $l = strlen($a);
            if ( $l > 0 && $l < 4 )
                $arr2[] = str_repeat('0', 4-$l).$a;
            else $arr2[] = $a;
        }
        $addr = join(':',$arr2);
        $fil = ':'.str_repeat('0000:', 9-count($arr));
        $addr = str_replace('::',$fil,$addr);
        $addr = preg_replace('/^\:/','0000:',$addr);
        $addr = preg_replace('/\:$/',':0000',$addr);
        return $addr;
    }
 
    function ip2bin($addr) {
        $type = self::type($addr);
        if ( $type === 0 ) return false;
        elseif ( $type === 4 ) $addr = self::ip426($addr);
        else $addr = self::fill($addr);
        $hexstr = str_replace(':','',$addr);
        return pack('H*', $hexstr);
    }
 
    function bin2ip($bin) {
        if ( strlen($bin) !== 16 ) return false;
        $arr = str_split(join('',unpack('H*', $bin)), 4);
        $addr = join(':',$arr);
        return $addr;
    }
 
    function ip426($addr) {
        // IPv4 to IPv6
        if (!self::ipv4_check($addr)) return $addr;
        $hex = dechex(self::ip2long($addr));
        $hex = str_repeat('0', 8-strlen($hex)).$hex;
        $ipv6 = '0000:0000:0000:0000:0000:0000:';
        $ipv6 .= substr($hex,0,4) . ':' . substr($hex,4,4);
        return $ipv6;
    }
 
    function type($addr) {
        if ( self::ipv6_check($addr) ) return 6;
        elseif ( self::ipv4_check($addr) ) return 4;
        else return 0;
    }
 
    function ipv4_check($addr) {
        $arr = explode('.', $addr);
        $l = count($arr);
        for ( $i=0;$i<$l;$i++ ) {
            if ( strlen($arr[$i]) > 3 ) return false;
            if ( !is_numeric($arr[$i]) ) return false;
            $a = intval($arr[$i], 10);
            if ($a > 255 || $a <0) return false;
        }
        return true;
    }
 
    function ipv6_check($addr) {
        $addr = self::_fix_v4($addr);
        if ( strpos($addr, '.') ) return false;
        $l1 = count(explode('::',$addr));
        if ( $l1 > 2 ) return false;
        $l2 = count(explode(':',$addr));
        if ( $l2 < 3 || $l2 > 8 ) return false;
        if ( $l2 < 8 && $l1 !== 2 ) return false;
        preg_match('/^([0-9a-f]{0,4}\:)+[0-9a-f]{0,4}$/i',$addr,$arr);
        if ( !$arr[0] ) return false;
        return true;
    }
 
    function ip2long($addr) {
        $arr = explode('.', $addr);
        $l = count($arr);
        $long = 0;
        for ( $i=0;$i<$l;$i++ ) {
            if ( strlen($arr[$i]) > 3 ) return false;
            if ( !is_numeric($arr[$i]) ) return false;
            $a = intval($arr[$i], 10);
            if ($a > 255 || $a <0) return false;
            $long += $a * pow(2, 24-$i*8);
        }
        return $long;
    }
 
    function wan_ip($addr) {
        // 检查外网可用地址
        if ( self::ipv6_check($addr) ) {
            $addr = self::fill($addr);
            // IPv4类地址处理
            $v4p = substr($addr,0,29);
            if ( $v4p == '0000:0000:0000:0000:0000:0000'
                || strtolower($v4p) == 'ffff:0000:0000:0000:0000:0000' ) {
                $t = str_replace($v4p,'',$addr);
                $t = str_replace(':','',$t);
                $ipv4 = long2ip(hexdec($t));
                return self::_wan_ipv4($ipv4);
            }
            // 取前16位进行比较
            $v6p = substr($addr,0,4);
            $bin = decbin(hexdec($v6p));
            $p = str_repeat(0, 16-strlen($bin)).$bin;
            if ( (($p&'1110000000000000')=='0010000000000000') //2000::/3
                || (($p&'1111111000000000')=='1111110000000000') //FC00::/7
                || (($p&'1111111111000000')=='1111111010000000') //FE80::/10
                || (($p&'1111111100000000')=='1111111100000000') //FF00::/8
                ) return false;
            return true;
        } else {
            return self::_wan_ipv4($addr);
        }
    }
 
    private function _wan_ipv4($addr){
        if ( !self::ipv4_check($addr) ) return false;
        $arr = explode('.',$addr);
        $bin = decbin($arr[0]*256+$arr[1]);
        $p = str_repeat(0, 16-strlen($bin)).$bin;
        $p8 = $p & '1111111100000000';
        $p16 = &$p;
        if ( ($p8 == '0000000000000000') // 0/8
            || ($p8 == '0000010100000000') // 5/8
            || ($p8 == '0000101000000000') // 10/8
            || ($p8 == '0001011100000000') // 23/8
            || ($p8 == '0010010000000000') // 36/8
            || ($p8 == '0010010100000000') // 37/8
            || ($p8 == '0010011100000000') // 39/8
            || ($p8 == '0010101000000000') // 42/8
            || ($p8 == '0110010000000000') // 100/8
            || ($p8 == '0110011000000000') // 102/8
            || ($p8 == '0110011100000000') // 103/8
            || ($p8 == '0110100000000000') // 104/8
            || ($p8 == '0110100100000000') // 105/8
            || ($p8 == '0110101000000000') // 106/8
            || ($p8 == '0111111100000000') // 127/8
            || ($p16 == '1010100111111110') // 169.254/16
            || (($p&'1111111111110000')=='1010110000010000') // 172.16/12
            || ($p8 == '1011001100000000') // 179/8
            || ($p8 == '1011100100000000') // 185/8
            || ($p16 == '1100000010101000') // 192.168/16
            || (($p&'1110000000000000')=='1110000000000000') // 224/8-255/8
            ) return false;
        return true;
    }
 
    private function _fix_v4($addr) {
        // 修正IPv4位址类IPv6格式为标准IPv6格式,不验证合法性
        if ( !strpos($addr, '.') ) return $addr;
        preg_match('/(\d+\.){3}\d+$/',$addr,$arr);
        if ( !self::ipv4_check($arr[0]) ) return $addr;
        $hex = dechex(self::ip2long($arr[0]));
        $hex = str_repeat('0', 8-strlen($hex)).$hex;
        $v4p = substr($hex,0,4) . ':' . substr($hex,4,4);
        $p1 = str_replace($arr[0],'',$addr);
        strtolower($p1) === 'ffff:' && $p1 = '::'.$p1;
        $addr = $p1 . $v4p;
        return $addr;
    }
}
?>


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值