转载自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