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

使用PHP从本地保存的GeoIP数据库获取数据,php,sql,geoip,Php,Sql,Geoip,很久以前,我使用php中的一些简单函数访问GeoIP数据库的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`,

很久以前,我使用php中的一些简单函数访问GeoIP数据库的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
其中网络以类似于CIDR地址的120.120.120.120/8的方式显示。。没有
StartIpNum
EndIpNum

您可以在图像中看到它:


现在我无法通过
StartIPNum
EndIpNum
进行搜索,我如何进行查询?

以下是我自己使用的解决方案:

没有办法知道网络是什么,因此最简单的解决方案是将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($r结果){
$network=$r['network'];
//将网络(CIDR)转换为ip范围
$range=array();
$cidr=explode(“/”,$network);
$range[0]=long2ip((ip2long($cidr[0]))和(-1=ip2long($range[0]))&&

(ip2long($ip)你的DBMS是什么?看看这个主题:@stickybit我同时使用MySQL和另一组依赖SQLite的脚本(最后,由于各种原因,我使用越来越多的SQLite)。它们都使用相同的表模式。
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'];
     }
}