PHP获取IP所在地区

转载自http://www.phper.org.cn/?post=88

这是一个PHP获取客户端IP所在地区的类,它能根据IP地址查地区,简单实用。

使用示例

示例一:

1. $IpLocation = new IpLocation();
2. $client = $IpLocation->getlocation();
3. print_r($client);

示例二:

1. $IpLocation = new IpLocation('../qqwry/QQWry.Dat');
2. $client = $IpLocation->getlocation('115.148.101.72');
3. print_r($client);

相关说明

此类要求提供纯真IP数据库,附件包里包含有此数据库。

在线演示

IP地址查询在线演示:http://down.phper.org.cn/ip/

iplocation.class.php

001. class IpLocation
002. {
003. /**
004. * QQWry.Dat文件指针
005. * @var resource
006. */
007. var $fp;
008.  
009. /**
010. * 第一条IP记录的偏移地址
011. * @var int
012. */
013. var $firstip;
014.  
015. /**
016. * 最后一条IP记录的偏移地址
017. * @var int
018. */
019. var $lastip;
020.  
021. /**
022. * IP记录的总条数(不包含版本信息记录)
023. * @var int
024. */
025. var $totalip;
026.  
027. /**
028. * 返回读取的长整型数
029. * @access private
030. * @return int
031. */
032. function getlong()
033. {
034. $result = unpack('Vlong', fread($this->fp, 4));  //将读取的little-endian编码的4个字节转化为长整型数
035. return $result['long'];
036. }
037.  
038. /**
039. * 返回读取的3个字节的长整型数
040. * @access private
041. * @return int
042. */
043. function getlong3()
044. {
045. $result = unpack('Vlong', fread($this->fp, 3).chr(0));   //将读取的little-endian编码的3个字节转化为长整型数
046. return $result['long'];
047. }
048.  
049. /**
050. * 返回压缩后可进行比较的IP地址
051. * @access private
052. * @param string $ip
053. * @return string
054. */
055. function packip($ip)
056. {
057. // 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
058. // 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
059. return pack('N', intval(ip2long($ip)));         //intaval 获取变量的整数值
060. }
061.  
062. /**
063. * 返回读取的字符串
064. * @access private
065. * @param string $data
066. * @return string
067. */
068. function getstring($data = "")
069. {
070. $char = fread($this->fp, 1);
071. while (ord($char) > 0)                   // 字符串按照C格式保存,以\0结束 ord()得到字符的ASCII码
072. {
073. $data .= $char;             // 将读取的字符连接到给定字符串之后
074. $char = fread($this->fp, 1);
075. }
076. return $data;
077. }
078.  
079. /**
080. * 返回地区信息
081. * @access private
082. * @return string
083. */
084. function getarea()
085. {
086. $byte = fread($this->fp, 1);             // 标志字节
087. switch (ord($byte)) {
088. case 0:                     // 没有区域信息
089. $area = "";
090. break;
091. case 1:
092. case 2:                     // 标志字节为1或2,表示区域信息被重定向
093. fseek($this->fp, $this->getlong3());
094. $area = $this->getstring();
095. break;
096. default:                    // 否则,表示区域信息没有被重定向
097. $area = $this->getstring($byte);
098. break;
099. }
100. return $area;
101. }
102.  
103. /**
104. * 根据所给 IP 地址或域名返回所在地区信息
105. * @access public
106. * @param string $ip
107. * @return array
108. */
109. function getlocation($ip = '')
110. {
111. if (!$this->fp) return null;             // 如果数据文件没有被正确打开,则直接返回空
112. if($ip == '') $ip = $this->clientIp();
113. $location['ip'] = gethostbyname($ip);           // 将输入的域名转化为IP地址
114. $ip = $this->packip($location['ip']);            // 将输入的IP地址转化为可比较的IP地址
115.  
116. $l = 0;                                         // 搜索的下边界
117. $u = $this->totalip;                         // 搜索的上边界
118. $findip = $this->lastip;                     // 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
119. while ($l <= $u)                 // 当上边界小于下边界时,查找失败
120. {
121. $i = floor(($l + $u) / 2);          // 计算近似中间记录
122. fseek($this->fp, $this->firstip + $i * 7);
123. $beginip = strrev(fread($this->fp, 4));        // 获取中间记录的开始IP地址
124.  
125. if ($ip < $beginip)                  // 用户的IP小于中间记录的开始IP地址时
126. {
127. $u = $i - 1;                        // 将搜索的上边界修改为中间记录减一
128. }
129. else
130. {
131. fseek($this->fp, $this->getlong3());
132. $endip = strrev(fread($this->fp, 4));    // 获取中间记录的结束IP地址
133. if ($ip > $endip)            // 用户的IP大于中间记录的结束IP地址时
134. {
135. $l = $i + 1;            // 将搜索的下边界修改为中间记录加一
136. }
137. else                    // 用户的IP在中间记录的IP范围内时
138. {
139. $findip = $this->firstip + $i * 7;
140. break;              // 则表示找到结果,退出循环
141. }
142. }
143. }
144.  
145. /* 获取查找到的IP地理位置信息 */
146. fseek($this->fp, $findip);
147. $location['beginip'] = long2ip($this->getlong());    // 用户IP所在范围的开始地址
148. $offset = $this->getlong3();
149. fseek($this->fp, $offset);
150. $location['endip'] = long2ip($this->getlong());  // 用户IP所在范围的结束地址
151. $byte = fread($this->fp, 1);             // 标志字节
152. switch (ord($byte))
153. {
154. case 1:                     // 标志字节为1,表示国家和区域信息都被同时重定向
155. $countryOffset = $this->getlong3();  // 重定向地址
156. fseek($this->fp, $countryOffset);
157. $byte = fread($this->fp, 1);     // 标志字节
158. switch (ord($byte))
159. {
160. case 2:             // 标志字节为2,表示国家信息又被重定向
161. fseek($this->fp, $this->getlong3());
162. $location['country'] = $this->getstring();
163. fseek($this->fp, $countryOffset + 4);
164. $location['area'] = $this->getarea();
165. break;
166. default:            // 否则,表示国家信息没有被重定向
167. $location['country'] = $this->getstring($byte);
168. $location['area'] = $this->getarea();
169. break;
170. }
171. break;
172. case 2:                     // 标志字节为2,表示国家信息被重定向
173. fseek($this->fp, $this->getlong3());
174. $location['country'] = $this->getstring();
175. fseek($this->fp, $offset + 8);
176. $location['area'] = $this->getarea();
177. break;
178. default:                    // 否则,表示国家信息没有被重定向
179. $location['country'] = $this->getstring($byte);
180. $location['area'] = $this->getarea();
181. break;
182. }
183.  
184. if ($location['country'] == " CZ88.NET")        // CZ88.NET表示没有有效信息
185. {
186. $location['country'] = "未知";
187. }
188. if ($location['area'] == " CZ88.NET")
189. {
190. $location['area'] = "";
191. }
192. return $location;
193. }
194.  
195. /**
196. * 获取客户端IP地址
197. * */
198. function clientIp(){
199. if(getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
200. $onlineip = getenv('HTTP_CLIENT_IP');
201. } elseif(getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
202. $onlineip = getenv('HTTP_X_FORWARDED_FOR');
203. } elseif(getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
204. $onlineip = getenv('REMOTE_ADDR');
205. } elseif(isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
206. $onlineip = $_SERVER['REMOTE_ADDR'];
207. }
208. preg_match("/[\d\.]{7,15}/", $onlineip, $onlineipmatches);
209. $onlineip = $onlineipmatches[0] ? $onlineipmatches[0] : 'unknown';
210. unset($onlineipmatches);
211. return $onlineip;
212. }
213.  
214. /**
215. * 构造函数,打开 QQWry.Dat 文件并初始化类中的信息
216. * @param string $filename
217. * @return IpLocation
218. */
219. function IpLocation($filename = "")
220. {
221. if(!$filename) $filename = dirname(__FILE__) . '/qqwry/QQWry.Dat';
222. if(!file_exists($filename)) exit('qqwry.dat is not exists!');
223. if (($this->fp = @fopen($filename, 'rb')) !== false)
224. {
225. $this->firstip = $this->getlong();
226. $this->lastip = $this->getlong();
227. $this->totalip = ($this->lastip - $this->firstip) / 7;
228. register_shutdown_function(array(&$this, '_IpLocation'));
229. }
230. }
231.  
232. /**
233. * 析构函数,用于在页面执行结束后自动关闭打开的文件。
234. */
235. function _IpLocation()
236. {
237. fclose($this->fp);
238. }
239. }

附件下载:
iplocation.class.rar 2.93MB

« 最近写的一个JS排版工具| JS判断浏览器版本号»

引用地址:

评论

凯凯 2012-06-05 08:58
嗯,不错,很ok,谢谢楼主咯~~~
Gavin 2011-09-16 11:48
哈哈
Gavin 2011-09-16 11:44
可以,值得收藏,博主转载你的,没关系吧
流水 2011-04-05 17:34
嗯嗯,正在写一个小程序,要用到IP地址和地理位置,谢谢博主!
闲逛 2010-12-28 19:02
有些地址返回的是国家,有些是国家的省份城市,不知道有没有单纯的返回国家代码的IP数据库
杨银川 2010-11-23 11:45
我用 php调用的时候出现乱码
如何解决?
Array ( [ip] => 115.148.101.72 [beginip] => 115.148.68.0 [endip] => 115.148.127.255 [country] => ??ηʡ?Ӗ݊Њ [area] => ?琅 )
博主回复:需要注意编码,如果你用的是utf8编码的话需要进行转换,具体是从gbk到utf8,编码转换可以参考:http://www.phper.org.cn/?post=54
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值