以前,我曾经使用php中的一些简单功能访问Geomin数据库的Maxmind本地副本。
基本上,他们的数据库具有类似以下的模式:
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的国家/城市信息,您只需使用以下命令将其转换为对应的数字:
$numeric_ip = ip2num($ip);
其中 ip2num()是:
function ip2num($ip) {
$ip = explode(".",$ip);
return (( (int) $ip[0] ) * 16777216) + (( (int) $ip[1] ) * 65536) + (( (int) $ip[2] ) * 256) + (( (int) $ip[3] ) * 1);
}
然后进行简单的查询:
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 ..等,您都可以cas进行查询并只比较2个整数。
使用新版本的GeoIP,您将拥有这种新型架构:
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地址)压缩网络的地方。而没有StartIpNum
和EndIpNum
您可以在图像中看到它:
现在我无法通过StartIPNum
和EndIpNum
进行搜索,该如何进行查询?
答案 0 :(得分:1)
这是我惯用的一项工作:
无法知道网络是什么,因此最简单的解决方案是像查看IP地址一样格式化它:a.b.c.d
可以是24.185.38.192
,其中a = 24, c = 38,等等。
获取仅包含a,b和c的子字符串:
$ip_substring = 'a.b.c.';
然后执行一个do-while循环,该循环将在返回有效值或检查所有网络时结束。该查询将在数据库中搜索任何值,例如带有%通配符的子字符串,以包括所有主机和掩码:
$ip_substring .= '%';
$search_ip = (string)$ip_substring;
$sql = "SELECT * FROM blocks WHERE network LIKE $search_ip";
执行此查询后,将结果捕获为数组,进行遍历,然后可以检查每个单独的网络以查看其中是否存在您的ip:
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
。这只是一种粗略且并非完全有效的方法,但是如果您现在需要解决方案,它就会起作用。