PHP調用純真IP數據庫查詢ip地址

準備工作:

建議本地IP地址數據庫,請到http://www.cz88.net/這個網站下載一個純真IP數據庫,安裝完成後,到安裝目錄裏把qqwry.dat文件取出來,這個就是我們想要的IP數據庫了,放到你想要目錄下。

IP地址查詢處理類

<?php 
   
/**
 * IP 地理位置查詢類
 */ 
class IpLocation
{ 
    /**
     * QQWry.Dat文件指針
     *
     * @var resource
     */ 
    private $fp; 
    /**
     * 第一條IP記錄的偏移地址
     *
     * @var int
     */ 
    private $firstip; 
    /**
     * 最後一條IP記錄的偏移地址
     *
     * @var int
     */ 
    private $lastip; 
    /**
     * IP記錄的總條數(不包含版本信息記錄)
     *
     * @var int
     */ 
    private $totalip; 
   
    /**
     * 構造函數,打開 QQWry.Dat 文件並初始化類中的信息
     *
     * @param string $filename
     */ 
    public function __construct($filename = 'qqwry.dat') 
    { 
        $this->fp = 0; 
        if (($this->fp = fopen($filename, 'rb')) !== false) { 
            $this->firstip = $this->getlong(); 
            $this->lastip = $this->getlong(); 
            $this->totalip = ($this->lastip - $this->firstip) / 7; 
            //註冊析構函數,使其在程序執行結束時執行 
            register_shutdown_function(array( 
                &$this, 
                '__destruct' 
            )); 
        } 
    } 
   
    /**
     * 析構函數,用於在頁面執行結束後自動關閉打開的文件。
     *
     */ 
    public function __destruct() 
    { 
        if ($this->fp) { 
            fclose($this->fp); 
        } 
        $this->fp = 0; 
    } 
   
    /**
     * 返回讀取的長整型數
     *
     * @access private
     * @return int
     */ 
    private function getlong() 
    { 
        //將讀取的little-endian編碼的4個字節轉化爲長整型數 
        $result = unpack('Vlong', fread($this->fp, 4)); 
        return $result['long']; 
    } 
   
    /**
     * 返回讀取的3個字節的長整型數
     *
     * @access private
     * @return int
     */ 
    private function getlong3() 
    { 
        //將讀取的little-endian編碼的3個字節轉化爲長整型數 
        $result = unpack('Vlong', fread($this->fp, 3) . chr(0)); 
        return $result['long']; 
    } 
   
    /**
     * 返回壓縮後可進行比較的IP地址
     *
     * @access private
     * @param string $ip
     * @return string
     */ 
    private function packip($ip) 
    { 
        // 將IP地址轉化爲長整型數,如果在PHP5中,IP地址錯誤,則返回False, 
        // 這時intval將Flase轉化爲整數-1,之後壓縮成big-endian編碼的字符串 
        return pack('N', intval(ip2long($ip))); 
    } 
   
    /**
     * 返回讀取的字符串
     *
     * @access private
     * @param string $data
     * @return string
     */ 
    private function getstring($data = "") 
    { 
        $char = fread($this->fp, 1); 
        while (ord($char) > 0) { // 字符串按照C格式保存,以結束 
            $data .= $char; // 將讀取的字符連接到給定字符串之後 
            $char = fread($this->fp, 1); 
        } 
        return iconv('gbk', 'utf-8', $data); 
    } 
   
    /**
     * 返回地區信息
     *
     * @access private
     * @return string
     */ 
    private function getarea() 
    { 
        $byte = fread($this->fp, 1); // 標誌字節 
        switch (ord($byte)) { 
            case 0: // 沒有區域信息 
                $area = ""; 
                break; 
            case 1: 
            case 2: // 標誌字節爲1或2,表示區域信息被重定向 
                fseek($this->fp, $this->getlong3()); 
                $area = $this->getstring(); 
                break; 
            default: // 否則,表示區域信息沒有被重定向 
                $area = $this->getstring($byte); 
                break; 
        } 
        return $area; 
    } 
   
