IP.php 25 KB

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