IP.php 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. <?php
  2. namespace App\Components;
  3. use Exception;
  4. use GeoIp2\Database\Reader;
  5. use GeoIp2\Exception\AddressNotFoundException;
  6. use GuzzleHttp\Client;
  7. use IP2Location\Database;
  8. use Ip2Region;
  9. use ipip\db\City;
  10. use Log;
  11. use MaxMind\Db\Reader\InvalidDatabaseException;
  12. class IP
  13. {
  14. // 获取IP地址信息
  15. public static function getIPInfo($ip)
  16. {
  17. // IPv6 推荐使用ip.sb
  18. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
  19. Log::info('识别到IPv6,尝试解析:' . $ip);
  20. $ipInfo = IP::IPSB($ip);
  21. } else {
  22. $ipInfo = IP::ip2Region($ip);
  23. if ( ! $ipInfo) {
  24. Log::info('无法识别,尝试使用【IPIP库】库解析:' . $ip);
  25. $ipInfo = IP::ip2Location($ip);
  26. }
  27. }
  28. return $ipInfo;
  29. }
  30. // 通过api.ip.sb查询IP地址的详细信息
  31. public static function IPSB($ip)
  32. {
  33. $request = (new Client(['timeout' => 15]))
  34. ->get('https://api.ip.sb/geoip/' . $ip);
  35. $message = json_decode($request->getBody(), true);
  36. if ($request->getStatusCode() == 200) {
  37. return $message;
  38. }
  39. Log::error('解析IPv6异常:' . $ip . PHP_EOL . var_export($request, true));
  40. return false;
  41. }
  42. // 通过ip2Region查询IP地址的详细信息 ← 聚合 淘宝IP库,GeoIP,纯真IP库
  43. public static function ip2Region(string $ip)
  44. {
  45. $ipInfo = false;
  46. try {
  47. $ipInfo = (new Ip2Region())->memorySearch($ip);
  48. } catch (Exception $e) {
  49. Log::error('【淘宝IP库】错误信息:' . $e->getMessage());
  50. }
  51. if ($ipInfo) {
  52. $location = explode("|", $ipInfo['region']);
  53. return [
  54. 'country' => $location[0] ?: '',
  55. 'province' => $location[2] ?: '',
  56. 'city' => $location[3] ?: '',
  57. ];
  58. }
  59. return $ipInfo;
  60. }
  61. //// 通过ip2Location查询IP地址的详细信息
  62. public static function ip2Location(string $ip)
  63. {
  64. $filePath = database_path('IP2LOCATION-LITE-DB3.IPV6.BIN');
  65. try {
  66. $location = (new Database($filePath, Database::FILE_IO))
  67. ->lookup(
  68. $ip,
  69. [
  70. Database::CITY_NAME,
  71. Database::REGION_NAME,
  72. Database::COUNTRY_NAME,
  73. ]
  74. );
  75. return [
  76. 'country' => $location['countryName'],
  77. 'province' => $location['regionName'],
  78. 'city' => $location['cityName'],
  79. ];
  80. } catch (Exception $e) {
  81. Log::error('【ip2Location】错误信息:' . $e->getMessage());
  82. }
  83. return false;
  84. }
  85. // 通过IPIP查询IP地址的详细信息
  86. public static function IPIP(string $ip): array
  87. {
  88. $filePath = database_path('ipip.ipdb');
  89. $location = (new City($filePath))->findMap($ip, 'CN');
  90. return [
  91. 'country' => $location['country_name'],
  92. 'province' => $location['region_name'],
  93. 'city' => $location['city_name'],
  94. ];
  95. }
  96. // 通过ip.taobao.com查询IP地址的详细信息
  97. public static function TaoBao(string $ip)
  98. {
  99. // 依据 http://ip.taobao.com/instructions 开发
  100. $request = (new Client(['timeout' => 15]))
  101. ->get(
  102. 'http://ip.taobao.com/outGetIpInfo?ip=' . $ip . '&accessKey=alibaba-inc'
  103. );
  104. $message = json_decode($request->getBody(), true);
  105. if ($request->getStatusCode() == 200) {
  106. if ($message['code'] === 0) {
  107. return [
  108. 'country' => $message['data']['country'] === "XX" ? '' : $message['data']['country'],
  109. 'province' => $message['data']['region'] === "XX" ? '' : $message['data']['region'],
  110. 'city' => $message['data']['city'] === "XX" ? '' : $message['data']['city'],
  111. ];
  112. }
  113. Log::error(
  114. '【淘宝IP库】返回错误信息:' . $ip . PHP_EOL . var_export(
  115. $message['msg'],
  116. true
  117. )
  118. );
  119. } else {
  120. Log::error(
  121. '【淘宝IP库】解析异常:' . $ip . PHP_EOL . var_export($request, true)
  122. );
  123. }
  124. return false;
  125. }
  126. // 通过api.map.baidu.com查询IP地址的详细信息
  127. public static function Baidu(string $ip)
  128. {
  129. if ( ! env('BAIDU_APP_AK')) {
  130. Log::error('【百度IP库】AK信息缺失');
  131. return false;
  132. }
  133. // 依据 http://lbsyun.baidu.com/index.php?title=webapi/ip-api 开发
  134. $request = (new Client(['timeout' => 15]))
  135. ->get(
  136. 'https://api.map.baidu.com/location/ip?ak=' . env(
  137. 'BAIDU_APP_AK'
  138. ) . '&' . $ip . '&coor=bd09ll'
  139. );
  140. $message = json_decode($request->getBody(), true);
  141. if ($request->getStatusCode() == 200) {
  142. if ($message['status'] === 0) {
  143. return [
  144. 'country' => $message['content']['address_detail']['country'],
  145. 'province' => $message['content']['address_detail']['province'],
  146. 'city' => $message['content']['address_detail']['city'],
  147. ];
  148. }
  149. Log::error(
  150. '【百度IP库】返回错误信息:' . $ip . PHP_EOL . var_export(
  151. $message['message'],
  152. true
  153. )
  154. );
  155. } else {
  156. Log::error(
  157. '【百度IP库】解析异常:' . $ip . PHP_EOL . var_export($request, true)
  158. );
  159. }
  160. return false;
  161. }
  162. // 通过GeoIP2查询IP地址的详细信息
  163. public static function GeoIP2(string $ip)
  164. {
  165. $filePath = database_path('maxmind.mmdb');
  166. try {
  167. $location = (new Reader($filePath))->city($ip);
  168. return [
  169. 'country' => $location->country->names['zh-CN'],
  170. 'province' => '',
  171. 'city' => $location->city->name ?? '',
  172. ];
  173. } catch (AddressNotFoundException $e) {
  174. Log::error('【GeoIP2】查询失败:' . $ip);
  175. } catch (InvalidDatabaseException $e) {
  176. Log::error('【GeoIP2】数据库无效:' . $ip);
  177. }
  178. return false;
  179. }
  180. // 获取访客真实IP
  181. public static function getClientIP()
  182. {
  183. /*
  184. * 访问时用localhost访问的,读出来的是“::1”是正常情况
  185. * ::1说明开启了IPv6支持,这是IPv6下的本地回环地址的表示
  186. * 使用IPv4地址访问或者关闭IPv6支持都可以不显示这个
  187. */
  188. if (isset($_SERVER)) {
  189. if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
  190. $_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
  191. $ip = $_SERVER['REMOTE_ADDR'];
  192. } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
  193. $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  194. } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
  195. $ip = $_SERVER['HTTP_CLIENT_IP'];
  196. } elseif (isset($_SERVER['REMOTE_ADDR'])) {
  197. $ip = $_SERVER['REMOTE_ADDR'];
  198. } else {
  199. $ip = 'unknown';
  200. }
  201. } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
  202. $ip = getenv('HTTP_X_FORWARDED_FOR');
  203. } elseif (getenv('HTTP_CLIENT_IP')) {
  204. $ip = getenv('HTTP_CLIENT_IP');
  205. } else {
  206. $ip = getenv('REMOTE_ADDR');
  207. }
  208. if (trim($ip) === '::1') {
  209. $ip = '127.0.0.1';
  210. }
  211. return $ip;
  212. }
  213. }