    /**
     * 根據所給 IP 地址或域名返回所在地區信息
     *
     * @access public
     * @param string $ip
     * @return array
     */ 
    public function getlocation($ip) 
    { 
        if (!$this->fp) 
            return null; // 如果數據文件沒有被正確打開,則直接返回空 
        $location['ip'] = gethostbyname($ip); // 將輸入的域名轉化爲IP地址 
        $ip = $this->packip($location['ip']); // 將輸入的IP地址轉化爲可比較的IP地址 
        // 不合法的IP地址會被轉化爲255.255.255.255 
        // 對分搜索 
        $l = 0; // 搜索的下邊界 
        $u = $this->totalip; // 搜索的上邊界 
        $findip = $this->lastip; // 如果沒有找到就返回最後一條IP記錄(QQWry.Dat的版本信息) 
        while ($l <= $u) { // 當上邊界小於下邊界時,查找失敗 
            $i = floor(($l + $u) / 2); // 計算近似中間記錄 
            fseek($this->fp, $this->firstip + $i * 7); 
            $beginip = strrev(fread($this->fp, 4)); // 獲取中間記錄的開始IP地址 
            // strrev函數在這裏的作用是將little-endian的壓縮IP地址轉化爲big-endian的格式 
            // 以便用於比較,後面相同。 
            if ($ip < $beginip) { // 用戶的IP小於中間記錄的開始IP地址時 
                $u = $i - 1; // 將搜索的上邊界修改爲中間記錄減一 
            } else { 
                fseek($this->fp, $this->getlong3()); 
                $endip = strrev(fread($this->fp, 4)); // 獲取中間記錄的結束IP地址 
                if ($ip > $endip) { // 用戶的IP大於中間記錄的結束IP地址時 
                    $l = $i + 1; // 將搜索的下邊界修改爲中間記錄加一 
                } else { // 用戶的IP在中間記錄的IP範圍內時 
                    $findip = $this->firstip + $i * 7; 
                    break; // 則表示找到結果,退出循環 
                } 
            } 
        } 
        //獲取查找到的IP地理位置信息 
        fseek($this->fp, $findip); 
        $location['beginip'] = long2ip($this->getlong()); // 用戶IP所在範圍的開始地址 
        $offset = $this->getlong3(); 
        fseek($this->fp, $offset); 
        $location['endip'] = long2ip($this->getlong()); // 用戶IP所在範圍的結束地址 
        $byte = fread($this->fp, 1); // 標誌字節 
        switch (ord($byte)) { 
            case 1: // 標誌字節爲1,表示國家和區域信息都被同時重定向 
                $countryOffset = $this->getlong3(); // 重定向地址 
                fseek($this->fp, $countryOffset); 
                $byte = fread($this->fp, 1); // 標誌字節 
                switch (ord($byte)) { 
                    case 2: // 標誌字節爲2,表示國家信息又被重定向 
                        fseek($this->fp, $this->getlong3()); 
                        $location['country'] = $this->getstring(); 
                        fseek($this->fp, $countryOffset + 4); 
                        $location['area'] = $this->getarea(); 
                        break; 
                    default: // 否則,表示國家信息沒有被重定向 
                        $location['country'] = $this->getstring($byte); 
                        $location['area'] = $this->getarea(); 
                        break; 
                } 
                break; 
            case 2: // 標誌字節爲2,表示國家信息被重定向 
                fseek($this->fp, $this->getlong3()); 
                $location['country'] = $this->getstring(); 
                fseek($this->fp, $offset + 8); 
                $location['area'] = $this->getarea(); 
                break; 
            default: // 否則,表示國家信息沒有被重定向 
                $location['country'] = $this->getstring($byte); 
                $location['area'] = $this->getarea(); 
                break; 
        } 
        if ($location['country'] == " CZ88.NET") { // CZ88.NET表示沒有有效信息 
            $location['country'] = "未知"; 
        } 
        if ($location['area'] == " CZ88.NET") { 
            $location['area'] = ""; 
        } 
        return $location; 
    } 
}

調用類:

<?php 
   
include 'IpLocation.php'; 
   
echo "<pre>";    
$ip = "127.0.0.1";    
$iplocation = new IpLocation();    
$location = $iplocation->getlocation($ip);    
print_r($location); 

轉載自 https://www.cnblogs.com/lxwphp/p/8807152.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章