123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- <?php
- /**
- * Created by PhpStorm.
- * User: wangfanchang
- * Date: 18/1/10
- * Time: 下午2:13
- */
- namespace app\common\library;
- use think\Exception;
- use think\Request;
- defined('INDEX_BLOCK_LENGTH') or define('INDEX_BLOCK_LENGTH', 12);
- defined('TOTAL_HEADER_LENGTH') or define('TOTAL_HEADER_LENGTH', 8192);
- class Ip
- {
- /**
- * db file handler
- */
- private static $dbFileHandler = null;
- /**
- * header block info
- */
- private static $HeaderSip = null;
- private static $HeaderPtr = null;
- /**
- * super block index info
- */
- private static $firstIndexPtr = 0;
- private static $lastIndexPtr = 0;
- private static $totalBlocks = 0;
- /**
- * for memory mode only
- * the original db binary string
- */
- private static $dbBinStr = null;
- /**
- * ip地址记录
- * @var array
- */
- private static $list = [];
- /**
- * 用户访问ip地址
- * @var null|string
- */
- private static $local = null;
- /**
- * 获取用户ip地址
- * @return string
- */
- public static function ip()
- {
- if (is_null(self::$local)) {
- $ip = Request::instance()->ip();
- if(filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
- self::$local = $ip;
- }
- }
- return static::$local;
- }
- /**
- * 获取IP的C段地址
- * @return string
- */
- public static function ip_c(){
- $ip = self::ip();
- $ip_c = null;
- if($ip){
- $ip_arr = explode('.',$ip);
- $ip_c = $ip_arr[0].'.'.$ip_arr[1].'.'.$ip_arr[2];
- }
- return $ip_c;
- }
- /**
- * 原ip格式返回
- * @param null $ip
- * @return string
- */
- public static function str($ip = null)
- {
- if (is_null($ip)) {
- $ip = self::ip();
- }
- if (isset(self::$list[$ip])) {
- return self::$list[$ip];
- }
- $res = self::memorySearch($ip);
- self::$list[$ip] = $res;
- return $res;
- }
- /**
- * 返回切割的region数组
- * @param null $ip
- * @return array 国家|区域|省份|城市|ISP
- */
- public static function arr($ip = null)
- {
- return explode('|', self::str($ip));
- }
- /**
- * 获取国家
- * @param null $ip
- * @return string
- */
- public static function country($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[0] ?? null;
- }
- /**
- * 获取区域
- * @param null $ip
- * @return string
- */
- public static function area($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[1] ?? null;
- }
- /**
- * 获取省份
- * @param null $ip
- * @return string
- */
- public static function province($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[2] ?? null;
- }
- /**
- * 获取城市
- * @param null $ip
- * @return string
- */
- public static function city($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[3] ?? null;
- }
- /**
- * 获取城市码
- * @param null $ip
- * @return string
- */
- public static function citycode($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[5] ?? null;
- }
- /**
- * 获取Isp
- * @param null $ip
- * @return string
- */
- public static function isp($ip = null)
- {
- $arr = self::arr($ip);
- return $arr[4] ?? null;
- }
- /**
- * all the db binary string will be loaded into memory
- * then search the memory only and this will a lot faster than disk base search
- * @Note:
- * invoke it once before put it to public invoke could make it thread safe
- *
- * @param $ip
- * @return string
- */
- private static function memorySearch($ip)
- {
- //check and load the binary string for the first time
- if (self::$dbBinStr == null) {
- $dbFile = ROOT_PATH . 'public/assets/data/ip2region.db';
- self::$dbBinStr = file_get_contents($dbFile);
- if (self::$dbBinStr == false) {
- throw new Exception("Fail to open the db file {$dbFile}");
- }
- self::$firstIndexPtr = self::getLong(self::$dbBinStr, 0);
- self::$lastIndexPtr = self::getLong(self::$dbBinStr, 4);
- self::$totalBlocks = (self::$lastIndexPtr - self::$firstIndexPtr) / INDEX_BLOCK_LENGTH + 1;
- }
- if (is_string($ip)) {
- $ip = self::safeIp2long($ip);
- }
- //binary search to define the data
- $l = 0;
- $h = self::$totalBlocks;
- $dataPtr = 0;
- while ($l <= $h) {
- $m = (($l + $h) >> 1);
- $p = self::$firstIndexPtr + $m * INDEX_BLOCK_LENGTH;
- $sip = self::getLong(self::$dbBinStr, $p);
- if ($ip < $sip) {
- $h = $m - 1;
- } else {
- $eip = self::getLong(self::$dbBinStr, $p + 4);
- if ($ip > $eip) {
- $l = $m + 1;
- } else {
- $dataPtr = self::getLong(self::$dbBinStr, $p + 8);
- break;
- }
- }
- }
- //not matched just stop it here
- if ($dataPtr == 0) {
- return null;
- }
- //get the data
- $dataLen = (($dataPtr >> 24) & 0xFF);
- $dataPtr = ($dataPtr & 0x00FFFFFF);
- return substr(self::$dbBinStr, $dataPtr + 4, $dataLen - 4).'|'.self::getLong(self::$dbBinStr, $dataPtr);
- }
- /**
- * safe self::safeIp2long function
- *
- * @param ip
- * */
- private static function safeIp2long($ip)
- {
- $ip = ip2long($ip);
- // convert signed int to unsigned int if on 32 bit operating system
- if ($ip < 0 && PHP_INT_SIZE == 4) {
- $ip = sprintf("%u", $ip);
- }
- return $ip;
- }
- /**
- * read a long from a byte buffer
- *
- * @param b
- * @param offset
- */
- private static function getLong($b, $offset)
- {
- $val = (
- (ord($b[$offset++])) |
- (ord($b[$offset++]) << 8) |
- (ord($b[$offset++]) << 16) |
- (ord($b[$offset]) << 24)
- );
- // convert signed int to unsigned int if on 32 bit operating system
- if ($val < 0 && PHP_INT_SIZE == 4) {
- $val = sprintf("%u", $val);
- }
- return $val;
- }
- /**
- * destruct method, resource destroy
- */
- public function __destruct()
- {
- if (self::$dbFileHandler != null) {
- fclose(self::$dbFileHandler);
- }
- self::$dbBinStr = null;
- self::$HeaderSip = null;
- self::$HeaderPtr = null;
- }
- }
|