昨天在写程序的时候,发现在用户的时候记录IP和地区信息也许以后用得上,去网上找了找,发现实现的方式有好多好多,因为我用的ThinkPHP,后来又去TP官网找了找,最后采用了下面这种方法。

 <?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2009 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +---------------------------------------------------------------------- /**
* IP 地理位置查询类 修改自 CoolCode.CN
* 由于使用UTF8编码 如果使用纯真IP地址库的话 需要对返回结果进行编码转换
* @author liu21st <liu21st@gmail.com>
*/
class IpLocation {
/**
* QQWry.Dat文件指针
*
* @var resource
*/
private $fp; /**
* 第一条IP记录的偏移地址
*
* @var int
*/
private $firstip; /**
* 最后一条IP记录的偏移地址
*
* @var int
*/
private $lastip; /**
* IP记录的总条数(不包含版本信息记录)
*
* @var int
*/
private $totalip; /**
* 构造函数,打开 QQWry.Dat 文件并初始化类中的信息
*
* @param string $filename
* @return IpLocation
*/
public function __construct($filename = "UTFWry.dat") {
$this->fp = 0;
if (($this->fp = fopen(dirname(__FILE__).'/'.$filename, 'rb')) !== false) {
$this->firstip = $this->getlong();
$this->lastip = $this->getlong();
$this->totalip = ($this->lastip - $this->firstip) / 7;
}
} /**
* 返回读取的长整型数
*
* @access private
* @return int
*/
private function getlong() {
//将读取的little-endian编码的4个字节转化为长整型数
$result = unpack('Vlong', fread($this->fp, 4));
return $result['long'];
} /**
* 返回读取的3个字节的长整型数
*
* @access private
* @return int
*/
private function getlong3() {
//将读取的little-endian编码的3个字节转化为长整型数
$result = unpack('Vlong', fread($this->fp, 3).chr(0));
return $result['long'];
} /**
* 返回压缩后可进行比较的IP地址
*
* @access private
* @param string $ip
* @return string
*/
private function packip($ip) {
// 将IP地址转化为长整型数,如果在PHP5中,IP地址错误,则返回False,
// 这时intval将Flase转化为整数-1,之后压缩成big-endian编码的字符串
return pack('N', intval(ip2long($ip)));
} /**
* 返回读取的字符串
*
* @access private
* @param string $data
* @return string
*/
private function getstring($data = "") {
$char = fread($this->fp, 1);
while (ord($char) > 0) { // 字符串按照C格式保存,以\0结束
$data .= $char; // 将读取的字符连接到给定字符串之后
$char = fread($this->fp, 1);
}
return $data;
// return iconv('gb2312', 'utf-8', $data); //通用函数,不能在这转
} /**
* 返回地区信息
*
* @access private
* @return string
*/
private function getarea() {
$byte = fread($this->fp, 1); // 标志字节
switch (ord($byte)) {
case 0: // 没有区域信息
$area = "";
break;
case 1:
case 2: // 标志字节为1或2,表示区域信息被重定向
fseek($this->fp, $this->getlong3());
$area = $this->getstring();
break;
default: // 否则,表示区域信息没有被重定向
$area = $this->getstring($byte);
break;
}
return $area;
// return iconv('gb2312', 'utf-8', $area); //在最后统一转
} /**
* 根据所给 IP 地址或域名返回所在地区信息
*
* @access public
* @param string $ip
* @return array
*/
public function getlocation($ip='') {
if (!$this->fp) return null; // 如果数据文件没有被正确打开,则直接返回空
if(empty($ip)) $ip = get_client_ip();
$location['ip'] = gethostbyname($ip); // 将输入的域名转化为IP地址
$ip = $this->packip($location['ip']); // 将输入的IP地址转化为可比较的IP地址
// 不合法的IP地址会被转化为255.255.255.255
// 对分搜索
$l = 0; // 搜索的下边界
$u = $this->totalip; // 搜索的上边界
$findip = $this->lastip; // 如果没有找到就返回最后一条IP记录(QQWry.Dat的版本信息)
while ($l <= $u) { // 当上边界小于下边界时,查找失败
$i = floor(($l + $u) / 2); // 计算近似中间记录
fseek($this->fp, $this->firstip + $i * 7);
$beginip = strrev(fread($this->fp, 4)); // 获取中间记录的开始IP地址
// strrev函数在这里的作用是将little-endian的压缩IP地址转化为big-endian的格式
// 以便用于比较,后面相同。
if ($ip < $beginip) { // 用户的IP小于中间记录的开始IP地址时
$u = $i - 1; // 将搜索的上边界修改为中间记录减一
}
else {
fseek($this->fp, $this->getlong3());
$endip = strrev(fread($this->fp, 4)); // 获取中间记录的结束IP地址
if ($ip > $endip) { // 用户的IP大于中间记录的结束IP地址时
$l = $i + 1; // 将搜索的下边界修改为中间记录加一
}
else { // 用户的IP在中间记录的IP范围内时
$findip = $this->firstip + $i * 7;
break; // 则表示找到结果,退出循环
}
}
} //获取查找到的IP地理位置信息
fseek($this->fp, $findip);
$location['beginip'] = long2ip($this->getlong()); // 用户IP所在范围的开始地址
$offset = $this->getlong3();
fseek($this->fp, $offset);
$location['endip'] = long2ip($this->getlong()); // 用户IP所在范围的结束地址
$byte = fread($this->fp, 1); // 标志字节
switch (ord($byte)) {
case 1: // 标志字节为1,表示国家和区域信息都被同时重定向
$countryOffset = $this->getlong3(); // 重定向地址
fseek($this->fp, $countryOffset);
$byte = fread($this->fp, 1); // 标志字节
switch (ord($byte)) {
case 2: // 标志字节为2,表示国家信息又被重定向
fseek($this->fp, $this->getlong3());
$location['country'] = $this->getstring();
fseek($this->fp, $countryOffset + 4);
$location['area'] = $this->getarea();
break;
default: // 否则,表示国家信息没有被重定向
$location['country'] = $this->getstring($byte);
$location['area'] = $this->getarea();
break;
}
break;
case 2: // 标志字节为2,表示国家信息被重定向
fseek($this->fp, $this->getlong3());
$location['country'] = $this->getstring();
fseek($this->fp, $offset + 8);
$location['area'] = $this->getarea();
break;
default: // 否则,表示国家信息没有被重定向
$location['country'] = $this->getstring($byte);
$location['area'] = $this->getarea();
break;
}
if (trim($location['country']) == 'CZ88.NET') { // CZ88.NET表示没有有效信息
$location['country'] = '未知';
}
if (trim($location['area']) == 'CZ88.NET') {
$location['area'] = '';
} $location['country']=iconv('gb2312', 'utf-8', $location['country']);
$location['area']=iconv('gb2312', 'utf-8', $location['area']); return $location;
} /**
* 析构函数,用于在页面执行结束后自动关闭打开的文件。
*
*/
public function __destruct() {
if ($this->fp) {
fclose($this->fp);
}
$this->fp = 0;
} }

IpLocation.class.php

  

  这个是TP里带的,从上面的信息上来看已经很旧了,不过感觉写的还不错,就没改什么,只是在后面加上了

$location['country']=iconv('gb2312', 'utf-8', $location['country']);
$location['area']=iconv('gb2312', 'utf-8', $location['area']);

  

  因为TP的纯真数据库已经转码了,但是很旧,似乎还是2012年的,IP这东西,那么老的东西估计很不准,于是我去纯真官网下了一个,但是纯真本身是GB2312的,所以要加上上面这两句转码一下。
然后在项目中导入了这个PHP类以后,这样去用就可以。

$ip=get_client_ip();
$ipData=new IpLocation("QQWry20140125.dat");
$ipInfo=$ipData->getlocation($ip);

  

  得到的结果是这样的,测试IP:218.197.147.154。

array (size=5)
'ip' => string '218.197.147.154' (length=15)
'beginip' => string '218.197.146.1' (length=13)
'endip' => string '218.197.147.154' (length=15)
'country' => string '湖北省武汉大学' (length=21)
'area' => string '外语自主学习中心' (length=24)

  

  最新版的纯真数据库去纯真官网下就可以,里面的QQWry.dat就是,用的时候也可以像我那样在后面标上版本,免得以后记不清。然后把.dat跟上面的php类放在一个目录下就OK啦。

PHP获取IP及地区信息(纯真IP数据库)的更多相关文章

  1. python爬虫爬取ip记录网站信息并存入数据库

    import requests import re import pymysql #10页 仔细观察路由 db = pymysql.connect("localhost",&quo ...

  2. 用ip来获得用户所在地区信息

    淘宝api: package com.ebways.mq.utils; import com.alibaba.fastjson.JSON; import com.ebways.common.utils ...

  3. 新浪网易淘宝等IP地区信息查询开放API接口调用方法

    通过IP地址获取对应的地区信息通常有两种方法:1)自己写程序,解析IP对应的地区信息,需要数据库.2)根据第三方提供的API查询获取地区信息. 第一种方法,参见文本<通过纯真IP数据库获取IP地 ...

  4. Java使用纯真IP库获取IP对应省份和城市

    原文:http://blog.csdn.net/chwshuang/article/details/78027873?locationNum=10&fps=1 Java使用纯真IP库获取IP对 ...

  5. 转载纯真ip库

    http://blog.csdn.net/clin003/archive/2007/08/14/1743157.aspx 利用 QQWry.Dat 实现 IP 地址高效检索(PHP) 根据 LumaQ ...

  6. API之IP地址查询---权威的IP地址查询接口集合

    原文地址:http://yushine.iteye.com/blog/1717586 推荐实用IP138 http://www.baidu.com/s?wd=IP&rsv_spt=1& ...

  7. 优化读取纯真IP数据库QQWry.dat获取地区信息

    改自HeDaode 2007-12-28的代码 将之改为从硬盘读取后文件后,将MemoryStream放到内存中,提高后续查询速度 ///<summary> /// 提供从纯真IP数据库搜 ...

  8. PHP获取访客IP、地区位置信息、浏览器、来源页面

    不多说了,每个方法都注释了,可以直接用: <?php //这个类似用来获取访客信息的 //方便统计 class visitorInfo { //获取访客ip public function ge ...

  9. 使用纯真IP库获取用户端地理位置信息

    引言 在一些电商类或者引流类的网站中经常会有获取用户地理位置信息的需求,下面我分享一个用纯真IP库获取用户地理位置信息的方案. 正文 第一步:本文的方案是基于纯真IP库的,所以首先要去下载最新的纯真I ...

随机推荐

  1. js中常用属性备忘

    . onsubmit一般用来做验证的,用来控制表单提交的. 之前使用数据提交基本上使用ajax做数据提交,在数据验证的时候,只有通过数据验证之后,才会post/get数据到对应api文件,但是这次涉及 ...

  2. 图片onerror(转)

    <script type="text/javascript"> <!– function nofind(){ var img=event.srcElement; ...

  3. Debian安装Apache2+MySQL5+PHP5(zz)

    转载:http://hi.baidu.com/lostdays/item/1d5e7e4833b4d20fc116134b 终于在Debian用apt-get安装好LAMP了,之前在CentOS使用编 ...

  4. this的分析分支

    最近看到这个题目,开始不太理解,但是仔细的看完this之后,觉得懂了一些 function Foo() { getName = function () { alert (1); }; return t ...

  5. Linux学习之路:命令别名、历史记录和命令查找执行顺序

    一.命令别名 alias rm='rm –i':删除命令时会随时出现提示;alias vi=vim alias 不加参数,显示系统内所以命令别名 unalias 取消别名 二.历史命令 history ...

  6. Linux 下安装配置nginx及常见问题解答

    其实也不能完全算是原创吧!都是我配置nginx时所遇到的问题,查阅资料后总结起来.即是巩固一下nginx的配置,也是分享给新入Linux的童鞋们一些知识 好了,不多废话,进入主题吧! 为nginx添加 ...

  7. 给jdk写注释系列之jdk1.6容器(12)-PriorityQueue源码解析

    PriorityQueue是一种什么样的容器呢?看过前面的几个jdk容器分析的话,看到Queue这个单词你一定会,哦~这是一种队列.是的,PriorityQueue是一种队列,但是它又是一种什么样的队 ...

  8. CF Playing with Paper

    Playing with Paper time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  9. hdu 4605 线段树与二叉树遍历

    思路: 首先将所有的查询有一个vector保存起来.我们从1号点开始dfs这颗二叉树,用线段树记录到当前节点时,走左节点的有多少比要查询该节点的X值小的,有多少大的, 同样要记录走右节点的有多少比X小 ...

  10. 【AngularJs】---$sce 输出Html

    [问题描述] angular js的强大之处之一就是他的数据双向绑定功能----->ng-bind和针对form的ng-model 但在我们的项目当中会遇到这样的情况,后台返回的数据中带有各种各 ...