php实现通过ip查询地理位置

 

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

 

效果:
Array
(
    [ip] => 123.112.215.244
    [beginip] => 123.112.202.0
    [endip] => 123.112.225.255
    [country] => 北京市
    [area] => 网通
)
float(0.00282621383667)

关于浪漫谎言

浪漫谎言,ALLCAN工作室创始人,从事网站开发、网站美工、网络编程等。
此条目发表在PHP分类目录,贴了, , , 标签。将固定链接加入收藏夹。

php实现通过ip查询地理位置》有1条回应

  1. hk说:

    这次我在哪里呢?IP能不能测出呢?

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注