首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用PHP从本地保存的GeoIP数据库获取数据

使用PHP从本地保存的GeoIP数据库获取数据
EN

Stack Overflow用户
提问于 2018-08-20 17:09:01
回答 1查看 516关注 0票数 1

很久以前,我使用php中的一些简单函数访问Maxmind本地GeoIP数据库副本。

基本上,他们的数据库使用的模式如下:

代码语言:javascript
复制
CREATE TABLE `geoip_city_blocks` (
    `startIpNum` INT(10) UNSIGNED NOT NULL,
    `endIpNum` INT(10) UNSIGNED NOT NULL,
    `locId` INT(10) UNSIGNED NOT NULL,
    PRIMARY KEY (`startIpNum`, `endIpNum`),
    INDEX `startIpNum` (`startIpNum`),
    INDEX `endIpNum` (`endIpNum`),
    INDEX `locId` (`locId`)
)

为了获取某个IP的国家/城市信息,您只需将其转换为与之对应的数字类型:

代码语言:javascript
复制
$numeric_ip = ip2num($ip);

其中ip2num()是:

代码语言:javascript
复制
function ip2num($ip) {

        $ip = explode(".",$ip);
        return (( (int) $ip[0] ) * 16777216) + (( (int) $ip[1] ) * 65536) + (( (int) $ip[2] ) * 256) + (( (int) $ip[3] ) * 1);

    }

然后进行一个简单的查询:

代码语言:javascript
复制
SELECT * FROM geoip_city_blocks AS blocks LEFT JOIN geoip_city_locations AS locations ON (blocks.locId = locations.locId) WHERE ".$numeric_ip." >= startIpNum AND ".$numeric_ip." <= endIpNum LIMIT 1

这是好的,因为任何数据库MySQL,SQLite,Postgre..。等等,你可以用这个查询来比较两个整数。

与新版本的GeoIP,您有这种新的模式:

代码语言:javascript
复制
CREATE TABLE blocks(
  "network" TEXT,
  "geoname_id" TEXT,
  "registered_country_geoname_id" TEXT,
  "represented_country_geoname_id" TEXT,
  "is_anonymous_proxy" TEXT,
  "is_satellite_provider" TEXT
);

其中网络以类似于120.120.120.120/8的方式被抑制,比如CIDR地址。没有StartIpNumEndIpNum

你可以在图像中看到它:

既然我不能通过StartIPNumEndIpNum进行搜索,我又如何进行查询呢?

EN

回答 1

Stack Overflow用户

发布于 2018-08-22 20:12:44

这是我用自己做的一件事:

没有办法知道网络是什么,所以最简单的解决方案就是把ip地址看作是这样格式化的:a.b.c.d,它可以是24.185.38.192,其中a= 24,c= 38等等。

获取一个只包含a、b和c的子字符串:

代码语言:javascript
复制
$ip_substring = 'a.b.c.';

然后执行一个do-while循环,该循环将在返回有效值或检查所有网络时结束。该查询将在数据库中搜索具有%通配符的子字符串之类的任何值,以包括任何主机和掩码:

代码语言:javascript
复制
$ip_substring .= '%';
$search_ip = (string)$ip_substring;

$sql = "SELECT * FROM blocks WHERE network LIKE $search_ip";

执行此查询后,将结果捕获为数组,然后预先检查每个单独的网络,以查看您的ip是否存在于其中:

代码语言:javascript
复制
foreach ( $result as $r ) {
     $network = $r['network'];

     // converts the network (CIDR) to an ip range
     $range = array();
     $cidr = explode( '/', $network );
     $range[0] = long2ip( ( ip2long( $cidr[0] )) & (( -1 << ( 32 - (int)$cidr[1] ) )) );
     $range[1] = long2ip( ( ip2long( $range[0] )) + pow( 2, (32 - (int)$cidr[1])) - 1 );

     // check that ip is within range
     if ( ( ip2long( $ip ) >= ip2long( $range[0] ) ) &&
          ( ip2long( $ip ) <= ip2long( $range[1] ) ) ) {
          // you can use the associated postal_code etc this is the row you were looking for
          echo $r['postal_code'];
     }
}

有一些逻辑来处理您的foreach没有返回有效网络的实例,在这种情况下,您需要通过删除ip_substring中的最后一个字符来扩大搜索范围:

意思:在最后一个循环中,您搜索24.185.38,没有返回任何内容,然后这次将搜索扩展到24.185.3。这只是一种粗糙且并非完全有效的方法,但如果您现在需要一个解决方案,它可以工作。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51935541

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档