zoukankan      html  css  js  c++  java
  • php通过ip获取地理位置的方法

    一、

    用thinkphp中提供的扩展类库

    需要结合QQwry纯真IP数据库 下载地址:http://www.crsky.com/soft/2611.html ,是一个qqwry.dat文件

    View Code
      1 <?php
      2 /**
      3  *  IP 地理位置查询类 修改自 CoolCode.CN
      4  *  由于使用UTF8编码 如果使用纯真IP地址库的话 需要对返回结果进行编码转换
      5  * @category   ORG
      6  * @package  ORG
      7  * @subpackage  Net
      8  * @author    liu21st <liu21st@gmail.com>
      9  */
     10 class IpLocation {
     11     /**
     12      * QQWry.Dat文件指针
     13      *
     14      * @var resource
     15      */
     16     private $fp;
     17 
     18     /**
     19      * 第一条IP记录的偏移地址
     20      *
     21      * @var int
     22      */
     23     private $firstip;
     24 
     25     /**
     26      * 最后一条IP记录的偏移地址
     27      *
     28      * @var int
     29      */
     30     private $lastip;
     31 
     32     /**
     33      * IP记录的总条数(不包含版本信息记录)
     34      *
     35      * @var int
     36      */
     37     private $totalip;
     38 
     39     /**
     40      * 构造函数,打开 QQWry.Dat 文件并初始化类中的信息
     41      *
     42      * @param string $filename
     43      * @return IpLocation
     44      */
     45     public function __construct($filename = "UTFWry.dat") {
     46         $this->fp = 0;
     47         if (($this->fp = fopen(dirname(__FILE__).'/'.$filename, 'rb')) !== false) {
     48             $this->firstip = $this->getlong();
     49             $this->lastip = $this->getlong();
     50             $this->totalip = ($this->lastip - $this->firstip) / 7;
     51         }
     52     }
     53 
     54     /**
     55      * 返回读取的长整型数
     56      *
     57      * @access private
     58      * @return int
     59      */
     60     private function getlong() {
     61         //将读取的little-endian编码的4个字节转化为长整型数
     62         $result = unpack('Vlong', fread($this->fp, 4));
     63         return $result['long'];
     64     }
     65 
     66     /**
     67      * 返回读取的3个字节的长整型数
     68      *
     69      * @access private
     70      * @return int
     71      */
     72     private function getlong3() {
     73         //将读取的little-endian编码的3个字节转化为长整型数
     74         $result = unpack('Vlong', fread($this->fp, 3).chr(0));
     75         return $result['long'];
     76     }
     77 
     78     /**
     79      * 返回压缩后可进行比较的IP地址
     80      *
     81      * @access private
     82      * @param string $ip
     83      * @return string
     84      */
     85     private function packip($ip) {
     86         // 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
     87         // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
     88         return pack('N', intval(ip2long($ip)));
     89     }
     90 
     91     /**
     92      * 返回读取的字符串
     93      *
     94      * @access private
     95      * @param string $data
     96      * @return string
     97      */
     98     private function getstring($data = "") {
     99         $char = fread($this->fp, 1);
    100         while (ord($char) > 0) {        // 字符串按照C格式保存,以\0结束
    101             $data .= $char;             // 将读取的字符连接到给定字符串之后
    102             $char = fread($this->fp, 1);
    103         }
    104         return $data;
    105     }
    106 
    107     /**
    108      * 返回地区信息
    109      *
    110      * @access private
    111      * @return string
    112      */
    113     private function getarea() {
    114         $byte = fread($this->fp, 1);    // 标志字节
    115         switch (ord($byte)) {
    116             case 0:                     // 没有区域信息
    117                 $area = "";
    118                 break;
    119             case 1:
    120             case 2:                     // 标志字节为1或2,表示区域信息被重定向
    121                 fseek($this->fp, $this->getlong3());
    122                 $area = $this->getstring();
    123                 break;
    124             default:                    // 否则,表示区域信息没有被重定向
    125                 $area = $this->getstring($byte);
    126                 break;
    127         }
    128         return $area;
    129     }
    130 
    131     /**
    132      * 根据所给 IP 地址或域名返回所在地区信息
    133      *
    134      * @access public
    135      * @param string $ip
    136      * @return array
    137      */
    138     public function getlocation($ip='') {
    139         if (!$this->fp) return null;            // 如果数据文件没有被正确打开,则直接返回空
    140         if(empty($ip)) $ip = $this->get_client_ip();
    141         $location['ip'] = gethostbyname($ip);   // 将输入的域名转化为IP地址
    142         $ip = $this->packip($location['ip']);   // 将输入的IP地址转化为可比较的IP地址
    143                                                 // 不合法的IP地址会被转化为255.255.255.255
    144         // 对分搜索
    145         $l = 0;                         // 搜索的下边界
    146         $u = $this->totalip;            // 搜索的上边界
    147         $findip = $this->lastip;        // 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
    148         while ($l <= $u) {              // 当上边界小于下边界时,查找失败
    149             $i = floor(($l + $u) / 2);  // 计算近似中间记录
    150             fseek($this->fp, $this->firstip + $i * 7);
    151             $beginip = strrev(fread($this->fp, 4));     // 获取中间记录的开始IP地址
    152             // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式
    153             // 以便用于比较,后面相同。
    154             if ($ip < $beginip) {       // 用户的IP小于中间记录的开始IP地址时
    155                 $u = $i - 1;            // 将搜索的上边界修改为中间记录减一
    156             }
    157             else {
    158                 fseek($this->fp, $this->getlong3());
    159                 $endip = strrev(fread($this->fp, 4));   // 获取中间记录的结束IP地址
    160                 if ($ip > $endip) {     // 用户的IP大于中间记录的结束IP地址时
    161                     $l = $i + 1;        // 将搜索的下边界修改为中间记录加一
    162                 }
    163                 else {                  // 用户的IP在中间记录的IP范围内时
    164                     $findip = $this->firstip + $i * 7;
    165                     break;              // 则表示找到结果,退出循环
    166                 }
    167             }
    168         }
    169 
    170         //获取查找到的IP地理位置信息
    171         fseek($this->fp, $findip);
    172         $location['beginip'] = long2ip($this->getlong());   // 用户IP所在范围的开始地址
    173         $offset = $this->getlong3();
    174         fseek($this->fp, $offset);
    175         $location['endip'] = long2ip($this->getlong());     // 用户IP所在范围的结束地址
    176         $byte = fread($this->fp, 1);    // 标志字节
    177         switch (ord($byte)) {
    178             case 1:                     // 标志字节为1,表示国家和区域信息都被同时重定向
    179                 $countryOffset = $this->getlong3();         // 重定向地址
    180                 fseek($this->fp, $countryOffset);
    181                 $byte = fread($this->fp, 1);    // 标志字节
    182                 switch (ord($byte)) {
    183                     case 2:             // 标志字节为2,表示国家信息又被重定向
    184                         fseek($this->fp, $this->getlong3());
    185                         $location['country'] = $this->getstring();
    186                         fseek($this->fp, $countryOffset + 4);
    187                         $location['area'] = $this->getarea();
    188                         break;
    189                     default:            // 否则,表示国家信息没有被重定向
    190                         $location['country'] = $this->getstring($byte);
    191                         $location['area'] = $this->getarea();
    192                         break;
    193                 }
    194                 break;
    195             case 2:                     // 标志字节为2,表示国家信息被重定向
    196                 fseek($this->fp, $this->getlong3());
    197                 $location['country'] = $this->getstring();
    198                 fseek($this->fp, $offset + 8);
    199                 $location['area'] = $this->getarea();
    200                 break;
    201             default:                    // 否则,表示国家信息没有被重定向
    202                 $location['country'] = $this->getstring($byte);
    203                 $location['area'] = $this->getarea();
    204                 break;
    205         }
    206         if ($location['country'] == " CZ88.NET") {  // CZ88.NET表示没有有效信息
    207             $location['country'] = "Unknown";
    208         }
    209         if ($location['area'] == " CZ88.NET") {
    210             $location['area'] = "Unknown";
    211         }
    212         return $location;
    213     }
    214 
    215     /**
    216      * 返回本机ip
    217      *
    218      * @access public
    219      * @param null
    220      * @return string
    221      */
    222     public function get_client_ip(){
    223        if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
    224            $ip = getenv("HTTP_CLIENT_IP");
    225        else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
    226            $ip = getenv("HTTP_X_FORWARDED_FOR");
    227        else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
    228            $ip = getenv("REMOTE_ADDR");
    229        else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
    230            $ip = $_SERVER['REMOTE_ADDR'];
    231        else
    232            $ip = "unknown";
    233        return($ip);
    234     }
    235 
    236 
    237     /**
    238      * 析构函数,用于在页面执行结束后自动关闭打开的文件。
    239      *
    240      */
    241     public function __destruct() {
    242         if ($this->fp) {
    243             fclose($this->fp);
    244         }
    245         $this->fp = 0;
    246     }
    247 }

    使用方法

    <?php 
    //引入类库
    include 'IpLocation.class.php';
    $ip = new IpLocation('qqwry.dat');
    /* 
    *  getlocation() 传入域名或ip地址,为空为本地ip
    *  返回包含国家地区等IP起始范围的数组
    */
    $arr = $ip->getlocation('www.cnblogs.com');
    var_dump($arr);
    echo '国家:'.iconv('GB2312', 'UTF-8', $arr['country']);
    echo '--地区:'.iconv('GB2312', 'UTF-8', $arr['area']);
    ?> 

    好处:不用连接网络,本地查询,但数据库不是最新的

    二、

    通过网站提供的IP地址库接口

    如淘宝ip地址库:http://ip.taobao.com/instructions.php

    新浪的 http://int.dpool.sina.com.cn/iplookup/iplookup.php

    若要获取获得特定的IP地址,并以JSON格式返回

    http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json&ip=123.123.123.123

    好处:数据库较新,方法简单

    类似的接口

    获取手机归属地

    http://www.youdao.com/smartresult-xml/search.s?type=mobile&q=13612345678

  • 相关阅读:
    coredump分析
    Sword LRU算法
    C++ STL迭代器失效问题
    Sword DB主从一致性的解决方法
    Sword CRC算法原理
    C语言 按位异或实现加法
    Linux 等待信号(sigsuspend)
    C语言 宏定义之可变参数
    Linux shell字符串操作
    C++ *和&
  • 原文地址:https://www.cnblogs.com/mafeifan/p/3028327.html
Copyright © 2011-2022 走看看