vlambda博客
学习文章列表

基于本地数据库的 IP 地址查询 PHP 源码

刹客网络科技资讯
点击右侧关注,最新科技资讯!


模块代码

PHP

 
   
   
 
  1. <?php

  2. /**

  3. * 纯真 IP 数据库查询

  4. *

  5. * 参考资料:

  6. * - 纯真 IP 数据库 http://www.cz88.net/ip/

  7. * - 纯真 IP 数据库自动更新文件教程 https://www.22vd.com/40035.html

  8. * - IpLocation https://github.com/nauxliu/IpLocation/

  9. *

  10. * 使用示例:

  11. * $ip = new IPQuery();

  12. * print_r($addr);

  13. */

  14.  

  15. class IPQuery {

  16. private $fh; // IP数据库文件句柄

  17. private $first; // 第一条索引

  18. private $last; // 最后一条索引

  19. private $total; // 索引总数

  20. private $dbFile = __DIR__ . DIRECTORY_SEPARATOR . 'qqwry.dat'; // 纯真 IP 数据库文件存放路径

  21. private $dbExpires = 86400 * 10; // 数据库文件有效期(10天)如无需自动更新 IP 数据库,请将此值改为 0

  22. // 构造函数

  23. function __construct() {

  24. // IP 数据库文件不存在或已过期,则自动获取

  25. if(!file_exists($this->dbFile) || ($this->dbExpires && ((time() - filemtime($this->dbFile)) > $this->dbExpires))) {

  26. $this->update();

  27. }

  28. }

  29. // 忽略超时

  30. private function ignore_timeout() {

  31. @ignore_user_abort(true);

  32. @ini_set('max_execution_time', 48 * 60 * 60);

  33. @set_time_limit(48 * 60 * 60); // set_time_limit(0) 2day

  34. @ini_set('memory_limit', '4000M');// 4G;

  35. }

  36. // 读取little-endian编码的4个字节转化为长整型数

  37. private function getLong4() {

  38. $result = unpack('Vlong', fread($this->fh, 4));

  39. return $result['long'];

  40. }

  41. // 读取little-endian编码的3个字节转化为长整型数

  42. private function getLong3() {

  43. $result = unpack('Vlong', fread($this->fh, 3).chr(0));

  44. return $result['long'];

  45. }

  46. // 查询位置信息

  47. private function getPos($data = '') {

  48. $char = fread($this->fh, 1);

  49. while (ord($char) != 0) { // 地区信息以 0 结束

  50. $data .= $char;

  51. $char = fread($this->fh, 1);

  52. }

  53. return $data;

  54. }

  55. // 查询运营商

  56. private function getISP() {

  57. $byte = fread($this->fh, 1); // 标志字节

  58. switch (ord($byte)) {

  59. case 0: $area = ''; break; // 没有相关信息

  60. case 1: // 被重定向

  61. fseek($this->fh, $this->getLong3());

  62. $area = $this->getPos(); break;

  63. case 2: // 被重定向

  64. fseek($this->fh, $this->getLong3());

  65. $area = $this->getPos(); break;

  66. default: $area = $this->getPos($byte); break; // 没有被重定向

  67. }

  68. return $area;

  69. }

  70. // 检查 IP 格式是否正确

  71. public function checkIp($ip) {

  72. $arr = explode('.', $ip);

  73. if(count($arr) != 4) return false;

  74. for ($i = 0; $i < 4; $i++) {

  75. if ($arr[$i] < '0' || $arr[$i] > '255') {

  76. return false;

  77. }

  78. }

  79. return true;

  80. }

  81. public function query($ip) {

  82. if(!$this->checkIp($ip)) {

  83. return false;

  84. }

  85. $this->fh = fopen($this->dbFile, 'rb');

  86. $this->first = $this->getLong4();

  87. $this->last = $this->getLong4();

  88. $this->total = ($this->last - $this->first) / 7; // 每条索引7字节

  89. $ip = pack('N', intval(ip2long($ip)));

  90. // 二分查找 IP 位置

  91. $l = 0;

  92. $r = $this->total;

  93. while($l <= $r) {

  94. $m = floor(($l + $r) / 2); // 计算中间索引

  95. fseek($this->fh, $this->first + $m * 7);

  96. fseek($this->fh, $this->getLong3());

  97. $r = $m - 1;

  98. } else {

  99. $l = $m + 1;

  100. } else { // 用户IP在中间索引的IP范围内时

  101. $findip = $this->first + $m * 7;

  102. break;

  103. }

  104. }

  105. }

  106. fseek($this->fh, $findip);

  107. $offset = $this->getlong3();

  108. fseek($this->fh, $offset);

  109. // 查找 IP 信息

  110. $byte = fread($this->fh, 1); // 标志字节

  111. switch (ord($byte)) {

  112. case 1: // 都被重定向

  113. fseek($this->fh, $countryOffset);

  114. $byte = fread($this->fh, 1); // 标志字节

  115. switch (ord($byte)) {

  116. case 2: // 信息被二次重定向

  117. fseek($this->fh, $this->getLong3());

  118. $location['pos'] = $this->getPos();

  119. fseek($this->fh, $countryOffset + 4);

  120. $location['isp'] = $this->getISP();

  121. break;

  122. default: // 信息没有被二次重定向

  123. $location['pos'] = $this->getPos($byte);

  124. $location['isp'] = $this->getISP();

  125. break;

  126. }

  127. break;

  128. case 2: // 信息被重定向

  129. fseek($this->fh, $this->getLong3());

  130. $location['pos'] = $this->getPos();

  131. fseek($this->fh, $offset + 8);

  132. $location['isp'] = $this->getISP();

  133. break;

  134. default: // 信息没有被重定向

  135. $location['pos'] = $this->getPos($byte);

  136. $location['isp'] = $this->getISP();

  137. break;

  138. }

  139. // 信息转码处理

  140. foreach ($location as $k => $v) {

  141. $location[$k] = iconv('gb2312', 'utf-8', $v);

  142. $location[$k] = preg_replace(array('/^.*CZ88\.NET.*$/isU', '/^.*纯真.*$/isU', '/^.*日IP数据/'), '', $location[$k]);

  143. $location[$k] = htmlspecialchars($location[$k]);

  144. }

  145. return $location;

  146. }

  147. // 更新数据库 https://www.22vd.com/40035.html

  148. public function update() {

  149. $this->ignore_timeout();

  150. $copywrite = file_get_contents('http://update.cz88.net/ip/copywrite.rar');

  151. $qqwry = file_get_contents('http://update.cz88.net/ip/qqwry.rar');

  152. $key = unpack('V6', $copywrite)[6];

  153. for($i = 0; $i < 0x200; $i++) {

  154. $key *= 0x805;

  155. $key ++;

  156. $key = $key & 0xFF;

  157. $qqwry[$i] = chr(ord($qqwry[$i]) ^ $key);

  158. }

  159. $qqwry = gzuncompress($qqwry);

  160. file_put_contents($this->dbFile, $qqwry);

  161. }

  162. // 析构函数

  163. function __destruct() {

  164. if($this->fh) {

  165. fclose($this->fh);

  166. }

  167. $this->fp = null;

  168. }

  169. }

 复制  文本

使用方法

将上面的模块代码保存为 IPQuery.class.php,然后按照如下方法调用即可:

PHP

 
   
   
 
  1. <?php

  2. require_once('IPQuery.class.php');

  3.  

  4. $ip = new IPQuery();

  5. $addr = $ip->query('123.233.233.233');

  6.  

  7. echo "<pre>

  8. IP起始段:{$addr['beginip']}

  9. IP结束段:{$addr['endip']}

  10. 运 营 商:{$addr['isp']}

  11. </pre>";

 复制  文本

输出效果如下所示:

基于本地数据库的 IP 地址查询 PHP 源码

注意事项

本模块会在第一次被调用时自动从纯真网下载最新的 IP 数据库到本地,因此第一次进行查询时会有点慢。如果你的服务器因为某些原因,无法连接到纯真网获取数据库,可以直接下载离线版,并将 IPQuery.class.php 第 25 行的 $dbExpires 值改为“0”(即永不自动更新数据库)。

基于本地数据库的 IP 地址查询 PHP 源码


基于本地数据库的 IP 地址查询 PHP 源码

点分享


基于本地数据库的 IP 地址查询 PHP 源码

点收藏

点点赞

点在看