IP.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. <?php
  2. namespace App\Utils;
  3. use Arr;
  4. use Cache;
  5. use Exception;
  6. use GeoIp2\Database\Reader;
  7. use GeoIp2\Exception\AddressNotFoundException;
  8. use Http;
  9. use Illuminate\Http\Client\PendingRequest;
  10. use IP2Location\Database;
  11. use ipip\db\City;
  12. use Log;
  13. use MaxMind\Db\Reader\InvalidDatabaseException;
  14. use XdbSearcher;
  15. use function request;
  16. class IP
  17. {
  18. private static bool $is_ipv4;
  19. private static string $ip;
  20. private static PendingRequest $basicRequest;
  21. public static function getClientIP(): ?string
  22. { // 获取访客真实IP
  23. return request()?->ip();
  24. }
  25. public static function getIPInfo(string $ip): array|null|false
  26. {// 获取IP地址信息
  27. $info = Cache::tags('IP_INFO')->get($ip);
  28. if (in_array($ip, ['::1', '127.0.0.1'], true)) {
  29. return false;
  30. }
  31. if ($info && ! empty(array_filter($info))) {
  32. return $info;
  33. }
  34. if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
  35. self::$is_ipv4 = true;
  36. } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
  37. self::$is_ipv4 = false;
  38. } else {
  39. return false;
  40. }
  41. self::$ip = $ip;
  42. self::$basicRequest = Http::timeout(10)->withOptions(['http_errors' => false])->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
  43. if (app()->getLocale() === 'zh_CN') {
  44. if (self::$is_ipv4) {
  45. $ret = self::IPLookup(['ipApi', 'Baidu', 'baiduBce', 'ipw', 'ipGeoLocation', 'TaoBao', 'speedtest', 'bjjii', 'TenAPI', 'fkcoder', 'vore', 'juHe', 'vvhan', 'ipjiance', 'ip2Region', 'IPDB']);
  46. } else {
  47. $ret = self::IPLookup(['ipApi', 'Baidu', 'baiduBce', 'ipw', 'ipGeoLocation', 'TenAPI', 'vore', 'ip2Region']);
  48. }
  49. } else {
  50. $ret = self::IPLookup(['ipApi', 'IPSB', 'ipinfo', 'ip234', 'ipGeoLocation', 'dbIP', 'IP2Online', 'ipdata', 'ipApiCo', 'ip2Location', 'GeoIP2', 'ipApiCom', 'akile']);
  51. }
  52. if ($ret !== null) {
  53. $ret['address'] = implode(' ', Arr::except(array_filter($ret), ['isp', 'latitude', 'longitude']));
  54. Cache::tags('IP_INFO')->put($ip, $ret, Day); // Store information for reduce API Calls
  55. }
  56. return $ret;
  57. }
  58. private static function IPLookup(array $checkers): ?array
  59. {
  60. foreach ($checkers as $checker) {
  61. try {
  62. if (method_exists(self::class, $checker)) {
  63. $result = self::$checker(self::$ip);
  64. if (is_array($result) && ! empty(array_filter($result))) {
  65. return $result;
  66. }
  67. }
  68. } catch (Exception $e) {
  69. Log::error("[$checker] IP信息获取报错: ".$e->getMessage());
  70. continue;
  71. }
  72. }
  73. return null;
  74. }
  75. public static function getIPGeo(string $ip): array|false
  76. {
  77. self::$ip = $ip;
  78. self::$basicRequest = Http::timeout(10)->withOptions(['http_errors' => false])->withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36');
  79. $ret = self::IPLookup(['IPSB', 'ipApi', 'baiduBce', 'ipw', 'ipinfo', 'IP2Online', 'speedtest', 'bjjii', 'Baidu', 'ip234', 'ipdata', 'ipGeoLocation', 'ipjiance', 'ipApiCo', 'ipApiCom', 'ip2Location', 'akile']);
  80. if (is_array($ret)) {
  81. return Arr::only($ret, ['latitude', 'longitude']);
  82. }
  83. return false;
  84. }
  85. private static function ipApi(string $ip): ?array
  86. { // 开发依据: https://ip-api.com/docs/api:json
  87. $key = config('services.ip.ip-api_key');
  88. if ($key) {
  89. $response = self::$basicRequest->withHeader('Origin', 'https://members.ip-api.com')->acceptJson()->get("https://pro.ip-api.com/json/$ip?fields=49881&key=$key&lang=".str_replace('_', '-', app()->getLocale()));
  90. if (! $response->ok()) {
  91. $response = self::$basicRequest->acceptJson()->get("http://ip-api.com/json/$ip?fields=49881&lang=".str_replace('_', '-', app()->getLocale()));
  92. }
  93. } else {
  94. $response = self::$basicRequest->acceptJson()->get("http://ip-api.com/json/$ip?fields=49881&lang=".str_replace('_', '-', app()->getLocale()));
  95. }
  96. if ($response->ok()) {
  97. $data = $response->json();
  98. if ($data['status'] === 'success') {
  99. return [
  100. 'country' => $data['country'],
  101. 'region' => $data['regionName'],
  102. 'city' => $data['city'],
  103. 'isp' => $data['isp'],
  104. 'area' => null,
  105. 'latitude' => $data['lat'],
  106. 'longitude' => $data['lon'],
  107. ];
  108. }
  109. Log::error('【ip-api.com】ip查询失败:'.$data['message'] ?? '');
  110. } else {
  111. Log::error('【ip-api.com】查询无效:'.$ip);
  112. }
  113. return null;
  114. }
  115. private static function Baidu(string $ip): ?array
  116. {// 通过api.map.baidu.com查询IP地址的详细信息
  117. $key = config('services.ip.baidu_ak');
  118. if ($key) {
  119. // 依据 http://lbsyun.baidu.com/index.php?title=webapi/ip-api 开发
  120. $response = self::$basicRequest->get("https://api.map.baidu.com/location/ip?ak=$key&ip=$ip&coor=gcj02");
  121. if ($response->ok()) {
  122. $message = $response->json();
  123. if ($message['status'] === 0) {
  124. $location = explode('|', $message['address']);
  125. return [
  126. 'country' => $location[0],
  127. 'region' => $message['content']['address_detail']['province'],
  128. 'city' => $message['content']['address_detail']['city'],
  129. 'isp' => $location[4],
  130. 'area' => $message['content']['address_detail']['street'],
  131. 'latitude' => $message['content']['y'],
  132. 'longitude' => $message['content']['x'],
  133. ];
  134. }
  135. Log::warning('【百度IP库】返回错误信息:'.$ip.PHP_EOL.var_export($message, true));
  136. } else {
  137. Log::error('【百度IP库】解析异常:'.$ip);
  138. }
  139. }
  140. return null;
  141. }
  142. private static function baiduBce(string $ip): ?array
  143. {
  144. if (self::$is_ipv4) {
  145. $url = "https://qifu-api.baidubce.com/ip/geo/v1/district?ip=$ip";
  146. } else {
  147. $url = "https://qifu-api.baidubce.com/ip/geo/v1/ipv6/district?ip=$ip";
  148. }
  149. $response = self::$basicRequest->get($url);
  150. $data = $response->json();
  151. if ($response->ok()) {
  152. if ($data['code'] === 'Success' && $ip === $data['ip']) {
  153. return [
  154. 'country' => $data['data']['country'],
  155. 'region' => $data['data']['prov'],
  156. 'city' => $data['data']['city'],
  157. 'isp' => $data['data']['isp'],
  158. 'area' => $data['data']['district'],
  159. 'latitude' => $data['data']['lat'],
  160. 'longitude' => $data['data']['lng'],
  161. ];
  162. }
  163. Log::error('【baiduBce】IP查询失败:'.$data['msg'] ?? '');
  164. } else {
  165. Log::error('【baiduBce】查询无效:'.$ip.var_export($data, true));
  166. }
  167. return null;
  168. }
  169. private static function ipGeoLocation(string $ip): ?array
  170. { // 开发依据: https://ipgeolocation.io/documentation.html
  171. $response = self::$basicRequest->withHeader('Origin', 'https://ipgeolocation.io')->get("https://api.ipgeolocation.io/ipgeo?ip=$ip&fields=country_name,state_prov,district,city,isp,latitude,longitude&lang=".config('common.language.'.app()->getLocale().'.1'));
  172. if ($response->ok()) {
  173. $data = $response->json();
  174. return [
  175. 'country' => $data['country_name'],
  176. 'region' => $data['state_prov'],
  177. 'city' => $data['city'],
  178. 'isp' => $data['isp'],
  179. 'area' => $data['district'],
  180. 'latitude' => $data['latitude'],
  181. 'longitude' => $data['longitude'],
  182. ];
  183. }
  184. return null;
  185. }
  186. private static function TaoBao(string $ip): ?array
  187. { // 通过ip.taobao.com查询IP地址的详细信息 依据 https://ip.taobao.com/instructions 开发
  188. $response = self::$basicRequest->retry(2)->post("https://ip.taobao.com/outGetIpInfo?ip=$ip&accessKey=alibaba-inc");
  189. $message = $response->json();
  190. if ($response->ok()) {
  191. $data = $message['data'];
  192. if ($message['code'] === 0 && $data['ip'] === $ip) {
  193. return [
  194. 'country' => strtolower($data['country']) !== 'xx' ?: null,
  195. 'region' => strtolower($data['region']) !== 'xx' ?: null,
  196. 'city' => strtolower($data['city']) !== 'xx' ?: null,
  197. 'isp' => strtolower($data['isp']) !== 'xx' ?: null,
  198. 'area' => strtolower($data['area']) !== 'xx' ?: null,
  199. ];
  200. }
  201. Log::warning('【淘宝IP库】返回错误信息:'.$ip.PHP_EOL.$message['msg']);
  202. } else {
  203. Log::error('【淘宝IP库】解析异常:'.$ip);
  204. }
  205. return null;
  206. }
  207. private static function speedtest(string $ip): ?array
  208. {
  209. $response = self::$basicRequest->get("https://forge.speedtest.cn/api/location/info?ip=$ip");
  210. $data = $response->json();
  211. if ($response->ok()) {
  212. if ($data['ip'] === $ip) {
  213. return [
  214. 'country' => $data['country'],
  215. 'region' => $data['province'],
  216. 'city' => $data['city'],
  217. 'isp' => $data['isp'],
  218. 'area' => $data['distinct'],
  219. 'latitude' => $data['lat'],
  220. 'longitude' => $data['lon'],
  221. ];
  222. }
  223. Log::error('【speedtest】IP查询失败');
  224. } else {
  225. Log::error('【speedtest】查询无效:'.$ip.var_export($data, true));
  226. }
  227. return null;
  228. }
  229. private static function TenAPI(string $ip): ?array
  230. { // 开发依据: https://docs.tenapi.cn/utility/getip.html
  231. $response = self::$basicRequest->asForm()->post('https://tenapi.cn/v2/getip', ['ip' => $ip]);
  232. if ($response->ok()) {
  233. $data = $response->json();
  234. if ($data['code'] === 200 && $data['data']['ip'] === $ip) {
  235. return [
  236. 'country' => $data['data']['country'],
  237. 'region' => $data['data']['province'],
  238. 'city' => $data['data']['city'],
  239. 'isp' => $data['data']['isp'],
  240. 'area' => null,
  241. ];
  242. }
  243. }
  244. return null;
  245. }
  246. private static function fkcoder(string $ip): ?array
  247. { // 开发依据: https://www.fkcoder.com/
  248. $response = self::$basicRequest->acceptJson()->get("https://www.fkcoder.com/ip?ip=$ip");
  249. if ($response->ok()) {
  250. $data = $response->json();
  251. return [
  252. 'country' => $data['country'],
  253. 'region' => $data['province'] ?: ($data['region'] ?: null),
  254. 'city' => $data['city'],
  255. 'isp' => $data['isp'],
  256. 'area' => null,
  257. ];
  258. }
  259. return null;
  260. }
  261. private static function juHe(string $ip): ?array
  262. { // 开发依据: https://www.juhe.cn/docs/api/id/1
  263. $response = self::$basicRequest->asForm()->post('https://apis.juhe.cn/ip/Example/query.php', ['IP' => $ip]);
  264. if ($response->ok()) {
  265. $data = $response->json();
  266. if ($data['resultcode'] === '200' && $data['error_code'] === 0) {
  267. return [
  268. 'country' => $data['result']['Country'],
  269. 'region' => $data['result']['Province'],
  270. 'city' => $data['result']['City'],
  271. 'isp' => $data['result']['Isp'],
  272. 'area' => $data['result']['District'],
  273. ];
  274. }
  275. }
  276. return null;
  277. }
  278. private static function ip2Region(string $ip): ?array
  279. { // 通过ip2Region查询IP地址的详细信息 数据库不经常更新
  280. try {
  281. $data = (new XdbSearcher)->search($ip);
  282. } catch (Exception $e) {
  283. Log::error('【ip2Region】错误信息:'.$e->getMessage());
  284. }
  285. if (! empty($data)) {
  286. $location = explode('|', $data);
  287. if ($location) {
  288. return [
  289. 'country' => $location[0],
  290. 'region' => $location[2],
  291. 'city' => $location[3],
  292. 'isp' => $location[4],
  293. 'area' => $location[1],
  294. ];
  295. }
  296. }
  297. return null;
  298. }
  299. private static function IPDB(string $ip): array
  300. { // 通过IPDB格式的离线数据查询IP地址的详细信息
  301. $filePath = database_path('qqwry.ipdb'); // 来源: https://github.com/metowolf/qqwry.ipdb
  302. $location = (new City($filePath))->findMap($ip, 'CN');
  303. return [
  304. 'country' => $location['country_name'],
  305. 'region' => $location['region_name'],
  306. 'city' => $location['city_name'],
  307. 'isp' => $location['isp_domain'],
  308. 'area' => null,
  309. ];
  310. }
  311. private static function ipjiance(string $ip): ?array
  312. {
  313. $response = self::$basicRequest->get("https://www.ipjiance.com/api/geoip/report?ip=$ip");
  314. $data = $response->json();
  315. if ($response->ok()) {
  316. if ($data['code'] === 1) {
  317. return [
  318. 'country' => $data['data']['country'],
  319. 'region' => null,
  320. 'city' => $data['data']['city'],
  321. 'isp' => $data['data']['isp'],
  322. 'area' => null,
  323. 'latitude' => $data['data']['latitude'],
  324. 'longitude' => $data['data']['longitude'],
  325. ];
  326. }
  327. Log::error('【ipjiance】IP查询失败:'.$data['msg'] ?? '');
  328. } else {
  329. Log::error('【ipjiance】查询无效:'.$ip.var_export($data, true));
  330. }
  331. return null;
  332. }
  333. private static function IPSB(string $ip): ?array
  334. { // 通过api.ip.sb查询IP地址的详细信息
  335. try {
  336. $response = self::$basicRequest->post("https://api.ip.sb/geoip/$ip");
  337. if ($response->ok()) {
  338. $data = $response->json();
  339. if ($data) {
  340. $ret = Arr::only($data, ['country', 'region', 'city', 'isp', 'latitude', 'longitude']);
  341. return Arr::prepend(['area' => null], $ret);
  342. }
  343. }
  344. Log::warning('[IPSB] 解析'.$ip.'异常: '.$response->body());
  345. } catch (Exception $e) {
  346. Log::error('[IPSB] 解析'.$ip.'错误: '.var_export($e->getMessage(), true));
  347. }
  348. return null;
  349. }
  350. private static function ipinfo(string $ip): ?array
  351. { // 开发依据: https://ipinfo.io/account/home
  352. $key = config('services.ip.ipinfo_token');
  353. if ($key) {
  354. $response = self::$basicRequest->acceptJson()->get("https://ipinfo.io/$ip?token=$key");
  355. } else {
  356. $response = self::$basicRequest->acceptJson()->withHeader('Referer', 'https://ipinfo.io/')->get("https://ipinfo.io/widget/demo/$ip");
  357. }
  358. if ($response->ok()) {
  359. $data = $key ? $response->json() : $response->json()['data'];
  360. $location = explode(',', $data['loc']);
  361. return [
  362. 'country' => $data['country'],
  363. 'region' => $data['region'],
  364. 'city' => $data['city'],
  365. 'isp' => $data['org'],
  366. 'area' => null,
  367. 'latitude' => $location[0],
  368. 'longitude' => $location[1],
  369. ];
  370. }
  371. return null;
  372. }
  373. private static function ip234(string $ip): ?array
  374. {
  375. $response = self::$basicRequest->get("https://ip234.in/search_ip?ip=$ip");
  376. $data = $response->json();
  377. if ($response->ok()) {
  378. if ($data['code'] === 0) {
  379. return [
  380. 'country' => $data['data']['country'],
  381. 'region' => $data['data']['region'],
  382. 'city' => $data['data']['city'],
  383. 'isp' => $data['data']['organization'],
  384. 'area' => null,
  385. 'latitude' => $data['data']['latitude'],
  386. 'longitude' => $data['data']['longitude'],
  387. ];
  388. }
  389. Log::error('【ip234】IP查询失败:'.$data['msg'] ?? '');
  390. } else {
  391. Log::error('【ip234】查询无效:'.$ip.var_export($data, true));
  392. }
  393. return null;
  394. }
  395. private static function dbIP(string $ip): ?array
  396. { // 开发依据: https://db-ip.com/api/doc.php
  397. $response = self::$basicRequest->acceptJson()->get("https://api.db-ip.com/v2/free/$ip");
  398. if ($response->ok()) {
  399. $data = $response->json();
  400. return [
  401. 'country' => $data['countryName'],
  402. 'region' => $data['stateProv'],
  403. 'city' => $data['city'],
  404. 'isp' => null,
  405. 'area' => null,
  406. ];
  407. }
  408. return null;
  409. }
  410. private static function IP2Online(string $ip): ?array
  411. { // 开发依据: https://www.ip2location.io/ip2location-documentation
  412. $key = config('services.ip.IP2Location_key');
  413. if ($key) {
  414. $response = self::$basicRequest->acceptJson()->get("https://api.ip2location.io/?key=$key&ip=$ip");
  415. if ($response->ok()) {
  416. $data = $response->json();
  417. return [
  418. 'country' => $data['country_name'],
  419. 'region' => $data['region_name'],
  420. 'city' => $data['city_name'],
  421. 'isp' => $data['as'],
  422. 'area' => null,
  423. 'latitude' => $data['latitude'],
  424. 'longitude' => $data['longitude'],
  425. ];
  426. }
  427. }
  428. return null;
  429. }
  430. private static function ipdata(string $ip): ?array
  431. { // 开发依据: https://docs.ipdata.co/docs
  432. $key = config('services.ip.ipdata_key');
  433. if ($key) {
  434. $response = self::$basicRequest->get("https://api.ipdata.co/$ip?api-key=$key&fields=ip,city,region,country_name,latitude,longitude,asn");
  435. if ($response->ok()) {
  436. $data = $response->json();
  437. return [
  438. 'country' => $data['country_name'],
  439. 'region' => $data['region'],
  440. 'city' => $data['city'],
  441. 'isp' => $data['asn']['name'],
  442. 'area' => null,
  443. 'latitude' => $data['latitude'],
  444. 'longitude' => $data['longitude'],
  445. ];
  446. }
  447. }
  448. return null;
  449. }
  450. private static function ipApiCo(string $ip): ?array
  451. { // 开发依据: https://ipapi.co/api/
  452. $response = self::$basicRequest->get("https://ipapi.co/$ip/json/");
  453. if ($response->ok()) {
  454. $data = $response->json();
  455. return [
  456. 'country' => $data['country_name'],
  457. 'region' => $data['region'],
  458. 'city' => $data['city'],
  459. 'isp' => $data['org'],
  460. 'area' => null,
  461. 'latitude' => $data['latitude'],
  462. 'longitude' => $data['longitude'],
  463. ];
  464. }
  465. return null;
  466. }
  467. private static function ip2Location(string $ip): ?array
  468. { // 通过ip2Location查询IP地址的详细信息
  469. $filePath = database_path('IP2LOCATION-LITE-DB11.IPV6.BIN'); // 来源: https://lite.ip2location.com/database-download
  470. try {
  471. $location = (new Database($filePath, Database::FILE_IO))->lookup($ip, [Database::CITY_NAME, Database::REGION_NAME, Database::COUNTRY_NAME, Database::LATITUDE, Database::LONGITUDE]);
  472. return [
  473. 'country' => $location['countryName'],
  474. 'region' => $location['regionName'],
  475. 'city' => $location['cityName'],
  476. 'isp' => null,
  477. 'area' => null,
  478. 'latitude' => $location['latitude'],
  479. 'longitude' => $location['longitude'],
  480. ];
  481. } catch (Exception $e) {
  482. Log::error('【ip2Location】错误信息:'.$e->getMessage());
  483. }
  484. return null;
  485. }
  486. private static function GeoIP2(string $ip): ?array
  487. {// 通过GeoIP2查询IP地址的详细信息
  488. $filePath = database_path('GeoLite2-City.mmdb'); // 来源:https://github.com/PrxyHunter/GeoLite2/releases
  489. try {
  490. $location = (new Reader($filePath))->city($ip);
  491. return [
  492. 'country' => $location->country->name,
  493. 'region' => $location->mostSpecificSubdivision->name,
  494. 'city' => $location->city->name,
  495. 'isp' => null,
  496. 'area' => null,
  497. ];
  498. } catch (AddressNotFoundException $e) {
  499. Log::error("【GeoIP2】查询失败:$ip ".$e->getMessage());
  500. } catch (InvalidDatabaseException $e) {
  501. Log::error("【GeoIP2】数据库无效:$ip ".$e->getMessage());
  502. }
  503. return null;
  504. }
  505. private static function ipApiCom(string $ip): ?array
  506. { // 开发依据: https://docs.ipdata.co/docs
  507. $response = self::$basicRequest->get("https://ipapi.com/ip_api.php?ip=$ip");
  508. if ($response->ok()) {
  509. $data = $response->json();
  510. return [
  511. 'country' => $data['country_name'],
  512. 'region' => $data['region_name'],
  513. 'city' => $data['city'],
  514. 'isp' => $data['connection']['isp'],
  515. 'area' => null,
  516. 'latitude' => $data['latitude'],
  517. 'longitude' => $data['longitude'],
  518. ];
  519. }
  520. return null;
  521. }
  522. private static function vore(string $ip): ?array
  523. { // 开发依据: https://api.vore.top/
  524. $response = self::$basicRequest->get("https://api.vore.top/api/IPdata?ip=$ip");
  525. if ($response->ok()) {
  526. $data = $response->json();
  527. if ($data['code'] === 200) {
  528. return [
  529. 'country' => $data['ipdata']['info1'],
  530. 'region' => $data['ipdata']['info2'],
  531. 'city' => $data['ipdata']['info3'],
  532. 'isp' => $data['ipdata']['isp'],
  533. 'area' => null,
  534. ];
  535. }
  536. }
  537. return null;
  538. }
  539. private static function vvhan(string $ip): ?array
  540. {
  541. $response = self::$basicRequest->get("https://api.vvhan.com/api/getIpInfo?ip=$ip");
  542. if ($response->ok()) {
  543. $data = $response->json();
  544. if ($data['success'] && $data['ip'] === $ip) {
  545. return [
  546. 'country' => $data['info']['country'],
  547. 'region' => $data['info']['prov'],
  548. 'city' => $data['info']['city'],
  549. 'isp' => $data['info']['isp'],
  550. 'area' => null,
  551. ];
  552. }
  553. }
  554. return null;
  555. }
  556. private static function ipw(string $ip): ?array
  557. { // 开发依据: https://api.vore.top/
  558. if (self::$is_ipv4) {
  559. $response = self::$basicRequest->asForm()->withHeader('Referer', 'https://ipw.cn/')->post('https://rest.ipw.cn/api/ip/queryThird',
  560. ['ip' => $ip, 'param1' => '33546680dcec944422ee9fea64ced0fb6', 'param2' => '5ac8d31b5b3434350048af37a497a9']);
  561. } else {
  562. $response = self::$basicRequest->asForm()->withHeader('Referer', 'https://ipw.cn/')->get("https://rest.ipw.cn/api/aw/v1/ipv6?ip=$ip&warning=1");
  563. }
  564. if ($response->ok()) {
  565. $data = $response->json();
  566. if (self::$is_ipv4) {
  567. if ($data['result'] && $data['Result']['code'] === 'Success' && $data['Result']['ip'] === $ip) {
  568. $data = $data['Result']['data'];
  569. return [
  570. 'country' => $data['country'],
  571. 'region' => $data['prov'],
  572. 'city' => $data['city'],
  573. 'isp' => $data['isp'],
  574. 'area' => $data['district'],
  575. 'latitude' => $data['lat'],
  576. 'longitude' => $data['lng'],
  577. ];
  578. }
  579. } elseif ($data['code'] === 'Success' && $data['ip'] === $ip) {
  580. $data = $data['data'];
  581. return [
  582. 'country' => $data['country'],
  583. 'region' => $data['prov'],
  584. 'city' => $data['city'],
  585. 'isp' => $data['isp'],
  586. 'area' => $data['district'],
  587. 'latitude' => $data['lat'],
  588. 'longitude' => $data['lng'],
  589. ];
  590. }
  591. }
  592. return null;
  593. }
  594. private static function bjjii(string $ip): ?array
  595. { // 开发依据: https://api.bjjii.com/doc/77
  596. $key = config('services.ip.bjjii_key');
  597. if ($key) {
  598. $response = self::$basicRequest->get("https://api.bjjii.com/api/ip/query?key=$key&ip=$ip");
  599. if ($response->ok()) {
  600. $data = $response->json();
  601. if ($data['code'] === 200 && $data['data']['ip'] === $ip) {
  602. $data = $data['data']['info'];
  603. return [
  604. 'country' => $data['nation'],
  605. 'region' => $data['province'],
  606. 'city' => $data['city'],
  607. 'isp' => $data['isp'],
  608. 'area' => $data['district'],
  609. 'latitude' => $data['lat'],
  610. 'longitude' => $data['lng'],
  611. ];
  612. }
  613. }
  614. }
  615. return null;
  616. }
  617. private static function akile(string $ip): ?array
  618. {
  619. $response = self::$basicRequest->get("https://tools.akile.io/api/location/$ip");
  620. $data = $response->json();
  621. if ($response->ok()) {
  622. $data = $data['data'];
  623. if ($data['ip'] === $ip) {
  624. $location = explode(',', $data['loc']);
  625. return [
  626. 'country' => $data['country'],
  627. 'region' => $data['region'],
  628. 'city' => $data['city'],
  629. 'isp' => $data['org'],
  630. 'area' => null,
  631. 'latitude' => $location[0] ?? null,
  632. 'longitude' => $location[1] ?? null,
  633. ];
  634. }
  635. Log::error('【akile】IP查询失败:'.$data['msg'] ?? '');
  636. } else {
  637. Log::error('【akile】查询无效:'.$ip.var_export($data, true));
  638. }
  639. return null;
  640. }
  641. private static function cz88(string $ip): ?array
  642. {
  643. $response = self::$basicRequest->get("https://www.cz88.net/api/cz88/ip/base?ip=$ip");
  644. if ($response->ok()) {
  645. $data = $response->json();
  646. if ($data['success'] && $data['data']['ip'] === $ip) {
  647. $data = $data['data'];
  648. $location = $data['locations'] ? $data['locations'][0] : null;
  649. return [
  650. 'country' => $data['country'],
  651. 'region' => $data['province'],
  652. 'city' => $data['city'],
  653. 'isp' => $data['isp'],
  654. 'area' => $data['districts'],
  655. 'latitude' => $location ? $location['latitude'] : null,
  656. 'longitude' => $location ? $location['longitude'] : null,
  657. ];
  658. }
  659. }
  660. return null;
  661. }
  662. private static function userAgentInfo(string $ip): ?array
  663. { // 开发依据: https://ip.useragentinfo.com/api 无法查外网的ip
  664. if (self::$is_ipv4) {
  665. $response = self::$basicRequest->withBody("ip:$ip")->get('https://ip.useragentinfo.com/json');
  666. } else {
  667. $response = self::$basicRequest->get("https://ip.useragentinfo.com/ipv6/$ip");
  668. }
  669. if ($response->ok()) {
  670. $data = $response->json();
  671. if ($data['code'] === 200 && $data['ip'] === $ip) {
  672. return [
  673. 'country' => $data['country'],
  674. 'region' => $data['province'],
  675. 'city' => $data['city'],
  676. 'isp' => $data['isp'],
  677. 'area' => $data['area'],
  678. ];
  679. }
  680. Log::error('【userAgentInfo】IP查询失败:'.$data ?? '');
  681. } else {
  682. Log::error('【userAgentInfo】查询无效:'.$ip);
  683. }
  684. return null;
  685. }
  686. }