PHP通过IP获取物理位置,php通过ip获取地理位置的方法

1 <?php2 /**3 * IP 地理位置查询类 修改自 CoolCode.CN4 * 由于使用UTF8编码 如果使用纯真IP地址库的话 需要对返回结果进行编码转换5 * @category ORG6 * @package ORG7 * @subpackage Net8 * @author liu21st 9 */

10 classIpLocation {11 /**12 * QQWry.Dat文件指针13 *14 * @var resource15 */

16 private $fp;17

18 /**19 * 第一条IP记录的偏移地址20 *21 * @var int22 */

23 private $firstip;24

25 /**26 * 最后一条IP记录的偏移地址27 *28 * @var int29 */

30 private $lastip;31

32 /**33 * IP记录的总条数(不包含版本信息记录)34 *35 * @var int36 */

37 private $totalip;38

39 /**40 * 构造函数,打开 QQWry.Dat 文件并初始化类中的信息41 *42 * @param string $filename43 * @return IpLocation44 */

45 public function __construct($filename = "UTFWry.dat") {46 $this->fp = 0;47 if (($this->fp = fopen(dirname(__FILE__).'/'.$filename, 'rb')) !== false) {48 $this->firstip = $this->getlong();49 $this->lastip = $this->getlong();50 $this->totalip = ($this->lastip - $this->firstip) / 7;51 }52 }53

54 /**55 * 返回读取的长整型数56 *57 * @access private58 * @return int59 */

60 private functiongetlong() {61 //将读取的little-endian编码的4个字节转化为长整型数

62 $result = unpack('Vlong', fread($this->fp, 4));63 return $result['long'];64 }65

66 /**67 * 返回读取的3个字节的长整型数68 *69 * @access private70 * @return int71 */

72 private functiongetlong3() {73 //将读取的little-endian编码的3个字节转化为长整型数

74 $result = unpack('Vlong', fread($this->fp, 3).chr(0));75 return $result['long'];76 }77

78 /**79 * 返回压缩后可进行比较的IP地址80 *81 * @access private82 * @param string $ip83 * @return string84 */

85 private function packip($ip) {86 //将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,87 // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串

88 return pack('N', intval(ip2long($ip)));89 }90

91 /**92 * 返回读取的字符串93 *94 * @access private95 * @param string $data96 * @return string97 */

98 private function getstring($data = "") {99 $char = fread($this->fp, 1);100 while (ord($char) > 0) { //字符串按照C格式保存,以\0结束

101 $data .= $char; //将读取的字符连接到给定字符串之后

102 $char = fread($this->fp, 1);103 }104 return $data;105 }106

107 /**108 * 返回地区信息109 *110 * @access private111 * @return string112 */

113 private functiongetarea() {114 $byte = fread($this->fp, 1); //标志字节

115 switch (ord($byte)) {116 case 0: //没有区域信息

117 $area = "";118 break;119 case 1:

120 case 2: //标志字节为1或2,表示区域信息被重定向

121 fseek($this->fp, $this->getlong3());122 $area = $this->getstring();123 break;124 default: //否则,表示区域信息没有被重定向

125 $area = $this->getstring($byte);126 break;127 }128 return $area;129 }130

131 /**132 * 根据所给 IP 地址或域名返回所在地区信息133 *134 * @access public135 * @param string $ip136 * @return array137 */

138 public function getlocation($ip='') {139 if (!$this->fp) return null; //如果数据文件没有被正确打开,则直接返回空

140 if(empty($ip)) $ip = $this->get_client_ip();141 $location['ip'] = gethostbyname($ip); //将输入的域名转化为IP地址

142 $ip = $this->packip($location['ip']); //将输入的IP地址转化为可比较的IP地址143 // 不合法的IP地址会被转化为255.255.255.255144 // 对分搜索

145 $l = 0; //搜索的下边界

146 $u = $this->totalip; //搜索的上边界

147 $findip = $this->lastip; //如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)

148 while ($l <= $u) { //当上边界小于下边界时,查找失败

149 $i = floor(($l + $u) / 2); //计算近似中间记录

150 fseek($this->fp, $this->firstip + $i * 7);151 $beginip = strrev(fread($this->fp, 4)); //获取中间记录的开始IP地址152 // strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式153 // 以便用于比较,后面相同。

154 if ($ip < $beginip) { //用户的IP小于中间记录的开始IP地址时

155 $u = $i - 1; //将搜索的上边界修改为中间记录减一

156 }157 else{158 fseek($this->fp, $this->getlong3());159 $endip = strrev(fread($this->fp, 4)); //获取中间记录的结束IP地址

160 if ($ip > $endip) { //用户的IP大于中间记录的结束IP地址时

161 $l = $i + 1; //将搜索的下边界修改为中间记录加一

162 }163 else { //用户的IP在中间记录的IP范围内时

164 $findip = $this->firstip + $i * 7;165 break; //则表示找到结果,退出循环

166 }167 }168 }169

170 //获取查找到的IP地理位置信息

171 fseek($this->fp, $findip);172 $location['beginip'] = long2ip($this->getlong()); //用户IP所在范围的开始地址

173 $offset = $this->getlong3();174 fseek($this->fp, $offset);175 $location['endip'] = long2ip($this->getlong()); //用户IP所在范围的结束地址

176 $byte = fread($this->fp, 1); //标志字节

177 switch (ord($byte)) {178 case 1: //标志字节为1,表示国家和区域信息都被同时重定向

179 $countryOffset = $this->getlong3(); //重定向地址

180 fseek($this->fp, $countryOffset);181 $byte = fread($this->fp, 1); //标志字节

182 switch (ord($byte)) {183 case 2: //标志字节为2,表示国家信息又被重定向

184 fseek($this->fp, $this->getlong3());185 $location['country'] = $this->getstring();186 fseek($this->fp, $countryOffset + 4);187 $location['area'] = $this->getarea();188 break;189 default: //否则,表示国家信息没有被重定向

190 $location['country'] = $this->getstring($byte);191 $location['area'] = $this->getarea();192 break;193 }194 break;195 case 2: //标志字节为2,表示国家信息被重定向

196 fseek($this->fp, $this->getlong3());197 $location['country'] = $this->getstring();198 fseek($this->fp, $offset + 8);199 $location['area'] = $this->getarea();200 break;201 default: //否则,表示国家信息没有被重定向

202 $location['country'] = $this->getstring($byte);203 $location['area'] = $this->getarea();204 break;205 }206 if ($location['country'] == " CZ88.NET") { //CZ88.NET表示没有有效信息

207 $location['country'] = "Unknown";208 }209 if ($location['area'] == " CZ88.NET") {210 $location['area'] = "Unknown";211 }212 return $location;213 }214

215 /**216 * 返回本机ip217 *218 * @access public219 * @param null220 * @return string221 */

222 public functionget_client_ip(){223 if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))224 $ip = getenv("HTTP_CLIENT_IP");225 else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))226 $ip = getenv("HTTP_X_FORWARDED_FOR");227 else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))228 $ip = getenv("REMOTE_ADDR");229 else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))230 $ip = $_SERVER['REMOTE_ADDR'];231 else

232 $ip = "unknown";233 return($ip);234 }235

236

237 /**238 * 析构函数,用于在页面执行结束后自动关闭打开的文件。239 *240 */

241 public function__destruct() {242 if ($this->fp) {243 fclose($this->fp);244 }245 $this->fp = 0;246 }247 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值