Ip.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: wangfanchang
  5. * Date: 18/1/10
  6. * Time: 下午2:13
  7. */
  8. namespace app\common\library;
  9. use think\Exception;
  10. use think\Request;
  11. defined('INDEX_BLOCK_LENGTH') or define('INDEX_BLOCK_LENGTH', 12);
  12. defined('TOTAL_HEADER_LENGTH') or define('TOTAL_HEADER_LENGTH', 8192);
  13. class Ip
  14. {
  15. /**
  16. * db file handler
  17. */
  18. private static $dbFileHandler = null;
  19. /**
  20. * header block info
  21. */
  22. private static $HeaderSip = null;
  23. private static $HeaderPtr = null;
  24. /**
  25. * super block index info
  26. */
  27. private static $firstIndexPtr = 0;
  28. private static $lastIndexPtr = 0;
  29. private static $totalBlocks = 0;
  30. /**
  31. * for memory mode only
  32. * the original db binary string
  33. */
  34. private static $dbBinStr = null;
  35. /**
  36. * ip地址记录
  37. * @var array
  38. */
  39. private static $list = [];
  40. /**
  41. * 用户访问ip地址
  42. * @var null|string
  43. */
  44. private static $local = null;
  45. /**
  46. * 获取用户ip地址
  47. * @return string
  48. */
  49. public static function ip()
  50. {
  51. if (is_null(self::$local)) {
  52. $ip = Request::instance()->ip();
  53. if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
  54. self::$local = $ip;
  55. }
  56. }
  57. return static::$local;
  58. }
  59. /**
  60. * 获取IP的C段地址
  61. * @return string
  62. */
  63. public static function ip_c(){
  64. $ip = self::ip();
  65. $ip_c = null;
  66. if($ip){
  67. $ip_arr = explode('.',$ip);
  68. $ip_c = $ip_arr[0].'.'.$ip_arr[1].'.'.$ip_arr[2];
  69. }
  70. return $ip_c;
  71. }
  72. /**
  73. * 原ip格式返回
  74. * @param null $ip
  75. * @return string
  76. */
  77. public static function str($ip = null)
  78. {
  79. if (is_null($ip)) {
  80. $ip = self::ip();
  81. }
  82. if (isset(self::$list[$ip])) {
  83. return self::$list[$ip];
  84. }
  85. $res = self::memorySearch($ip);
  86. self::$list[$ip] = $res;
  87. return $res;
  88. }
  89. /**
  90. * 返回切割的region数组
  91. * @param null $ip
  92. * @return array 国家|区域|省份|城市|ISP
  93. */
  94. public static function arr($ip = null)
  95. {
  96. return explode('|', self::str($ip));
  97. }
  98. /**
  99. * 获取国家
  100. * @param null $ip
  101. * @return string
  102. */
  103. public static function country($ip = null)
  104. {
  105. $arr = self::arr($ip);
  106. return $arr[0] ?? null;
  107. }
  108. /**
  109. * 获取区域
  110. * @param null $ip
  111. * @return string
  112. */
  113. public static function area($ip = null)
  114. {
  115. $arr = self::arr($ip);
  116. return $arr[1] ?? null;
  117. }
  118. /**
  119. * 获取省份
  120. * @param null $ip
  121. * @return string
  122. */
  123. public static function province($ip = null)
  124. {
  125. $arr = self::arr($ip);
  126. return $arr[2] ?? null;
  127. }
  128. /**
  129. * 获取城市
  130. * @param null $ip
  131. * @return string
  132. */
  133. public static function city($ip = null)
  134. {
  135. $arr = self::arr($ip);
  136. return $arr[3] ?? null;
  137. }
  138. /**
  139. * 获取城市码
  140. * @param null $ip
  141. * @return string
  142. */
  143. public static function citycode($ip = null)
  144. {
  145. $arr = self::arr($ip);
  146. return $arr[5] ?? null;
  147. }
  148. /**
  149. * 获取Isp
  150. * @param null $ip
  151. * @return string
  152. */
  153. public static function isp($ip = null)
  154. {
  155. $arr = self::arr($ip);
  156. return $arr[4] ?? null;
  157. }
  158. /**
  159. * all the db binary string will be loaded into memory
  160. * then search the memory only and this will a lot faster than disk base search
  161. * @Note:
  162. * invoke it once before put it to public invoke could make it thread safe
  163. *
  164. * @param $ip
  165. * @return string
  166. */
  167. private static function memorySearch($ip)
  168. {
  169. //check and load the binary string for the first time
  170. if (self::$dbBinStr == null) {
  171. $dbFile = ROOT_PATH . 'public/assets/data/ip2region.db';
  172. self::$dbBinStr = file_get_contents($dbFile);
  173. if (self::$dbBinStr == false) {
  174. throw new Exception("Fail to open the db file {$dbFile}");
  175. }
  176. self::$firstIndexPtr = self::getLong(self::$dbBinStr, 0);
  177. self::$lastIndexPtr = self::getLong(self::$dbBinStr, 4);
  178. self::$totalBlocks = (self::$lastIndexPtr - self::$firstIndexPtr) / INDEX_BLOCK_LENGTH + 1;
  179. }
  180. if (is_string($ip)) {
  181. $ip = self::safeIp2long($ip);
  182. }
  183. //binary search to define the data
  184. $l = 0;
  185. $h = self::$totalBlocks;
  186. $dataPtr = 0;
  187. while ($l <= $h) {
  188. $m = (($l + $h) >> 1);
  189. $p = self::$firstIndexPtr + $m * INDEX_BLOCK_LENGTH;
  190. $sip = self::getLong(self::$dbBinStr, $p);
  191. if ($ip < $sip) {
  192. $h = $m - 1;
  193. } else {
  194. $eip = self::getLong(self::$dbBinStr, $p + 4);
  195. if ($ip > $eip) {
  196. $l = $m + 1;
  197. } else {
  198. $dataPtr = self::getLong(self::$dbBinStr, $p + 8);
  199. break;
  200. }
  201. }
  202. }
  203. //not matched just stop it here
  204. if ($dataPtr == 0) {
  205. return null;
  206. }
  207. //get the data
  208. $dataLen = (($dataPtr >> 24) & 0xFF);
  209. $dataPtr = ($dataPtr & 0x00FFFFFF);
  210. return substr(self::$dbBinStr, $dataPtr + 4, $dataLen - 4).'|'.self::getLong(self::$dbBinStr, $dataPtr);
  211. }
  212. /**
  213. * safe self::safeIp2long function
  214. *
  215. * @param ip
  216. * */
  217. private static function safeIp2long($ip)
  218. {
  219. $ip = ip2long($ip);
  220. // convert signed int to unsigned int if on 32 bit operating system
  221. if ($ip < 0 && PHP_INT_SIZE == 4) {
  222. $ip = sprintf("%u", $ip);
  223. }
  224. return $ip;
  225. }
  226. /**
  227. * read a long from a byte buffer
  228. *
  229. * @param b
  230. * @param offset
  231. */
  232. private static function getLong($b, $offset)
  233. {
  234. $val = (
  235. (ord($b[$offset++])) |
  236. (ord($b[$offset++]) << 8) |
  237. (ord($b[$offset++]) << 16) |
  238. (ord($b[$offset]) << 24)
  239. );
  240. // convert signed int to unsigned int if on 32 bit operating system
  241. if ($val < 0 && PHP_INT_SIZE == 4) {
  242. $val = sprintf("%u", $val);
  243. }
  244. return $val;
  245. }
  246. /**
  247. * destruct method, resource destroy
  248. */
  249. public function __destruct()
  250. {
  251. if (self::$dbFileHandler != null) {
  252. fclose(self::$dbFileHandler);
  253. }
  254. self::$dbBinStr = null;
  255. self::$HeaderSip = null;
  256. self::$HeaderPtr = null;
  257. }
  258. }