首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 取得客戶端 IP 與地理位置類別:新浪 IP 接口整合

     編輯:PHP 綜合知識

整合 IP 取得邏輯與新浪 IP 歸屬地查詢 API,封裝為可重用類別,支援 JSON/純文字格式、Cookie 快取、代理穿透偵測,適用於訪客統計、內容地域化、安全風控等場景。

完整類別實作

<?php
class IPLocator {
    private $config = [];
    private $sinaApi = 'https://int.dpool.sina.com.cn/iplookup/iplookup.php';

    public function __construct(array $options = []) {
        $defaults = [
            'format' => 'json',      // json | text
            'timeout' => 5,          // 秒
            'cache_ttl' => 3600,     // 秒,0 關閉快取
            'proxy_headers' => ['HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'HTTP_X_REAL_IP', 'REMOTE_ADDR'],
        ];
        $this->config = array_merge($defaults, $options);
    }

    /**
     * 取得客戶端真實 IP (支援代理穿透)
     */
    public function getClientIP() {
        foreach ($this->config['proxy_headers'] as $header) {
            if (!empty($_SERVER[$header]) && $_SERVER[$header] !== 'unknown') {
                // 取第一個非私有 IP (代理鏈中最原始的)
                $ips = array_map('trim', explode(',', $_SERVER[$header]));
                foreach ($ips as $ip) {
                    if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
                        return $ip;
                    }
                }
                // 找不到公網 IP 回傳第一個
                return $ips[0];
            }
        }
        return 'unknown';
    }

    /**
     * 查詢 IP 歸屬地
     * @param string|null $ip 指定 IP,null 為自動偵測
     * @return array|string|false
     */
    public function locate($ip = null) {
        $ip = $ip ?? $this->getClientIP();
        
        // 驗證 IP 格式
        if (!filter_var($ip, FILTER_VALIDATE_IP)) {
            return false;
        }

        // 快取檢查
        $cacheKey = 'ip_loc_' . md5($ip);
        if ($this->config['cache_ttl'] > 0) {
            // 這裡可整合 Redis/Memcached/APCu
            // $cached = Cache::get($cacheKey);
            // if ($cached) return $cached;
        }

        // 建立請求 URL
        $url = $this->sinaApi . '?ip=' . urlencode($ip) . '&format=' . $this->config['format'];
        
        // cURL 請求
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => $this->config['timeout'],
            CURLOPT_CONNECTTIMEOUT => 3,
            CURLOPT_USERAGENT => 'PHP IPLocator/1.0',
        ]);
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($response === false || $httpCode !== 200) {
            return false;
        }

        // 格式化回應
        $result = $this->config['format'] === 'json' ? json_decode($response, true) : $response;
        
        // 新浪 API 文字格式為 GBK,需轉碼
        if ($this->config['format'] === 'text') {
            $result = iconv('GBK', 'UTF-8//IGNORE', $result);
        }

        // 寫入快取
        // if ($this->config['cache_ttl'] > 0) Cache::set($cacheKey, $result, $this->config['cache_ttl']);

        return $result;
    }

    /**
     * 靜態快速呼叫
     */
    public static function getLocation($ip = null, $format = 'json') {
        $locator = new self(compact('format'));
        return $locator->locate($ip);
    }
}

// 使用範例
$locator = new IPLocator(['format' => 'json', 'cache_ttl' => 3600]);

// 1. 取得當前訪客 IP
$ip = $locator->getClientIP();
echo "訪客 IP: $ip\n";

// 2. 查詢歸屬地 (JSON 格式)
$location = $locator->locate();
if ($location) {
    echo "國家: " . ($location['country'] ?? '未知') . "\n";
    echo "省份: " . ($location['province'] ?? '未知') . "\n";
    echo "城市: " . ($location['city'] ?? '未知') . "\n";
    echo "ISP: " . ($location['isp'] ?? '未知') . "\n";
}

// 3. 指定 IP 查詢
$location = IPLocator::getLocation('8.8.8.8');
var_dump($location);
?>

新浪 API 回應格式說明

JSON 格式 (推薦)

{
    "ret": 1,
    "start": "8.8.8.0",
    "end": "8.8.8.255",
    "country": "美国",
    "province": "加利福尼亚",
    "city": "山景城",
    "district": "",
    "isp": "Google",
    "type": "DNS服务器",
    "desc": "Google Public DNS"
}

文字格式 (GBK 編碼)

8.8.8.0	8.8.8.255	美国	加利福尼亚	山景城		Google	DNS服务器	Google Public DNS

前端整合範例 (JavaScript)

<script>
// 後端直接輸出 JS 變數
var visitorIP = "<?php echo (new IPLocator(['format'=>'json']))->getClientIP(); ?>";
var locationData = <?php echo json_encode((new IPLocator(['format'=>'json']))->locate()); ?>;

console.log('IP:', visitorIP);
console.log('位置:', locationData);

// 顯示在地化內容
if (locationData && locationData.city) {
    document.getElementById('local-content').textContent = 
        '歡迎來自 ' + locationData.city + ' 的訪客!';
}
</script>

生產環境強化建議

  1. 快取層:整合 Redis/Memcached 避免重複查詢 API
  2. 備用 API:新浪 API 可能不穩,建議整合 ip-api.com、ipinfo.io、MaxMind GeoIP2 作為備援
  3. 本地資料庫:高流量場景部署 MaxMind GeoLite2 本地資料庫,毫秒級查詢
  4. 隱私合規:遵循 GDPR/個資法,IP 屬個人資料,需告知用途、提供選擇權
  5. 速率限制:對外部 API 呼叫加入 Token Bucket 限流

替代方案對比

方案準確度速度成本維護
新浪 API中 (國內較準)慢 (網路請求)免費零維護
ip-api.com中中免費/付費零維護
MaxMind GeoIP2 (本地)高極快 (本地)免費/授權費定期更新資料庫
ipinfo.io高快免費額度/付費零維護
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved