1. <?php 
  2. require 'iplocation.class.php'//需要包含类 
  3. //实例化IpLocation类,IpLocation可以传入两个参数([IP数据库文件,默认当前目录QQWry.Dat],[需要显示的文字编码,默认utf-8]); 
  4. $IpLocation = new IpLocation("QQWry.Dat","gbk"); 
  5. $ip="74.125.67.100"
  6. //$IpLocation->getlocation()可返回信息的数组,若不给ip则自动获取客户端的ip地址,在本机实验可手动输入一组ip 
  7. print_r($IpLocation->getlocation($ip)); 
  8. ?> 

 

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