| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683 |
- <?php
- namespace App\Utils;
- use Arr;
- use Cache;
- use Exception;
- use GeoIp2\Database\Reader;
- use GeoIp2\Exception\AddressNotFoundException;
- use Http;
- use Illuminate\Http\Client\PendingRequest;
- use IP2Location\Database;
- use ipip\db\City;
- use Log;
- use MaxMind\Db\Reader\InvalidDatabaseException;
- use XdbSearcher;
- use function request;
- class IP
- {
- private static bool $is_ipv4;
- public static function getClientIP(): string|null
- { // 获取访客真实IP
- return request()?->ip();
- }
- public static function getIPInfo(string $ip): array|null|false
- {// 获取IP地址信息
- $info = Cache::tags('IP_INFO')->get($ip);
- if (in_array($ip, ['::1', '127.0.0.1'], true)) {
- return false;
- }
- if ($info && ! empty(array_filter($info))) {
- return $info;
- }
- $ret = null;
- $source = 0;
- if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
- self::$is_ipv4 = true;
- } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
- self::$is_ipv4 = false;
- } else {
- return false;
- }
- if (app()->getLocale() === 'zh_CN') {
- if (self::$is_ipv4) {
- while ($source <= 11 && ($ret === null || (is_array($ret) && empty(array_filter($ret))))) { // 中文ipv6
- $ret = match ($source) {
- 0 => self::ipApi($ip),
- 1 => self::Baidu($ip),
- 2 => self::baiduBce($ip),
- 3 => self::ipGeoLocation($ip),
- 4 => self::TaoBao($ip),
- 5 => self::speedtest($ip),
- 6 => self::TenAPI($ip),
- 7 => self::fkcoder($ip),
- 8 => self::juHe($ip),
- 9 => self::ipjiance($ip),
- 10 => self::ip2Region($ip),
- 11 => self::IPIP($ip),
- //10 => self::userAgentInfo($ip), // 无法查外网的ip
- };
- $source++;
- }
- } else {
- while ($source <= 5 && ($ret === null || (is_array($ret) && empty(array_filter($ret))))) {
- $ret = match ($source) {
- 0 => self::ipApi($ip),
- 1 => self::baiduBce($ip),
- 2 => self::Baidu($ip),
- 3 => self::ipGeoLocation($ip),
- 4 => self::TenAPI($ip),
- 5 => self::ip2Region($ip),
- };
- $source++;
- }
- }
- } else {
- while ($source <= 11 && ($ret === null || (is_array($ret) && empty(array_filter($ret))))) { // 英文
- $ret = match ($source) {
- 0 => self::ipApi($ip),
- 1 => self::IPSB($ip),
- 2 => self::ipinfo($ip),
- 3 => self::ip234($ip),
- 4 => self::ipGeoLocation($ip),
- 5 => self::dbIP($ip),
- 6 => self::IP2Online($ip),
- 7 => self::ipdata($ip),
- 8 => self::ipApiCo($ip),
- 9 => self::ip2Location($ip),
- 10 => self::GeoIP2($ip),
- 11 => self::ipApiCom($ip),
- };
- $source++;
- }
- }
- if ($ret !== null) {
- $ret['address'] = implode(' ', Arr::except(array_filter($ret), ['isp', 'latitude', 'longitude']));
- Cache::tags('IP_INFO')->put($ip, $ret, Day); // Store information for reduce API Calls
- }
- return $ret;
- }
- private static function ipApi(string $ip): array|null
- { // 开发依据: https://ip-api.com/docs/api:json
- $key = config('services.ip.ip-api_key');
- if ($key) {
- $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()));
- if (! $response->ok()) {
- $response = self::setBasicHttp()->acceptJson()->get("http://ip-api.com/json/$ip?fields=49881&lang=".str_replace('_', '-', app()->getLocale()));
- }
- } else {
- $response = self::setBasicHttp()->acceptJson()->get("http://ip-api.com/json/$ip?fields=49881&lang=".str_replace('_', '-', app()->getLocale()));
- }
- if ($response->ok()) {
- $data = $response->json();
- if ($data['status'] === 'success') {
- return [
- 'country' => $data['country'],
- 'region' => $data['regionName'],
- 'city' => $data['city'],
- 'isp' => $data['isp'],
- 'area' => null,
- 'latitude' => $data['lat'],
- 'longitude' => $data['lon'],
- ];
- }
- Log::error('【ip-api.com】ip查询失败:'.$data['message'] ?? '');
- } else {
- Log::error('【ip-api.com】查询无效:'.$ip);
- }
- return null;
- }
- private static function setBasicHttp(): PendingRequest
- {
- 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');
- }
- private static function Baidu(string $ip): array|null
- {// 通过api.map.baidu.com查询IP地址的详细信息
- $key = config('services.ip.baidu_ak');
- if ($key) {
- // 依据 http://lbsyun.baidu.com/index.php?title=webapi/ip-api 开发
- $response = self::setBasicHttp()->get("https://api.map.baidu.com/location/ip?ak=$key&ip=$ip&coor=gcj02");
- if ($response->ok()) {
- $message = $response->json();
- if ($message['status'] === 0) {
- $location = explode('|', $message['address']);
- return [
- 'country' => $location[0],
- 'region' => $message['content']['address_detail']['province'],
- 'city' => $message['content']['address_detail']['city'],
- 'isp' => $location[4],
- 'area' => $message['content']['address_detail']['street'],
- 'latitude' => $message['content']['y'],
- 'longitude' => $message['content']['x'],
- ];
- }
- Log::warning('【百度IP库】返回错误信息:'.$ip.PHP_EOL.var_export($message, true));
- } else {
- Log::error('【百度IP库】解析异常:'.$ip);
- }
- }
- return null;
- }
- private static function baiduBce(string $ip): array|null
- {
- if (self::$is_ipv4) {
- $url = "https://qifu-api.baidubce.com/ip/geo/v1/district?ip=$ip";
- } else {
- $url = "https://qifu-api.baidubce.com/ip/geo/v1/ipv6/district?ip=$ip";
- }
- $response = self::setBasicHttp()->get($url);
- $data = $response->json();
- if ($response->ok()) {
- if ($data['code'] === 'Success' && $ip === $data['ip']) {
- return [
- 'country' => $data['data']['country'],
- 'region' => $data['data']['prov'],
- 'city' => $data['data']['city'],
- 'isp' => $data['data']['isp'],
- 'area' => $data['data']['district'],
- 'latitude' => $data['data']['lat'],
- 'longitude' => $data['data']['lng'],
- ];
- }
- Log::error('【baiduBce】IP查询失败:'.$data['msg'] ?? '');
- } else {
- Log::error('【baiduBce】查询无效:'.$ip.var_export($data, true));
- }
- return null;
- }
- private static function ipGeoLocation(string $ip): array|null
- { // 开发依据: https://ipgeolocation.io/documentation.html
- $response = self::setBasicHttp()->withHeaders(['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'));
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country_name'],
- 'region' => $data['state_prov'],
- 'city' => $data['city'],
- 'isp' => $data['isp'],
- 'area' => $data['district'],
- 'latitude' => $data['latitude'],
- 'longitude' => $data['longitude'],
- ];
- }
- return null;
- }
- private static function TaoBao(string $ip): array|null
- { // 通过ip.taobao.com查询IP地址的详细信息 依据 https://ip.taobao.com/instructions 开发
- $response = self::setBasicHttp()->retry(2)->post("https://ip.taobao.com/outGetIpInfo?ip=$ip&accessKey=alibaba-inc");
- $message = $response->json();
- if ($response->ok()) {
- $data = $message['data'];
- if ($message['code'] === 0 && $data['ip'] === $ip) {
- return [
- 'country' => 'xx' !== strtolower($data['country']) ?: null,
- 'region' => 'xx' !== strtolower($data['region']) ?: null,
- 'city' => 'xx' !== strtolower($data['city']) ?: null,
- 'isp' => 'xx' !== strtolower($data['isp']) ?: null,
- 'area' => 'xx' !== strtolower($data['area']) ?: null,
- ];
- }
- Log::warning('【淘宝IP库】返回错误信息:'.$ip.PHP_EOL.$message['msg']);
- } else {
- Log::error('【淘宝IP库】解析异常:'.$ip);
- }
- return null;
- }
- private static function speedtest(string $ip): array|null
- {
- $response = self::setBasicHttp()->get("https://forge.speedtest.cn/api/location/info?ip=$ip");
- $data = $response->json();
- if ($response->ok()) {
- if ($data['ip'] === $ip) {
- return [
- 'country' => $data['country'],
- 'region' => $data['province'],
- 'city' => $data['city'],
- 'isp' => $data['isp'],
- 'area' => $data['distinct'],
- 'latitude' => $data['lat'],
- 'longitude' => $data['lon'],
- ];
- }
- Log::error('【speedtest】IP查询失败');
- } else {
- Log::error('【speedtest】查询无效:'.$ip.var_export($data, true));
- }
- return null;
- }
- private static function TenAPI(string $ip): array|null
- { // 开发依据: https://docs.tenapi.cn/utility/getip.html
- $response = self::setBasicHttp()->asForm()->post('https://tenapi.cn/v2/getip', ['ip' => $ip]);
- if ($response->ok()) {
- $data = $response->json();
- if ($data['code'] === 200 && $data['data']['ip'] === $ip) {
- return [
- 'country' => $data['data']['country'],
- 'region' => $data['data']['province'],
- 'city' => $data['data']['city'],
- 'isp' => $data['data']['isp'],
- 'area' => '',
- ];
- }
- }
- return null;
- }
- private static function fkcoder(string $ip): array|null
- { // 开发依据: https://www.fkcoder.com/
- $response = self::setBasicHttp()->acceptJson()->get("https://www.fkcoder.com/ip?ip=$ip");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country'],
- 'region' => $data['province'] ?: ($data['region'] ?: null),
- 'city' => $data['city'],
- 'isp' => $data['isp'],
- 'area' => null,
- ];
- }
- return null;
- }
- private static function juHe(string $ip): array|null
- { // 开发依据: https://www.juhe.cn/docs/api/id/1
- $response = self::setBasicHttp()->asForm()->post('https://apis.juhe.cn/ip/Example/query.php', ['IP' => $ip]);
- if ($response->ok()) {
- $data = $response->json();
- if ($data['resultcode'] === '200' && $data['error_code'] === 0) {
- return [
- 'country' => $data['result']['Country'],
- 'region' => $data['result']['Province'],
- 'city' => $data['result']['City'],
- 'isp' => $data['result']['Isp'],
- 'area' => $data['result']['District'],
- ];
- }
- }
- return null;
- }
- private static function ip2Region(string $ip): array|null
- { // 通过ip2Region查询IP地址的详细信息 数据库不经常更新
- try {
- $data = (new XdbSearcher())->search($ip);
- } catch (Exception $e) {
- Log::error('【ip2Region】错误信息:'.$e->getMessage());
- }
- if (! empty($data)) {
- $location = explode('|', $data);
- if ($location) {
- return [
- 'country' => $location[0],
- 'region' => $location[2],
- 'city' => $location[3],
- 'isp' => $location[4],
- 'area' => $location[1],
- ];
- }
- }
- return null;
- }
- private static function IPIP(string $ip): array
- { // 通过IPIP离线数据查询IP地址的详细信息
- $filePath = database_path('ipipfree.ipdb'); // 来源: https://www.ipip.net/free_download/
- $location = (new City($filePath))->findMap($ip, 'CN');
- return [
- 'country' => $location['country_name'],
- 'region' => $location['region_name'],
- 'city' => $location['city_name'],
- 'isp' => null,
- 'area' => null,
- ];
- }
- private static function ipjiance(string $ip): array|null
- {
- $response = self::setBasicHttp()->get("https://www.ipjiance.com/api/geoip/report?ip=$ip");
- $data = $response->json();
- if ($response->ok()) {
- if ($data['code'] === 1) {
- return [
- 'country' => $data['data']['country'],
- 'region' => '',
- 'city' => $data['data']['city'],
- 'isp' => $data['data']['isp'],
- 'area' => '',
- 'latitude' => $data['data']['latitude'],
- 'longitude' => $data['data']['longitude'],
- ];
- }
- Log::error('【ipjiance】IP查询失败:'.$data['msg'] ?? '');
- } else {
- Log::error('【ipjiance】查询无效:'.$ip.var_export($data, true));
- }
- return null;
- }
- private static function IPSB(string $ip): array|null
- { // 通过api.ip.sb查询IP地址的详细信息
- try {
- $response = self::setBasicHttp()->post("https://api.ip.sb/geoip/$ip");
- if ($response->ok()) {
- $data = $response->json();
- if ($data) {
- $ret = Arr::only($data, ['country', 'region', 'city', 'isp', 'latitude', 'longitude']);
- return Arr::prepend(['area' => null], $ret);
- }
- }
- Log::warning('[IPSB] 解析'.$ip.'异常: '.$response->body());
- } catch (Exception $e) {
- Log::error('[IPSB] 解析'.$ip.'错误: '.var_export($e->getMessage(), true));
- }
- return null;
- }
- private static function ipinfo(string $ip): array|null
- { // 开发依据: https://ipinfo.io/account/home
- $key = config('services.ip.ipinfo_token');
- if ($key) {
- $response = self::setBasicHttp()->acceptJson()->get("https://ipinfo.io/$ip?token=$key");
- } else {
- $response = self::setBasicHttp()->acceptJson()->withHeaders(['Referer' => 'https://ipinfo.io/'])->get("https://ipinfo.io/widget/demo/$ip");
- }
- if ($response->ok()) {
- $data = $key ? $response->json() : $response->json()['data'];
- $location = explode(',', $data['loc']);
- return [
- 'country' => $data['country'],
- 'region' => $data['region'],
- 'city' => $data['city'],
- 'isp' => $data['org'],
- 'area' => null,
- 'latitude' => $location[0],
- 'longitude' => $location[1],
- ];
- }
- return null;
- }
- private static function ip234(string $ip): array|null
- {
- $response = self::setBasicHttp()->get("https://ip234.in/search_ip?ip=$ip");
- $data = $response->json();
- if ($response->ok()) {
- if ($data['code'] === 0) {
- return [
- 'country' => $data['data']['country'],
- 'region' => $data['data']['region'],
- 'city' => $data['data']['city'],
- 'isp' => $data['data']['organization'],
- 'area' => '',
- 'latitude' => $data['data']['latitude'],
- 'longitude' => $data['data']['longitude'],
- ];
- }
- Log::error('【ip234】IP查询失败:'.$data['msg'] ?? '');
- } else {
- Log::error('【ip234】查询无效:'.$ip.var_export($data, true));
- }
- return null;
- }
- private static function dbIP(string $ip): array|null
- { // 开发依据: https://db-ip.com/api/doc.php
- $response = self::setBasicHttp()->acceptJson()->get("https://api.db-ip.com/v2/free/$ip");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['countryName'],
- 'region' => $data['stateProv'],
- 'city' => $data['city'],
- 'isp' => null,
- 'area' => null,
- ];
- }
- return null;
- }
- private static function IP2Online(string $ip): array|null
- { // 开发依据: https://www.ip2location.io/ip2location-documentation
- $key = config('services.ip.IP2Location_key');
- if ($key) {
- $response = self::setBasicHttp()->acceptJson()->get("https://api.ip2location.io/?key=$key&ip=$ip");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country_name'],
- 'region' => $data['region_name'],
- 'city' => $data['city_name'],
- 'isp' => $data['as'],
- 'area' => null,
- 'latitude' => $data['latitude'],
- 'longitude' => $data['longitude'],
- ];
- }
- }
- return null;
- }
- private static function ipdata(string $ip): array|null
- { // 开发依据: https://docs.ipdata.co/docs
- $key = config('services.ip.ipdata_key');
- if ($key) {
- $response = self::setBasicHttp()->get("https://api.ipdata.co/$ip?api-key=$key&fields=ip,city,region,country_name,latitude,longitude,asn");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country_name'],
- 'region' => $data['region'],
- 'city' => $data['city'],
- 'isp' => $data['asn']['name'],
- 'area' => null,
- 'latitude' => $data['latitude'],
- 'longitude' => $data['longitude'],
- ];
- }
- }
- return null;
- }
- private static function ipApiCo(string $ip): array|null
- { // 开发依据: https://ipapi.co/api/
- $response = self::setBasicHttp()->get("https://ipapi.co/$ip/json/");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country_name'],
- 'region' => $data['region'],
- 'city' => $data['city'],
- 'isp' => $data['org'],
- 'area' => null,
- 'latitude' => $data['latitude'],
- 'longitude' => $data['longitude'],
- ];
- }
- return null;
- }
- private static function ip2Location(string $ip): array|null
- { // 通过ip2Location查询IP地址的详细信息
- $filePath = database_path('IP2LOCATION-LITE-DB5.IPV6.BIN'); // 来源: https://lite.ip2location.com/database-download
- try {
- $location = (new Database($filePath, Database::FILE_IO))
- ->lookup($ip, [Database::CITY_NAME, Database::REGION_NAME, Database::COUNTRY_NAME, Database::LATITUDE, Database::LONGITUDE]);
- return [
- 'country' => $location['countryName'],
- 'region' => $location['regionName'],
- 'city' => $location['cityName'],
- 'isp' => null,
- 'area' => null,
- 'latitude' => $location['latitude'],
- 'longitude' => $location['longitude'],
- ];
- } catch (Exception $e) {
- Log::error('【ip2Location】错误信息:'.$e->getMessage());
- }
- return null;
- }
- private static function GeoIP2(string $ip): array|null
- {// 通过GeoIP2查询IP地址的详细信息
- $filePath = database_path('GeoLite2-City.mmdb'); // 来源:https://github.com/PrxyHunter/GeoLite2/releases
- try {
- $location = (new Reader($filePath))->city($ip);
- return [
- 'country' => $location->country->name,
- 'region' => $location->mostSpecificSubdivision->name,
- 'city' => $location->city->name,
- 'isp' => null,
- 'area' => null,
- ];
- } catch (AddressNotFoundException $e) {
- Log::error("【GeoIP2】查询失败:$ip ".$e->getMessage());
- } catch (InvalidDatabaseException $e) {
- Log::error("【GeoIP2】数据库无效:$ip ".$e->getMessage());
- }
- return null;
- }
- private static function ipApiCom(string $ip): array|null
- { // 开发依据: https://docs.ipdata.co/docs
- $response = self::setBasicHttp()->get("https://ipapi.com/ip_api.php?ip=$ip");
- if ($response->ok()) {
- $data = $response->json();
- return [
- 'country' => $data['country_name'],
- 'region' => $data['region_name'],
- 'city' => $data['city'],
- 'isp' => $data['connection']['isp'],
- 'area' => null,
- 'latitude' => $data['latitude'],
- 'longitude' => $data['longitude'],
- ];
- }
- return null;
- }
- public static function getIPGeo(string $ip): array|null
- {
- $ret = null;
- $source = 0;
- while ($source <= 13 && ($ret === null || (is_array($ret) && empty(array_filter($ret))))) {
- $ret = match ($source) {
- 0 => self::IPSB($ip),
- 1 => self::ipApi($ip),
- 2 => self::baiduBce($ip),
- 3 => self::ipinfo($ip),
- 4 => self::IP2Online($ip),
- 5 => self::speedtest($ip),
- 6 => self::Baidu($ip),
- 7 => self::ip234($ip),
- 8 => self::ipdata($ip),
- 9 => self::ipGeoLocation($ip),
- 10 => self::ipjiance($ip),
- 11 => self::ipApiCo($ip),
- 12 => self::ipApiCom($ip),
- 13 => self::ip2Location($ip),
- };
- $source++;
- }
- return Arr::only($ret, ['latitude', 'longitude']);
- }
- private static function userAgentInfo(string $ip): array|null
- { // 开发依据: https://ip.useragentinfo.com/api
- if (self::$is_ipv4) {
- $response = self::setBasicHttp()->withBody("ip:$ip")->get('https://ip.useragentinfo.com/json');
- } else {
- $response = self::setBasicHttp()->get("https://ip.useragentinfo.com/ipv6/$ip");
- }
- if ($response->ok()) {
- $data = $response->json();
- if ($data['code'] === 200 && $data['ip'] === $ip) {
- return [
- 'country' => $data['country'],
- 'region' => $data['province'],
- 'city' => $data['city'],
- 'isp' => $data['isp'],
- 'area' => $data['area'],
- ];
- }
- Log::error('【userAgentInfo】IP查询失败:'.$data ?? '');
- } else {
- Log::error('【userAgentInfo】查询无效:'.$ip);
- }
- return null;
- }
- }
|