IP.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <?php
  2. /**
  3. * IP 信息
  4. *
  5. * @author luolongf <[email protected]>
  6. * @date 2022-05-14
  7. * @time 8:28
  8. */
  9. namespace Luolongfei\Libs;
  10. use GuzzleHttp\Client;
  11. use GuzzleHttp\Exception\ClientException;
  12. class IP extends Base
  13. {
  14. const TIMEOUT = 2.14;
  15. /**
  16. * @var string 匹配 ip 的正则
  17. */
  18. const REGEX_IP = '/(?:\d{1,3}\.){3}\d{1,3}/u';
  19. const REGEX_LOC = '/^.*:(?P<country>[^\s]+?)\s+?(?P<region>[^\s]+?)\s+?(?P<city>[^\s]+?)\s/iu';
  20. /**
  21. * @var string 匹配 IPIP.NET 首页数据的正则
  22. */
  23. const REGEX_IPIP_NET_PAGE = '/class="yourInfo">[\s\S]+?IP.*?<a[^>]+>(?P<ip>.*?)<\/a>[\d\D]+?位置.*?>(?P<loc>[^<]+)/iu';
  24. /**
  25. * @var string ipip.net 首页地址,显示中文
  26. */
  27. const IPIP_NET_URL = 'https://www.ipip.net/?origin=EN';
  28. /**
  29. * @var string ip 地址
  30. */
  31. public static $ip = '';
  32. /**
  33. * @var string 位置信息
  34. */
  35. public static $loc = '';
  36. /**
  37. * @var Client
  38. */
  39. protected $client;
  40. /**
  41. * @var string 用于查询 ip 的地址
  42. */
  43. protected $url;
  44. public function init()
  45. {
  46. $this->client = new Client([
  47. 'headers' => [
  48. 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
  49. ],
  50. 'cookies' => false,
  51. 'timeout' => self::TIMEOUT,
  52. 'verify' => config('verify_ssl'),
  53. 'debug' => config('debug'),
  54. ]);
  55. $this->url = is_chinese() ? 'https://myip.ipip.net' : 'https://ipinfo.io/json';
  56. }
  57. /**
  58. * 获取 IP 信息
  59. *
  60. * @return string
  61. * @throws \GuzzleHttp\Exception\GuzzleException
  62. */
  63. public function get()
  64. {
  65. try {
  66. if (!self::$ip && !self::$loc) {
  67. $res = $this->client->get($this->url);
  68. $body = $res->getBody()->getContents();
  69. $this->setIpInfo($body);
  70. }
  71. return sprintf(lang('100130'), self::$ip, self::$loc);
  72. } catch (ClientException $e) {
  73. // myip.ipip.net 针对某些国外 VPS 会直接返回 404,需要进一步处理
  74. if ($e->getResponse()->getStatusCode() === 404 && $this->setIpInfoByIpIpNetPage()) {
  75. return sprintf(lang('100130'), self::$ip, self::$loc);
  76. }
  77. throw $e;
  78. } catch (\Exception $e) {
  79. Log::error(lang('100132') . $e->getMessage());
  80. return lang('100131');
  81. }
  82. }
  83. /**
  84. * 通过 IPIP.NET 页面设置 IP 信息
  85. *
  86. * @return bool
  87. * @throws \GuzzleHttp\Exception\GuzzleException
  88. */
  89. private function setIpInfoByIpIpNetPage()
  90. {
  91. try {
  92. $body = $this->client->get(self::IPIP_NET_URL)->getBody()->getContents();
  93. if (preg_match(self::REGEX_IPIP_NET_PAGE, $body, $m)) {
  94. self::$ip = $m['ip'];
  95. self::$loc = $m['loc'];
  96. return true;
  97. }
  98. } catch (\Exception $e) {
  99. Log::error(lang('100132') . $e->getMessage());
  100. }
  101. return false;
  102. }
  103. /**
  104. * 设置 ip 信息
  105. *
  106. * @param $body
  107. *
  108. * @return bool
  109. */
  110. protected function setIpInfo($body)
  111. {
  112. if (is_chinese()) {
  113. if (preg_match(self::REGEX_IP, $body, $m)) {
  114. self::$ip = $m[0];
  115. }
  116. if (preg_match(self::REGEX_LOC, $body, $m)) {
  117. self::$loc = sprintf('%s %s %s', $m['country'], $m['region'], $m['city']);
  118. }
  119. return true;
  120. }
  121. $body = (array)json_decode($body, true);
  122. self::$ip = $body['ip'] ?? '';
  123. self::$loc = sprintf('%s %s %s', $body['country'] ?? '', $body['region'] ?? '', $body['city'] ?? '');
  124. return true;
  125. }
  126. }