<?php

/**
 * 純真IP根據IP地址獲得地址
 */

class ipLocation {

    public $fp;

    public $firstip;  //第一條ip索引的偏移地址

    public $lastip;   //最後一條ip索引的偏移地址

    public $totalip;  //總ip數



//

//*

//構造函數,初始化一些變量

//$datfile 的值為純真IP數據庫的名子,可自行修改.

//*



    function __construct($datfile = "CoralWry.dat") {

        $filepath = APP_CLASS . 'Public/CoralWry.dat';

        $this->fp = fopen($filepath, 'rb');   //二制方式打開

        $this->firstip = $this->get4b(); //第一條ip索引的絕對偏移地址

        $this->lastip = $this->get4b();  //最後一條ip索引的絕對偏移地址

        $this->totalip = ($this->lastip - $this->firstip) / 7; //ip總數 索引區是定長的7個字節,在此要除以7,

        register_shutdown_function(array($this, "closefp"));  //為了兼容php5以下版本,本類沒有用析構函數,自動關閉ip庫.

    }



//*

//關閉ip庫

//*

    function closefp() {

        fclose($this->fp);

    }



//*

//讀取4個字節並將解壓成long的長模式

//*

    function get4b() {

        $str = unpack("V", fread($this->fp, 4));

        return $str[1];

    }



//*

//讀取重定向了的偏移地址

//*

    function getoffset() {

        $str = unpack("V", fread($this->fp, 3) . chr(0));

        return $str[1];

    }



//*

//讀取ip的詳細地址信息

//*

    function getstr() {

        $split = fread($this->fp, 1);

        while (ord($split) != 0) {

            $str .=$split;

            $split = fread($this->fp, 1);

        }

        return $str;

    }



//*

//將ip通過ip2long轉成ipv4的互聯網地址,再將他壓縮成big-endian字節序

//用來和索引區內的ip地址做比較

//*

    function iptoint($ip) {

        return pack("N", intval(ip2long($ip)));

    }



//*

//獲取客户端ip地址

//注意:如果你想要把ip記錄到服務器上,請在寫庫時先檢查一下ip的數據是否安全.

//*

    function getIP() {

        if (getenv('HTTP_CLIENT_IP')) {

            $ip = getenv('HTTP_CLIENT_IP');

        } elseif (getenv('HTTP_X_FORWARDED_FOR')) { //獲取客户端用代理服務器訪問時的真實ip 地址

            $ip = getenv('HTTP_X_FORWARDED_FOR');

        } elseif (getenv('HTTP_X_FORWARDED')) {

            $ip = getenv('HTTP_X_FORWARDED');

        } elseif (getenv('HTTP_FORWARDED_FOR')) {

            $ip = getenv('HTTP_FORWARDED_FOR');

        } elseif (getenv('HTTP_FORWARDED')) {

            $ip = getenv('HTTP_FORWARDED');

        } else {

            $ip = $_SERVER['REMOTE_ADDR'];

        }

        return $ip;

    }



//*

//獲取地址信息

//*

    function readaddress() {

        $now_offset = ftell($this->fp); //得到當前的指針位址

        $flag = $this->getflag();

        switch (ord($flag)) {

            case 0:

                $address = "";

                break;

            case 1:

            case 2:

                fseek($this->fp, $this->getoffset());

                $address = $this->getstr();

                break;

            default:

                fseek($this->fp, $now_offset);

                $address = $this->getstr();

                break;

        }

        return $address;

    }



//*

//獲取標誌1或2

//用來確定地址是否重定向了.

//*

    function getflag() {

        return fread($this->fp, 1);

    }



//*

//用二分查找法在索引區內搜索ip

//*

    function searchip($ip) {

        $ip = gethostbyname($ip);     //將域名轉成ip

        $ip_offset["ip"] = $ip;

        $ip = $this->iptoint($ip);    //將ip轉換成長整型

        $firstip = 0;                 //搜索的上邊界

        $lastip = $this->totalip;     //搜索的下邊界

        $ipoffset = $this->lastip;    //初始化為最後一條ip地址的偏移地址

        while ($firstip <= $lastip) {

            $i = floor(($firstip + $lastip) / 2);          //計算近似中間記錄 floor函數記算給定浮點數小的最大整數,説白了就是四舍五也舍

            fseek($this->fp, $this->firstip + $i * 7);    //定位指針到中間記錄

            $startip = strrev(fread($this->fp, 4));         //讀取當前索引區內的開始ip地址,並將其little-endian的字節序轉換成big-endian的字節序

            if ($ip < $startip) {

                $lastip = $i - 1;

            } else {

                fseek($this->fp, $this->getoffset());

                $endip = strrev(fread($this->fp, 4));

                if ($ip > $endip) {

                    $firstip = $i + 1;

                } else {

                    $ip_offset["offset"] = $this->firstip + $i * 7;

                    break;

                }

            }

        }

        return $ip_offset;

    }



//*

//獲取ip地址詳細信息

//*

    function getaddress($ip) {

        $ip_offset = $this->searchip($ip);  //獲取ip 在索引區內的絕對編移地址

        $ipoffset = $ip_offset["offset"];

        $address["ip"] = $ip_offset["ip"];

        fseek($this->fp, $ipoffset);      //定位到索引區

        $address["startip"] = long2ip($this->get4b()); //索引區內的開始ip 地址

        $address_offset = $this->getoffset();            //獲取索引區內ip在ip記錄區內的偏移地址

        fseek($this->fp, $address_offset);            //定位到記錄區內

        $address["endip"] = long2ip($this->get4b());   //記錄區內的結束ip 地址

        $flag = $this->getflag();                      //讀取標誌字節

        switch (ord($flag)) {

            case 1:  //地區1地區2都重定向

                $address_offset = $this->getoffset();   //讀取重定向地址

                fseek($this->fp, $address_offset);     //定位指針到重定向的地址

                $flag = $this->getflag();               //讀取標誌字節

                switch (ord($flag)) {

                    case 2:  //地區1又一次重定向,

                        fseek($this->fp, $this->getoffset());

                        $address["area1"] = $this->getstr();

                        fseek($this->fp, $address_offset + 4);      //跳4個字節

                        $address["area2"] = $this->readaddress();  //地區2有可能重定向,有可能沒有

                        break;

                    default: //地區1,地區2都沒有重定向

                        fseek($this->fp, $address_offset);        //定位指針到重定向的地址

                        $address["area1"] = $this->getstr();

                        $address["area2"] = $this->readaddress();

                        break;

                }

                break;

            case 2: //地區1重定向 地區2沒有重定向

                $address1_offset = $this->getoffset();   //讀取重定向地址

                fseek($this->fp, $address1_offset);

                $address["area1"] = $this->getstr();

                fseek($this->fp, $address_offset + 8);

                $address["area2"] = $this->readaddress();

                break;

            default: //地區1地區2都沒有重定向

                fseek($this->fp, $address_offset + 4);

                $address["area1"] = $this->getstr();

                $address["area2"] = $this->readaddress();

                break;

        }

        //*過濾一些無用數據

        if (strpos($address["area1"], "CZ88.NET") != false) {

            $address["area1"] = "未知";

        }

        if (strpos($address["area2"], "CZ88.NET") != false) {

            $address["area2"] = " ";

        }

        $address['area1'] = Fun::gbkToutf8($address['area1']);

        $address['area2'] = Fun::gbkToutf8($address['area2']);

        $address['loc'] = $address['area1'] . ' ' . $address['area2'];

        return $address;

    }



}



?>