PHP binding IPv6 supports is ready
This commit is contained in:
parent
95c129c7d1
commit
7bbe7d681d
|
|
@ -2,30 +2,57 @@
|
||||||
|
|
||||||
# 使用方式
|
# 使用方式
|
||||||
|
|
||||||
第三方 composer 地址:
|
第三方 composer 地址 (请自行确认仓库对 IPv6 的支持):
|
||||||
1. [https://github.com/zoujingli/ip2region](https://github.com/zoujingli/ip2region)
|
1. [https://github.com/zoujingli/ip2region](https://github.com/zoujingli/ip2region)
|
||||||
2. [https://github.com/chinayin/ip2region-core-php](https://github.com/chinayin/ip2region-core-php)
|
2. [https://github.com/chinayin/ip2region-core-php](https://github.com/chinayin/ip2region-core-php)
|
||||||
|
|
||||||
|
|
||||||
|
### 关于 IPv4 和 IPv6
|
||||||
|
该 xdb 查询客户端实现同时支持对 IPv4 和 IPv6 的查询,使用方式如下:
|
||||||
|
```php
|
||||||
|
use \ip2region\xdb\{IPv4, IPv6};
|
||||||
|
|
||||||
|
// 如果是 IPv4: 设置 xdb 路径为 v4 的 xdb 文件,IP版本指定为 IPv4
|
||||||
|
$dbFile = "../../data/ip2region_v4.xdb"; // 或者你的 ipv4 xdb 的路径
|
||||||
|
$version = IPv4::default();
|
||||||
|
|
||||||
|
// 如果是 IPv6: 设置 xdb 路径为 v6 的 xdb 文件,IP版本指定为 IPv6
|
||||||
|
$dbFile = "../../data/ip2region_v6.xdb"; // 或者你的 ipv6 xdb 路径
|
||||||
|
$version = IPv6::default();
|
||||||
|
|
||||||
|
// dbPath 指定的 xdb 的 IP 版本必须和 version 指定的一致,不然查询执行的时候会报错
|
||||||
|
// 备注:以下演示直接使用 $dbFile 和 $version 变量
|
||||||
|
```
|
||||||
|
|
||||||
### 完全基于文件的查询
|
### 完全基于文件的查询
|
||||||
```php
|
```php
|
||||||
$dbFile = "ip2region.xdb file path";
|
// require or autoload the xdb\Searcher
|
||||||
|
require 'xdb\Searcher.php';
|
||||||
|
use \ip2region\xdb\Util;
|
||||||
|
use \ip2region\xdb\Searcher;
|
||||||
|
|
||||||
|
// 1, 使用上述的 $version 和 $dbFile 创建 Searcher 对象
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithFileOnly($dbFile);
|
$searcher = Searcher::newWithFileOnly($version, $dbFile);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create searcher with '%s': %s\n", $dbFile, $e);
|
printf("failed to create searcher with '%s': %s\n", $dbFile, $e->getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ip = '1.2.3.4';
|
// 2, 查询,IPv4 或者 IPv6 的地址都支持
|
||||||
$sTime = XdbSearcher::now();
|
try {
|
||||||
$region = $searcher->search($ip);
|
$ip = '1.2.3.4';
|
||||||
if ($region === null) {
|
// $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6
|
||||||
// something is wrong
|
$sTime = Util::now();
|
||||||
printf("failed search(%s)\n", $ip);
|
$region = $searcher->search($ip);
|
||||||
return;
|
$costMs = Util::now() - $sTime;
|
||||||
|
printf("{region: %s, took: %.5f ms}\n", $region, $costMs);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to search(%s): %s", $ip, $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("{region: %s, took: %.5f ms}\n", $region, XdbSearcher::now() - $sTime);
|
// 3,关闭资源
|
||||||
|
$searcher->close();
|
||||||
|
|
||||||
// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象。
|
// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象。
|
||||||
```
|
```
|
||||||
|
|
@ -34,62 +61,84 @@ printf("{region: %s, took: %.5f ms}\n", $region, XdbSearcher::now() - $sTime);
|
||||||
|
|
||||||
如果你的 php 母环境支持,可以预先加载 vectorIndex 缓存,然后做成全局变量,每次创建 Searcher 的时候使用全局的 vectorIndex,可以减少一次固定的 IO 操作从而加速查询,减少 io 压力。
|
如果你的 php 母环境支持,可以预先加载 vectorIndex 缓存,然后做成全局变量,每次创建 Searcher 的时候使用全局的 vectorIndex,可以减少一次固定的 IO 操作从而加速查询,减少 io 压力。
|
||||||
```php
|
```php
|
||||||
// 1、从 dbPath 加载 VectorIndex 缓存,把下述的 vIndex 变量缓存到内存里面。
|
// require or autoload the xdb\Searcher
|
||||||
$vIndex = XdbSearcher::loadVectorIndexFromFile($dbPath);
|
require 'xdb\Searcher.php';
|
||||||
|
use \ip2region\xdb\Util;
|
||||||
|
use \ip2region\xdb\Searcher;
|
||||||
|
|
||||||
|
|
||||||
|
// 1、从 $dbFile 加载 VectorIndex 缓存,把下述的 vIndex 变量缓存到内存里面。
|
||||||
|
$vIndex = Util::loadVectorIndexFromFile($dbFile);
|
||||||
if ($vIndex === null) {
|
if ($vIndex === null) {
|
||||||
printf("failed to load vector index from '%s'\n", $dbPath);
|
printf("failed to load vector index from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
|
// 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithVectorIndex($dbFile, $vIndex);
|
$searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create vectorIndex cached searcher with '%s': %s\n", $dbFile, $e);
|
printf("failed to create vectorIndex searcher with '%s': %s\n", $dbFile, $e->getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3、查询
|
// 3、查询,IPv4 或者 IPv6 都支持
|
||||||
$sTime = XdbSearcher::now();
|
try {
|
||||||
$region = $searcher->search('1.2.3.4');
|
$ip = '1.2.3.4';
|
||||||
if ($region === null) {
|
// $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6
|
||||||
printf("failed search(1.2.3.4)\n");
|
$sTime = Util::now();
|
||||||
return;
|
$region = $searcher->search($ip);
|
||||||
|
$costMs = Util::now() - $sTime;
|
||||||
|
printf("{region: %s, took: %.5f ms}\n", $region, $costMs);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to search(%s): %s", $ip, $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("{region: %s, took: %.5f ms}\n", $region, XdbSearcher::now() - $sTime);
|
// 4, 关闭资源
|
||||||
|
$searcher->close();
|
||||||
|
|
||||||
// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象,但是都共享统一的只读 vectorIndex。
|
// 备注:并发使用,每个线程或者协程需要创建一个独立的 searcher 对象,但是都共享统一的只读全局 vectorIndex。。
|
||||||
```
|
```
|
||||||
|
|
||||||
### 缓存整个 `xdb` 数据
|
### 缓存整个 `xdb` 数据
|
||||||
|
|
||||||
如果你的 PHP 母环境支持,可以预先加载整个 `xdb` 的数据到内存,这样可以实现完全基于内存的查询,类似之前的 memory search 查询。
|
如果你的 PHP 母环境支持,可以预先加载整个 `xdb` 的数据到内存,这样可以实现完全基于内存的查询,类似之前的 memory search 查询。
|
||||||
```php
|
```php
|
||||||
// 1、从 dbPath 加载整个 xdb 到内存。
|
// require or autoload the xdb\Searcher
|
||||||
$cBuff = XdbSearcher::loadContentFromFile($dbPath);
|
require 'xdb\Searcher.php';
|
||||||
|
use \ip2region\xdb\Util;
|
||||||
|
use \ip2region\xdb\Searcher;
|
||||||
|
|
||||||
|
// 1、从 $dbFile 加载整个 xdb 到内存。
|
||||||
|
$cBuff = Util::loadContentFromFile($dbFile);
|
||||||
if ($cBuff === null) {
|
if ($cBuff === null) {
|
||||||
printf("failed to load content buffer from '%s'\n", $dbPath);
|
printf("failed to load content buffer from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2、使用全局的 cBuff 创建带完全基于内存的查询对象。
|
// 2、使用全局的 cBuff 创建带完全基于内存的查询对象。
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithBuffer($cBuff);
|
$searcher = Searcher::newWithBuffer($version, $cBuff);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create buffer cached searcher: %s\n", $dbFile, $e);
|
printf("failed to create buffer cached searcher: %s\n", $dbFile, $e->getMessage());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3、查询
|
// 3、查询,IPv4 或者 IPv6 都支持
|
||||||
$sTime = XdbSearcher::now();
|
try {
|
||||||
$region = $searcher->search('1.2.3.4');
|
$ip = '1.2.3.4';
|
||||||
if ($region === null) {
|
// $ip = "2001:4:112:ffff:ffff:ffff:ffff:ffff"; // IPv6
|
||||||
printf("failed search(1.2.3.4)\n");
|
$sTime = Util::now();
|
||||||
return;
|
$region = $searcher->search($ip);
|
||||||
|
$costMs = Util::now() - $sTime;
|
||||||
|
printf("{region: %s, took: %.5f ms}\n", $region, $costMs);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to search(%s): %s", $ip, $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("{region: %s, took: %.5f ms}\n", $region, XdbSearcher::now() - $sTime);
|
// 4,关闭资源
|
||||||
|
// 该 searcher 对象可以安全的用于并发,等整个服务都关闭的时候再关闭 searcher
|
||||||
|
// $searcher->close();
|
||||||
|
|
||||||
// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。
|
// 备注:并发使用,用整个 xdb 缓存创建的 searcher 对象可以安全用于并发。
|
||||||
```
|
```
|
||||||
|
|
@ -98,21 +147,33 @@ printf("{region: %s, took: %.5f ms}\n", $region, XdbSearcher::now() - $sTime);
|
||||||
|
|
||||||
通过 `search_test.php` 脚本来进行查询测试:
|
通过 `search_test.php` 脚本来进行查询测试:
|
||||||
```bash
|
```bash
|
||||||
➜ php git:(v2.0_xdb) ✗ php ./search_test.php
|
➜ php git:(fr_php_ipv6) ✗ php search_test.php
|
||||||
php ./search_test.php [command options]
|
php search_test.php [command options]
|
||||||
options:
|
options:
|
||||||
--db string ip2region binary xdb file path
|
--db string ip2region binary xdb file path
|
||||||
--cache-policy string cache policy: file/vectorIndex/content
|
--cache-policy string cache policy: file/vectorIndex/content
|
||||||
```
|
```
|
||||||
|
|
||||||
例如:使用默认的 data/ip2region.xdb 进行查询测试:
|
例如:使用默认的 data/ip2region_v4.xdb 进行 IPv4 的查询测试:
|
||||||
```bash
|
```bash
|
||||||
➜ php git:(v2.0_xdb) ✗ php ./search_test.php --db=../../data/ip2region.xdb --cache-policy=vectorIndex
|
➜ php git:(fr_php_ipv6) ✗ php search_test.php --db=../../data/ip2region_v4.xdb
|
||||||
ip2region xdb searcher test program, cachePolicy: vectorIndex
|
ip2region xdb searcher test program
|
||||||
|
source xdb file: ../../data/ip2region_v4.xdb (IPv4, vectorIndex)
|
||||||
type 'quit' to exit
|
type 'quit' to exit
|
||||||
ip2region>> 1.2.3.4
|
ip2region>> 120.229.45.2
|
||||||
{region: 美国|0|华盛顿|0|谷歌, ioCount: 7, took: 0.04492 ms}
|
{region: 中国|广东省|深圳市|移动, ioCount: 3, took: 0.02783 ms}
|
||||||
ip2region>>
|
```
|
||||||
|
|
||||||
|
例如:使用默认的 data/ip2region_v6.xdb 进行 IPv6 的查询测试:
|
||||||
|
```bash
|
||||||
|
➜ php git:(fr_php_ipv6) ✗ php search_test.php --db=../../data/ip2region_v6.xdb
|
||||||
|
ip2region xdb searcher test program
|
||||||
|
source xdb file: ../../data/ip2region_v6.xdb (IPv6, vectorIndex)
|
||||||
|
type 'quit' to exit
|
||||||
|
ip2region>> ::
|
||||||
|
{region: |||, ioCount: 2, took: 0.06982 ms}
|
||||||
|
ip2region>> 2604:bc80:8001:11a4:ffff:ffff:ffff:ffff
|
||||||
|
{region: 中国|广东省|深圳市|数据中心, ioCount: 13, took: 0.19409 ms}
|
||||||
```
|
```
|
||||||
|
|
||||||
输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。
|
输入 ip 即可进行查询测试。也可以分别设置 `cache-policy` 为 file/vectorIndex/content 来测试三种不同缓存实现的效率。
|
||||||
|
|
@ -121,18 +182,22 @@ ip2region>>
|
||||||
|
|
||||||
通过 `bench_test.php` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能:
|
通过 `bench_test.php` 脚本来进行自动 bench 测试,一方面确保 `xdb` 文件没有错误,另一方面通过大量的查询测试平均查询性能:
|
||||||
```bash
|
```bash
|
||||||
➜ php git:(v2.0_xdb) ✗ php ./bench_test.php
|
➜ php git:(fr_php_ipv6) ✗ php bench_test.php
|
||||||
php ./bench_test.php [command options]
|
php bench_test.php [command options]
|
||||||
options:
|
options:
|
||||||
--db string ip2region binary xdb file path
|
--db string ip2region binary xdb file path
|
||||||
--src string source ip text file path
|
--src string source ip text file path
|
||||||
--cache-policy string cache policy: file/vectorIndex/content
|
--cache-policy string cache policy: file/vectorIndex/content
|
||||||
```
|
```
|
||||||
|
|
||||||
例如:通过默认的 data/ip2region.xdb 和 data/ip.merge.txt 来进行 bench 测试:
|
例如:通过默认的 data/ip2region_v4.xdb 和 data/ipv4_source.txt 文件进行 IPv4 的 bench 测试:
|
||||||
```bash
|
```bash
|
||||||
➜ php git:(v2.0_xdb) ✗ php ./bench_test.php --db=../../data/ip2region.xdb --src=../../data/ip.merge.txt --cache-policy=vectorIndex
|
php bench_test.php --db=../../data/ip2region_v4.xdb --src=../../data/ipv4_source.txt
|
||||||
Bench finished, {cachePolicy: vectorIndex, total: 3417955, took: 15s, cost: 0.005 ms/op}
|
```
|
||||||
|
|
||||||
|
例如:通过默认的 data/ip2region_v6.xdb 和 data/ipv6_source.txt 文件进行 IPv6 的 bench 测试:
|
||||||
|
```bash
|
||||||
|
php bench_test.php --db=../../data/ip2region_v6.xdb --src=../../data/ipv6_source.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。
|
可以通过设置 `cache-policy` 参数来分别测试 file/vectorIndex/content 三种不同的缓存实现的的性能。
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@
|
||||||
// @Author Lion <chenxin619315@gmail.com>
|
// @Author Lion <chenxin619315@gmail.com>
|
||||||
// @Date 2022/06/22
|
// @Date 2022/06/22
|
||||||
|
|
||||||
require dirname(__FILE__) . '/XdbSearcher.class.php';
|
require dirname(__FILE__) . '/xdb/Searcher.class.php';
|
||||||
|
|
||||||
|
use \ip2region\xdb\Util;
|
||||||
|
use \ip2region\xdb\{IPv4, IPv6};
|
||||||
|
use \ip2region\xdb\Searcher;
|
||||||
|
|
||||||
function printHelp($argv) {
|
function printHelp($argv) {
|
||||||
printf("php %s [command options]\n", $argv[0]);
|
printf("php %s [command options]\n", $argv[0]);
|
||||||
|
|
@ -60,39 +64,60 @@ if (strlen($dbFile) < 1 || strlen($srcFile) < 1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
||||||
|
$handle = fopen($dbFile, 'r');
|
||||||
|
if ($handle === false) {
|
||||||
|
printf("failed to open the xdb file `{$dbFile}`\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load header
|
||||||
|
$header = Util::loadHeader($handle);
|
||||||
|
if ($header == null) {
|
||||||
|
printf("failed to load the header\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the version number from the xdb header
|
||||||
|
try {
|
||||||
|
$version = Util::versionFromHeader($header);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to detect version from header: {$e->getMessage()}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create the xdb searcher by the cache-policy
|
// create the xdb searcher by the cache-policy
|
||||||
switch ( $cachePolicy ) {
|
switch ( $cachePolicy ) {
|
||||||
case 'file':
|
case 'file':
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithFileOnly($dbFile);
|
$searcher = Searcher::newWithFileOnly($version, $dbFile);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create searcher with '%s': %s\n", $dbFile, $e);
|
printf("failed to create searcher with '%s': %s\n", $dbFile, $e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'vectorIndex':
|
case 'vectorIndex':
|
||||||
$vIndex = XdbSearcher::loadVectorIndexFromFile($dbFile);
|
$vIndex = Util::loadVectorIndex($handle);
|
||||||
if ($vIndex == null) {
|
if ($vIndex == null) {
|
||||||
printf("failed to load vector index from '%s'\n", $dbFile);
|
printf("failed to load vector index from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithVectorIndex($dbFile, $vIndex);
|
$searcher = Searcher::newWithVectorIndex($version, $dbFile, $vIndex);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create vector index cached searcher with '%s': %s\n", $dbFile, $e);
|
printf("failed to create vector index cached searcher with '%s': %s\n", $dbFile, $e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'content':
|
case 'content':
|
||||||
$cBuff = XdbSearcher::loadContentFromFile($dbFile);
|
$cBuff = Util::loadContent($handle);
|
||||||
if ($cBuff == null) {
|
if ($cBuff == null) {
|
||||||
printf("failed to load xdb content from '%s'\n", $dbFile);
|
printf("failed to load xdb content from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$searcher = XdbSearcher::newWithBuffer($cBuff);
|
$searcher = Searcher::newWithBuffer($version, $cBuff);
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
printf("failed to create content cached searcher: %s", $e);
|
printf("failed to create content cached searcher: %s", $e);
|
||||||
return;
|
return;
|
||||||
|
|
@ -119,21 +144,18 @@ while (!feof($handle)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ip = XdbSearcher::ip2long($line);
|
try {
|
||||||
if ($ip === null) {
|
$ipBytes = Util::parseIP($line);
|
||||||
printf("invalid ip `%s`\n", $line);
|
} catch (Exception $e) {
|
||||||
return;
|
printf("failed to parse ip `%s`: %s\n", $line, $e->getMessage());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$count++;
|
$count++;
|
||||||
$region = $searcher->search($ip);
|
$region = $searcher->searchByBytes($ipBytes);
|
||||||
$ss = explode('|', $region);
|
$ss = explode('|', $region);
|
||||||
if (strlen($ss[3]) > 1) {
|
|
||||||
$qx_count++;
|
|
||||||
}
|
|
||||||
echo $line, ",", str_replace('|', ',', $region), "\n";
|
echo $line, ",", str_replace('|', ',', $region), "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose($handle);
|
fclose($handle);
|
||||||
echo "qx_count: {$qx_count}";
|
|
||||||
echo "Done, with {$count} IPs\n";
|
echo "Done, with {$count} IPs\n";
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,26 @@ if (strlen($dbFile) < 1 || strlen($srcFile) < 1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
||||||
$version = IPv4::default();
|
$handle = fopen($dbFile, 'r');
|
||||||
|
if ($handle === false) {
|
||||||
|
printf("failed to open the xdb file `{$dbFile}`\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load header
|
||||||
|
$header = Util::loadHeader($handle);
|
||||||
|
if ($header == null) {
|
||||||
|
printf("failed to load the header\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the version number from the xdb header
|
||||||
|
try {
|
||||||
|
$version = Util::versionFromHeader($header);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to detect version from header: {$e->getMessage()}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create the xdb searcher by the cache-policy
|
// create the xdb searcher by the cache-policy
|
||||||
switch ( $cachePolicy ) {
|
switch ( $cachePolicy ) {
|
||||||
|
|
@ -77,7 +96,7 @@ switch ( $cachePolicy ) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'vectorIndex':
|
case 'vectorIndex':
|
||||||
$vIndex = Util::loadVectorIndexFromFile($dbFile);
|
$vIndex = Util::loadVectorIndex($handle);
|
||||||
if ($vIndex == null) {
|
if ($vIndex == null) {
|
||||||
printf("failed to load vector index from '%s'\n", $dbFile);
|
printf("failed to load vector index from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -91,7 +110,7 @@ switch ( $cachePolicy ) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'content':
|
case 'content':
|
||||||
$cBuff = Util::loadContentFromFile($dbFile);
|
$cBuff = Util::loadContent($handle);
|
||||||
if ($cBuff == null) {
|
if ($cBuff == null) {
|
||||||
printf("failed to load xdb content from '%s'\n", $dbFile);
|
printf("failed to load xdb content from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -126,6 +145,11 @@ while (!feof($handle)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ignore the comment
|
||||||
|
if ($line[0] == '#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$ps = explode('|', $line, 3);
|
$ps = explode('|', $line, 3);
|
||||||
if (count($ps) != 3) {
|
if (count($ps) != 3) {
|
||||||
printf("invalid ip segment line `${line}`\n");
|
printf("invalid ip segment line `${line}`\n");
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,26 @@ if (strlen($dbFile) < 1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
// printf("debug: dbFile: %s, cachePolicy: %s\n", $dbFile, $cachePolicy);
|
||||||
$version = IPv4::default();
|
$handle = fopen($dbFile, 'r');
|
||||||
|
if ($handle === false) {
|
||||||
|
printf("failed to open the xdb file `{$dbFile}`\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// load header
|
||||||
|
$header = Util::loadHeader($handle);
|
||||||
|
if ($header == null) {
|
||||||
|
printf("failed to load the header\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the version number from the xdb header
|
||||||
|
try {
|
||||||
|
$version = Util::versionFromHeader($header);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
printf("failed to detect version from header: {$e->getMessage()}\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// create the xdb searcher by the cache-policy
|
// create the xdb searcher by the cache-policy
|
||||||
switch ( $cachePolicy ) {
|
switch ( $cachePolicy ) {
|
||||||
|
|
@ -73,7 +92,7 @@ case 'file':
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'vectorIndex':
|
case 'vectorIndex':
|
||||||
$vIndex = Util::loadVectorIndexFromFile($dbFile);
|
$vIndex = Util::loadVectorIndex($handle);
|
||||||
if ($vIndex == null) {
|
if ($vIndex == null) {
|
||||||
printf("failed to load vector index from '%s'\n", $dbFile);
|
printf("failed to load vector index from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -87,7 +106,7 @@ case 'vectorIndex':
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'content':
|
case 'content':
|
||||||
$cBuff = Util::loadContentFromFile($dbFile);
|
$cBuff = Util::loadContent($handle);
|
||||||
if ($cBuff == null) {
|
if ($cBuff == null) {
|
||||||
printf("failed to load xdb content from '%s'\n", $dbFile);
|
printf("failed to load xdb content from '%s'\n", $dbFile);
|
||||||
return;
|
return;
|
||||||
|
|
@ -105,7 +124,11 @@ default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("ip2region xdb searcher test program, cachePolicy: ${cachePolicy}\ntype 'quit' to exit\n");
|
printf(<<<EOF
|
||||||
|
ip2region xdb searcher test program
|
||||||
|
source xdb file: {$dbFile} ({$version->name}, ${cachePolicy})
|
||||||
|
type 'quit' to exit\n
|
||||||
|
EOF);
|
||||||
while ( true ) {
|
while ( true ) {
|
||||||
echo "ip2region>> ";
|
echo "ip2region>> ";
|
||||||
$line = trim(fgets(STDIN));
|
$line = trim(fgets(STDIN));
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ class Util {
|
||||||
// compare two ip bytes (packed string return by parsedIP)
|
// compare two ip bytes (packed string return by parsedIP)
|
||||||
// returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2
|
// returns: -1 if ip1 < ip2, 0 if ip1 == ip2 or 1 if ip1 > ip2
|
||||||
public static function ipSubCompare($ip1, $buff, $offset) {
|
public static function ipSubCompare($ip1, $buff, $offset) {
|
||||||
$r = substr_compare($ip1, $buff, $offset, strlen($ip1));
|
// $r = substr_compare($ip1, $buff, $offset, strlen($ip1));
|
||||||
|
// @Note: substr_compare is not working, use the substr + strcmp instead
|
||||||
|
$r = strcmp($ip1, substr($buff, $offset, strlen($ip1)));
|
||||||
if ($r < 0) {
|
if ($r < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if ($r > 0) {
|
} else if ($r > 0) {
|
||||||
|
|
@ -64,6 +66,48 @@ class Util {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// version parse
|
||||||
|
public static function versionFromName($ver_name) {
|
||||||
|
$name = strtoupper($ver_name);
|
||||||
|
if ($name == "V4" || $name == "IPv4") {
|
||||||
|
return IPv4::default();
|
||||||
|
} else if ($name == "V6" || $name == "IPv6") {
|
||||||
|
return IPv6::default();
|
||||||
|
} else {
|
||||||
|
throw new Exception("invalid verstion name `{$ver_name}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// version parse from header
|
||||||
|
public static function versionFromHeader($header) {
|
||||||
|
// Old structure 2.0 with IPv4 supports ONLY
|
||||||
|
if ($header['version'] == Structure_20) {
|
||||||
|
return IPv4::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
// structure 3.0 after IPv6 supporting
|
||||||
|
if ($header['version'] != Structure_30) {
|
||||||
|
throw new Exception("invalid xdb structure version `{$header['version']}`");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($header['ipVersion'] == IPv4VersionNo) {
|
||||||
|
return IPv4::default();
|
||||||
|
} else if ($header['ipVersion'] == IPv6VersionNo) {
|
||||||
|
return IPv6::default();
|
||||||
|
} else {
|
||||||
|
throw new Exception("invalid ip version number `{$header['ipVersion']}`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// binary string chars implode with space
|
||||||
|
public static function bytesToString($buff, $offset, $length) {
|
||||||
|
$sb = [];
|
||||||
|
for ($i = 0; $i < $length; $i++) {
|
||||||
|
$sb[] = ord($buff[$offset+$i]) & 0xFF;
|
||||||
|
}
|
||||||
|
return '['.implode(' ', $sb).']';
|
||||||
|
}
|
||||||
|
|
||||||
// decode a 4bytes long with Little endian byte order from a byte buffer
|
// decode a 4bytes long with Little endian byte order from a byte buffer
|
||||||
public static function le_getUint32($b, $idx) {
|
public static function le_getUint32($b, $idx) {
|
||||||
$val = (ord($b[$idx])) | (ord($b[$idx+1]) << 8)
|
$val = (ord($b[$idx])) | (ord($b[$idx+1]) << 8)
|
||||||
|
|
@ -203,10 +247,14 @@ class IPv4 {
|
||||||
public $name;
|
public $name;
|
||||||
public $bytes;
|
public $bytes;
|
||||||
public $segmentIndexSize;
|
public $segmentIndexSize;
|
||||||
|
|
||||||
|
private static $C = null;
|
||||||
public static function default() {
|
public static function default() {
|
||||||
// 14 = 4 + 4 + 2 + 4
|
if (self::$C == null) {
|
||||||
return new self(IPv4VersionNo, 'IPv4', 4, 14);
|
// 14 = 4 + 4 + 2 + 4
|
||||||
|
self::$C = new self(IPv4VersionNo, 'IPv4', 4, 14);
|
||||||
|
}
|
||||||
|
return self::$C;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct($id, $name, $bytes, $segmentIndexSize) {
|
public function __construct($id, $name, $bytes, $segmentIndexSize) {
|
||||||
|
|
@ -220,24 +268,23 @@ class IPv4 {
|
||||||
public function ipSubCompare($ip1, $buff, $offset) {
|
public function ipSubCompare($ip1, $buff, $offset) {
|
||||||
// ip1: Little endian byte order encoded long from searcher.
|
// ip1: Little endian byte order encoded long from searcher.
|
||||||
// ip2: Little endian byte order read from xdb index.
|
// ip2: Little endian byte order read from xdb index.
|
||||||
// @Note: to compatible with the old Litten endian index encode implementation.
|
$len = strlen($ip1);
|
||||||
$ip2 = (
|
$eIdx = $offset + $len;
|
||||||
(ord($buff[$offset ]) << 24) |
|
for ($i = 0, $j = $eIdx - 1; $i < $len; $i++, $j--) {
|
||||||
(ord($buff[$offset+1]) << 16) |
|
$i1 = ord($ip1[$i]) & 0xFF;
|
||||||
(ord($buff[$offset+2]) << 8) | ord($buff[$offset+3])
|
$i2 = ord($buff[$j]) & 0xFF;
|
||||||
);
|
// printf("i:%d, j:%d, i1:%d, i2:%d\n", $i, $j, $i1, $i2);
|
||||||
|
if ($i1 > $i2) {
|
||||||
$r = $ip1 - $ip2;
|
return 1;
|
||||||
if ($r > 0) {
|
} else if ($i1 < $i2) {
|
||||||
return 1;
|
return -1;
|
||||||
} else if ($r < 0) {
|
}
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString() {
|
public function __toString() {
|
||||||
return sprintf(
|
return sprintf(
|
||||||
"{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}",
|
"{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}",
|
||||||
$this->id, $this->name, $this->bytes, $this->segmentIndexSize
|
$this->id, $this->name, $this->bytes, $this->segmentIndexSize
|
||||||
|
|
@ -251,9 +298,14 @@ class IPv6 {
|
||||||
public $bytes;
|
public $bytes;
|
||||||
public $segmentIndexSize;
|
public $segmentIndexSize;
|
||||||
|
|
||||||
|
private static $C = null;
|
||||||
public static function default() {
|
public static function default() {
|
||||||
// 38 = 16 + 16 + 2 + 4
|
if (self::$C == null) {
|
||||||
return new self(IPv6VersionNo, 'IPv6', 16, 38);
|
// 38 = 16 + 16 + 2 + 4
|
||||||
|
self::$C = new self(IPv6VersionNo, 'IPv6', 16, 38);
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$C;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __construct($id, $name, $bytes, $segmentIndexSize) {
|
public function __construct($id, $name, $bytes, $segmentIndexSize) {
|
||||||
|
|
@ -264,10 +316,11 @@ class IPv6 {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ipSubCompare($ip, $buff, $offset) {
|
public function ipSubCompare($ip, $buff, $offset) {
|
||||||
|
// return Util::ipCompare($ip, substr($buff, $offset, strlen($ip)));
|
||||||
return Util::ipSubCompare($ip, $buff, $offset);
|
return Util::ipSubCompare($ip, $buff, $offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toString() {
|
public function __toString() {
|
||||||
return sprintf(
|
return sprintf(
|
||||||
"{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}",
|
"{id:%d, name:%s, bytes:%d, segmentIndexSize:%d}",
|
||||||
$this->id, $this->name, $this->bytes, $this->segmentIndexSize
|
$this->id, $this->name, $this->bytes, $this->segmentIndexSize
|
||||||
|
|
@ -281,8 +334,7 @@ class Searcher {
|
||||||
private $version;
|
private $version;
|
||||||
|
|
||||||
// xdb file handle
|
// xdb file handle
|
||||||
private $handle = null;
|
private $handle = null;
|
||||||
|
|
||||||
private $ioCount = 0;
|
private $ioCount = 0;
|
||||||
|
|
||||||
// vector index in binary string.
|
// vector index in binary string.
|
||||||
|
|
@ -406,15 +458,6 @@ class Searcher {
|
||||||
|
|
||||||
// printf("sPtr: %d, ePtr: %d\n", $sPtr, $ePtr);
|
// printf("sPtr: %d, ePtr: %d\n", $sPtr, $ePtr);
|
||||||
[$bytes, $dBytes] = [strlen($ipBytes), strlen($ipBytes) << 1];
|
[$bytes, $dBytes] = [strlen($ipBytes), strlen($ipBytes) << 1];
|
||||||
if ($bytes == 4) {
|
|
||||||
// encode the IPv4 bytes to an long with Litten endian byte order
|
|
||||||
// to avoid the repeated calcs in the binary search loop.
|
|
||||||
$ipBytes = (
|
|
||||||
(ord($ipBytes[3]) << 24) |
|
|
||||||
(ord($ipBytes[2]) << 24) |
|
|
||||||
(ord($ipBytes[1]) << 24) | (ord($ipBytes[0]))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// binary search the segment index to get the region info
|
// binary search the segment index to get the region info
|
||||||
$idxSize = $this->version->segmentIndexSize;
|
$idxSize = $this->version->segmentIndexSize;
|
||||||
|
|
@ -430,6 +473,11 @@ class Searcher {
|
||||||
throw new Exception("failed to read segment index with ptr={$p}");
|
throw new Exception("failed to read segment index with ptr={$p}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printf("l=%d, h=%d, sip=%s, eip=%s\n",
|
||||||
|
// $l, $h,
|
||||||
|
// Util::ipToString(strrev(substr($buff, 0, 4))),
|
||||||
|
// Util::ipToString(strrev(substr($buff, 4, 4)))
|
||||||
|
// );
|
||||||
if ($this->version->ipSubCompare($ipBytes, $buff, 0) < 0) {
|
if ($this->version->ipSubCompare($ipBytes, $buff, 0) < 0) {
|
||||||
$h = $m - 1;
|
$h = $m - 1;
|
||||||
} else if ($this->version->ipSubCompare($ipBytes, $buff, $bytes) > 0) {
|
} else if ($this->version->ipSubCompare($ipBytes, $buff, $bytes) > 0) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
// @Author Lion <chenxin619315@gmail.com>
|
// @Author Lion <chenxin619315@gmail.com>
|
||||||
// @Date 2022/06/22
|
// @Date 2022/06/22
|
||||||
|
|
||||||
require dirname(__FILE__) . '/Searcher.class.php';
|
require 'Searcher.class.php';
|
||||||
use \ip2region\xdb\Util;
|
use \ip2region\xdb\Util;
|
||||||
use \ip2region\xdb\Searcher;
|
use \ip2region\xdb\Searcher;
|
||||||
use \ip2region\xdb\IPv4;
|
use \ip2region\xdb\IPv4;
|
||||||
|
|
@ -20,9 +20,11 @@ if($argc < 2) {
|
||||||
$func_name = trim($argv[1]);
|
$func_name = trim($argv[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$basePath = dirname(dirname(dirname(dirname(__FILE__))));
|
||||||
|
|
||||||
function testLoadHeader() {
|
function testLoadHeader() {
|
||||||
$header = Util::loadHeaderFromFile('../../data/ip2region_v4.xdb');
|
global $basePath;
|
||||||
|
$header = Util::loadHeaderFromFile("{$basePath}/data/ip2region_v4.xdb");
|
||||||
if ($header == null) {
|
if ($header == null) {
|
||||||
printf("failed to load header from file\n");
|
printf("failed to load header from file\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -33,7 +35,8 @@ function testLoadHeader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testLoadVectorIndex() {
|
function testLoadVectorIndex() {
|
||||||
$vIndex = Util::loadVectorIndexFromFile('../../data/ip2region_v4.xdb');
|
global $basePath;
|
||||||
|
$vIndex = Util::loadVectorIndexFromFile("{$basePath}/data/ip2region_v4.xdb");
|
||||||
if ($vIndex == null) {
|
if ($vIndex == null) {
|
||||||
printf("failed to load vector index from file\n");
|
printf("failed to load vector index from file\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -43,7 +46,8 @@ function testLoadVectorIndex() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function testLoadContent() {
|
function testLoadContent() {
|
||||||
$cBuff = Util::loadContentFromFile('../../data/ip2region_v4.xdb');
|
global $basePath;
|
||||||
|
$cBuff = Util::loadContentFromFile("{$basePath}/data/ip2region_v4.xdb");
|
||||||
if ($cBuff == null) {
|
if ($cBuff == null) {
|
||||||
printf("failed to load content from file\n");
|
printf("failed to load content from file\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -87,6 +91,8 @@ function testIPCompare() {
|
||||||
["1.0.0.0", "1.0.0.1"],
|
["1.0.0.0", "1.0.0.1"],
|
||||||
["192.168.1.101", "192.168.1.90"],
|
["192.168.1.101", "192.168.1.90"],
|
||||||
["219.133.111.87", "114.114.114.114"],
|
["219.133.111.87", "114.114.114.114"],
|
||||||
|
["1.0.4.0", "1.0.1.0"],
|
||||||
|
["1.0.4.0", "1.0.3.255"],
|
||||||
["2000::", "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"],
|
["2000::", "2000:ffff:ffff:ffff:ffff:ffff:ffff:ffff"],
|
||||||
["2001:4:112::", "2001:4:112:ffff:ffff:ffff:ffff:ffff"],
|
["2001:4:112::", "2001:4:112:ffff:ffff:ffff:ffff:ffff"],
|
||||||
["ffff::", "2001:4:ffff:ffff:ffff:ffff:ffff:ffff"]
|
["ffff::", "2001:4:ffff:ffff:ffff:ffff:ffff:ffff"]
|
||||||
|
|
@ -102,8 +108,8 @@ function testIPCompare() {
|
||||||
function testAttributes() {
|
function testAttributes() {
|
||||||
printf("IPv4VersioNo: %d\n", \ip2region\xdb\IPv4VersionNo);
|
printf("IPv4VersioNo: %d\n", \ip2region\xdb\IPv4VersionNo);
|
||||||
printf("IPv6VersioNo: %d\n", \ip2region\xdb\IPv6VersionNo);
|
printf("IPv6VersioNo: %d\n", \ip2region\xdb\IPv6VersionNo);
|
||||||
printf("IPv4 Object: %s\n", IPv4::default()->toString());
|
printf("IPv4 Object: %s\n", IPv4::default());
|
||||||
printf("IPv6 Object: %s\n", IPv6::default()->toString());
|
printf("IPv6 Object: %s\n", IPv6::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue