PHP根据IP地址获取所在城市具体实现


Posted in PHP onNovember 27, 2013

文件目录:

ipLocation
-----qqwry
----------QQWry.Dat
-----ipCity.class.php

ipCity.class.php文件代码:

<?php
class ipCity {
    /**
     * 根据ip地址获取对应所在城市
     * @param type $userip 用户IP地址
     * @return string
     */
    public function getCity( $userip, $dat_path = '' ) {
        //IP数据库路径,这里用的是QQ IP数据库 20110405 纯真版
        empty( $dat_path ) && $dat_path = FCPATH . 'plugin/ipLocation/qqwry/QQWry.Dat';
        //判断IP地址是否有效
        if ( preg_match( "/^([0-9]{1,3}.){3}[0-9]{1,3}$/", $userip ) == 0 ) {
            return 'IP Address Invalid';
        }
        //打开IP数据库
        if ( !$fd = @fopen( $dat_path, 'rb' ) ) {
            return 'IP data file not exists or access denied';
        }
        //explode函数分解IP地址,运算得出整数形结果
        $userip = explode( '.', $userip );
        $useripNum = $userip[0] * 16777216 + $userip[1] * 65536 + $userip[2] * 256 + $userip[3];
        //获取IP地址索引开始和结束位置
        $DataBegin = fread( $fd, 4 );
        $DataEnd = fread( $fd, 4 );
        $useripbegin = implode( '', unpack( 'L', $DataBegin ) );
        if ( $useripbegin < 0 )
            $useripbegin += pow( 2, 32 );
        $useripend = implode( '', unpack( 'L', $DataEnd ) );
        if ( $useripend < 0 )
            $useripend += pow( 2, 32 );
        $useripAllNum = ($useripend - $useripbegin) / 7 + 1;
        $BeginNum = 0;
        $EndNum = $useripAllNum;
        //使用二分查找法从索引记录中搜索匹配的IP地址记录
        while ( $userip1num > $useripNum || $userip2num < $useripNum ) {
            $Middle = intval( ($EndNum + $BeginNum) / 2 );
            //偏移指针到索引位置读取4个字节
            fseek( $fd, $useripbegin + 7 * $Middle );
            $useripData1 = fread( $fd, 4 );
            if ( strlen( $useripData1 ) < 4 ) {
                fclose( $fd );
                return 'File Error';
            }
            //提取出来的数据转换成长整形,如果数据是负数则加上2的32次幂
            $userip1num = implode( '', unpack( 'L', $useripData1 ) );
            if ( $userip1num < 0 )
                $userip1num += pow( 2, 32 );
            //提取的长整型数大于我们IP地址则修改结束位置进行下一次循环
            if ( $userip1num > $useripNum ) {
                $EndNum = $Middle;
                continue;
            }
            //取完上一个索引后取下一个索引
            $DataSeek = fread( $fd, 3 );
            if ( strlen( $DataSeek ) < 3 ) {
                fclose( $fd );
                return 'File Error';
            }
            $DataSeek = implode( '', unpack( 'L', $DataSeek . chr( 0 ) ) );
            fseek( $fd, $DataSeek );
            $useripData2 = fread( $fd, 4 );
            if ( strlen( $useripData2 ) < 4 ) {
                fclose( $fd );
                return 'File Error';
            }
            $userip2num = implode( '', unpack( 'L', $useripData2 ) );
            if ( $userip2num < 0 )
                $userip2num += pow( 2, 32 );
            //找不到IP地址对应城市
            if ( $userip2num < $useripNum ) {
                if ( $Middle == $BeginNum ) {
                    fclose( $fd );
                    return 'No Data';
                }
                $BeginNum = $Middle;
            }
        }
        $useripFlag = fread( $fd, 1 );
        if ( $useripFlag == chr( 1 ) ) {
            $useripSeek = fread( $fd, 3 );
            if ( strlen( $useripSeek ) < 3 ) {
                fclose( $fd );
                return 'System Error';
            }
            $useripSeek = implode( '', unpack( 'L', $useripSeek . chr( 0 ) ) );
            fseek( $fd, $useripSeek );
            $useripFlag = fread( $fd, 1 );
        }
        if ( $useripFlag == chr( 2 ) ) {
            $AddrSeek = fread( $fd, 3 );
            if ( strlen( $AddrSeek ) < 3 ) {
                fclose( $fd );
                return 'System Error';
            }
            $useripFlag = fread( $fd, 1 );
            if ( $useripFlag == chr( 2 ) ) {
                $AddrSeek2 = fread( $fd, 3 );
                if ( strlen( $AddrSeek2 ) < 3 ) {
                    fclose( $fd );
                    return 'System Error';
                }
                $AddrSeek2 = implode( '', unpack( 'L', $AddrSeek2 . chr( 0 ) ) );
                fseek( $fd, $AddrSeek2 );
            } else {
                fseek( $fd, -1, SEEK_CUR );
            }
            while ( ($char = fread( $fd, 1 )) != chr( 0 ) )
                $useripAddr2 .= $char;
            $AddrSeek = implode( '', unpack( 'L', $AddrSeek . chr( 0 ) ) );
            fseek( $fd, $AddrSeek );
            while ( ($char = fread( $fd, 1 )) != chr( 0 ) )
                $useripAddr1 .= $char;
        } else {
            fseek( $fd, -1, SEEK_CUR );
            while ( ($char = fread( $fd, 1 )) != chr( 0 ) )
                $useripAddr1 .= $char;
            $useripFlag = fread( $fd, 1 );
            if ( $useripFlag == chr( 2 ) ) {
                $AddrSeek2 = fread( $fd, 3 );
                if ( strlen( $AddrSeek2 ) < 3 ) {
                    fclose( $fd );
                    return 'System Error';
                }
                $AddrSeek2 = implode( '', unpack( 'L', $AddrSeek2 . chr( 0 ) ) );
                fseek( $fd, $AddrSeek2 );
            } else {
                fseek( $fd, -1, SEEK_CUR );
            }
            while ( ($char = fread( $fd, 1 )) != chr( 0 ) ) {
                $useripAddr2 .= $char;
            }
        }
        fclose( $fd );
        //返回IP地址对应的城市结果
        if ( preg_match( '/http/i', $useripAddr2 ) ) {
            $useripAddr2 = '';
        }
        $useripaddr = "$useripAddr1 $useripAddr2";
        $useripaddr = preg_replace( '/CZ88.Net/is', '', $useripaddr );
        $useripaddr = preg_replace( '/^s*/is', '', $useripaddr );
        $useripaddr = preg_replace( '/s*$/is', '', $useripaddr );
        if ( preg_match( '/http/i', $useripaddr ) || $useripaddr == '' ) {
            $useripaddr = 'No Data';
        } elseif ( !$this->is_utf8( $useripaddr ) ) {
            $useripaddr = iconv( 'GBK', 'UTF-8', $useripaddr );
        }
        return $useripaddr;
    }
    /**
     * 判断是否我utf-8编码的字符串
     * @param type $string
     * @return boolean
     */
    private function is_utf8( $string ) {
        if ( preg_match( "/^([" . chr( 228 ) . "-" . chr( 233 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}){1}/", $string ) == true || preg_match( "/([" . chr( 228 ) . "-" . chr( 233 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}){1}$/", $string ) == true || preg_match( "/([" . chr( 228 ) . "-" . chr( 233 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}[" . chr( 128 ) . "-" . chr( 191 ) . "]{1}){2,}/", $string ) == true ) {
            return true;
        } else {
            return false;
        }
    }
}

QQWry.Dat文件下载地址:http://xiazai.3water.com/201311/yuanma/qqwry.dat(3water.com).zip

使用演示:

include FCPATH . 'plugin/ipLocation/ipCity.class.php';
$city = new ipCity();
$addr = $city->getCity( '172.0.0.1' );
echo $addr; // echo 本地地址
PHP 相关文章推荐
用PHP控制用户的浏览器--ob*函数的使用说明
Mar 16 PHP
php单件模式结合命令链模式使用说明
Sep 07 PHP
php printf输出格式使用说明
Dec 05 PHP
PHP imagecreatefrombmp 从BMP文件或URL新建一图像
Jul 16 PHP
分享下PHP register_globals 值为on与off的理解
Sep 26 PHP
php获取客户端电脑屏幕参数的方法
Jan 09 PHP
PHP处理postfix邮件内容的方法
Jun 16 PHP
WordPress的主题编写中获取头部模板和底部模板
Dec 28 PHP
详解PHP原生DOM对象操作XML的方法
Oct 17 PHP
Laravel中七个非常有用但很少人知道的Carbon方法
Sep 21 PHP
Windows下wamp php单元测试工具PHPUnit安装及生成日志文件配置方法
May 28 PHP
PHP上传图片到数据库并显示的实例代码
Dec 20 PHP
php编写的简单页面跳转功能实现代码
Nov 27 #PHP
关于JSON以及JSON在PHP中的应用技巧
Nov 27 #PHP
XAMPP安装与使用方法详细解析
Nov 27 #PHP
浅析echo(),print(),print_r(),return之间的区别
Nov 27 #PHP
PHP echo,print,printf,sprintf函数之间的区别与用法详解
Nov 27 #PHP
php strnatcmp()函数的用法总结
Nov 27 #PHP
PHP 正则判断中文UTF-8或GBK的思路及具体实现
Nov 26 #PHP
You might like
PHP实现手机号码中间四位用星号(*)隐藏的自定义函数分享
2014/09/27 PHP
详解PHP中的状态模式编程
2015/08/11 PHP
json 定义
2008/06/10 Javascript
Select标签下拉列表二级联动级联实例代码
2014/02/07 Javascript
简单js代码实现selece二级联动(推荐)
2014/02/18 Javascript
SeaJS入门教程系列之SeaJS介绍(一)
2014/03/03 Javascript
js浏览器本地存储store.js介绍及应用
2014/05/13 Javascript
JavaScript实现上下浮动的窗口效果代码
2015/10/12 Javascript
js控件Kindeditor实现图片自动上传功能
2020/07/20 Javascript
jQuery购物网页经典制作案例
2016/08/19 Javascript
基于JS实现弹出一个隐藏的div窗口body页面变成灰色并且不可被编辑
2016/12/14 Javascript
浅析Vue项目中使用keep-Alive步骤
2018/07/27 Javascript
Vue常用的几个指令附完整案例
2018/11/06 Javascript
如何对react hooks进行单元测试的方法
2019/08/14 Javascript
详解element-ui级联菜单(城市三级联动菜单)和回显问题
2019/10/02 Javascript
微信小程序学习总结(三)条件、模板、文件引用实例分析
2020/06/04 Javascript
[03:28]2014DOTA2国际邀请赛 EG战队官方纪录片
2014/07/21 DOTA
python中stdout输出不缓存的设置方法
2014/05/29 Python
python批量提取word内信息
2015/08/09 Python
多版本Python共存的配置方法
2017/05/22 Python
Windows下Python3.6安装第三方模块的方法
2018/11/22 Python
Flask框架web开发之零基础入门
2018/12/10 Python
Python使用统计函数绘制简单图形实例代码
2019/05/15 Python
Keras - GPU ID 和显存占用设定步骤
2020/06/22 Python
pyspark对Mysql数据库进行读写的实现
2020/12/30 Python
利用css3 translate完美实现表头固定效果
2017/02/28 HTML / CSS
兰芝美国网上商城:购买LANEIGE睡眠面膜等
2017/06/30 全球购物
美国知名珠宝首饰品牌:Gemvara
2017/10/06 全球购物
澳大利亚宠物食品和用品商店:PETstock
2020/01/02 全球购物
迪卡侬(Decathlon)加拿大官网:源自法国的运动专业超市
2020/11/22 全球购物
大一新生军训时的自我评价分享
2013/12/05 职场文书
薪酬专员岗位职责
2014/02/18 职场文书
小学运动会班级口号
2014/06/09 职场文书
党的群众路线对照检查材料思想汇报
2014/09/25 职场文书
2016高考冲刺决心书
2015/09/23 职场文书
MySQL系列之开篇 MySQL关系型数据库基础概念
2021/07/02 MySQL