├── EXPERIMENTAL ├── CREDITS ├── ip2city.data ├── ip2city.so ├── config.w32 ├── test-ip2city.php ├── README.md ├── ip2city.php ├── tests └── 001.phpt ├── config.m4 ├── php_ip2city.h └── ip2city.c /EXPERIMENTAL: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | ip2city 2 | -------------------------------------------------------------------------------- /ip2city.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meolu/ip2city/HEAD/ip2city.data -------------------------------------------------------------------------------- /ip2city.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meolu/ip2city/HEAD/ip2city.so -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | // If your extension references something external, use ARG_WITH 5 | // ARG_WITH("ip2city", "for ip2city support", "no"); 6 | 7 | // Otherwise, use ARG_ENABLE 8 | // ARG_ENABLE("ip2city", "enable ip2city support", "no"); 9 | 10 | if (PHP_IP2CITY != "no") { 11 | EXTENSION("ip2city", "ip2city.c"); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /test-ip2city.php: -------------------------------------------------------------------------------- 1 | "; 3 | 4 | if(!extension_loaded('ip2city')) { 5 | dl('ip2city.' . PHP_SHLIB_SUFFIX); 6 | } 7 | $module = 'ip2city'; 8 | $functions = get_extension_funcs($module); 9 | echo "Functions available in the test extension:$br\n"; 10 | foreach($functions as $func) { 11 | echo $func."$br\n"; 12 | } 13 | echo "$br\n"; 14 | $function = 'confirm_' . $module . '_compiled'; 15 | if (extension_loaded($module)) { 16 | $str = $function($module); 17 | } else { 18 | $str = "Module $module is not compiled into PHP"; 19 | } 20 | echo "$str\n"; 21 | ?> 22 | -------------------------------------------------------------------------------- /tests/001.phpt: -------------------------------------------------------------------------------- 1 | --TEST-- 2 | Check for ip2city presence 3 | --SKIPIF-- 4 | 5 | --FILE-- 6 | 20 | --EXPECT-- 21 | ip2city extension is available 22 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension ip2city 3 | 4 | dnl Comments in this file start with the string 'dnl'. 5 | dnl Remove where necessary. This file will not work 6 | dnl without editing. 7 | 8 | dnl If your extension references something external, use with: 9 | 10 | dnl PHP_ARG_WITH(ip2city, for ip2city support, 11 | dnl Make sure that the comment is aligned: 12 | dnl [ --with-ip2city Include ip2city support]) 13 | 14 | dnl Otherwise use enable: 15 | 16 | PHP_ARG_ENABLE(ip2city, whether to enable ip2city support, 17 | dnl Make sure that the comment is aligned: 18 | [ --enable-ip2city Enable ip2city support]) 19 | 20 | if test "$PHP_IP2CITY" != "no"; then 21 | dnl Write more examples of tests here... 22 | 23 | dnl # --with-ip2city -> check with-path 24 | dnl SEARCH_PATH="/usr/local /usr" # you might want to change this 25 | dnl SEARCH_FOR="/include/ip2city.h" # you most likely want to change this 26 | dnl if test -r $PHP_IP2CITY/$SEARCH_FOR; then # path given as parameter 27 | dnl IP2CITY_DIR=$PHP_IP2CITY 28 | dnl else # search default path list 29 | dnl AC_MSG_CHECKING([for ip2city files in default path]) 30 | dnl for i in $SEARCH_PATH ; do 31 | dnl if test -r $i/$SEARCH_FOR; then 32 | dnl IP2CITY_DIR=$i 33 | dnl AC_MSG_RESULT(found in $i) 34 | dnl fi 35 | dnl done 36 | dnl fi 37 | dnl 38 | dnl if test -z "$IP2CITY_DIR"; then 39 | dnl AC_MSG_RESULT([not found]) 40 | dnl AC_MSG_ERROR([Please reinstall the ip2city distribution]) 41 | dnl fi 42 | 43 | dnl # --with-ip2city -> add include path 44 | dnl PHP_ADD_INCLUDE($IP2CITY_DIR/include) 45 | 46 | dnl # --with-ip2city -> check for lib and symbol presence 47 | dnl LIBNAME=ip2city # you may want to change this 48 | dnl LIBSYMBOL=ip2city # you most likely want to change this 49 | 50 | dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 51 | dnl [ 52 | dnl PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $IP2CITY_DIR/$PHP_LIBDIR, IP2CITY_SHARED_LIBADD) 53 | dnl AC_DEFINE(HAVE_IP2CITYLIB,1,[ ]) 54 | dnl ],[ 55 | dnl AC_MSG_ERROR([wrong ip2city lib version or lib not found]) 56 | dnl ],[ 57 | dnl -L$IP2CITY_DIR/$PHP_LIBDIR -lm 58 | dnl ]) 59 | dnl 60 | dnl PHP_SUBST(IP2CITY_SHARED_LIBADD) 61 | 62 | PHP_NEW_EXTENSION(ip2city, ip2city.c, $ext_shared) 63 | fi 64 | -------------------------------------------------------------------------------- /php_ip2city.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2015 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_IP2CITY_H 22 | #define PHP_IP2CITY_H 23 | 24 | extern zend_module_entry ip2city_module_entry; 25 | #define phpext_ip2city_ptr &ip2city_module_entry 26 | 27 | #define PHP_IP2CITY_VERSION "0.1.0" /* Replace with version number for your extension */ 28 | 29 | #ifdef PHP_WIN32 30 | # define PHP_IP2CITY_API __declspec(dllexport) 31 | #elif defined(__GNUC__) && __GNUC__ >= 4 32 | # define PHP_IP2CITY_API __attribute__ ((visibility("default"))) 33 | #else 34 | # define PHP_IP2CITY_API 35 | #endif 36 | 37 | #ifdef ZTS 38 | #include "TSRM.h" 39 | #endif 40 | 41 | /* 42 | Declare any global variables you may need between the BEGIN 43 | and END macros here: 44 | 45 | ZEND_BEGIN_MODULE_GLOBALS(ip2city) 46 | long global_value; 47 | char *global_string; 48 | ZEND_END_MODULE_GLOBALS(ip2city) 49 | */ 50 | 51 | /* In every utility function you add that needs to use variables 52 | in php_ip2city_globals, call TSRMLS_FETCH(); after declaring other 53 | variables used by that function, or better yet, pass in TSRMLS_CC 54 | after the last function argument and declare your utility function 55 | with TSRMLS_DC after the last declared argument. Always refer to 56 | the globals in your function as IP2CITY_G(variable). You are 57 | encouraged to rename these macros something shorter, see 58 | examples in any other php module directory. 59 | */ 60 | 61 | #ifdef ZTS 62 | #define IP2CITY_G(v) TSRMG(ip2city_globals_id, zend_ip2city_globals *, v) 63 | #else 64 | #define IP2CITY_G(v) (ip2city_globals.v) 65 | #endif 66 | 67 | #endif /* PHP_IP2CITY_H */ 68 | 69 | 70 | /* 71 | * Local variables: 72 | * tab-width: 4 73 | * c-basic-offset: 4 74 | * End: 75 | * vim600: noet sw=4 ts=4 fdm=marker 76 | * vim<600: noet sw=4 ts=4 77 | */ 78 | -------------------------------------------------------------------------------- /ip2city.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2013 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include "php.h" 26 | #include "php_ini.h" 27 | #include "ext/standard/info.h" 28 | #include "php_ip2city.h" 29 | 30 | #include "ext/standard/php_standard.h" 31 | 32 | 33 | #define BYTE3INT(X) ( ( X[0] & 0x000000FF ) \ 34 | | ( ( X[1] & 0x000000FF ) << 8 ) \ 35 | | ( ( X[2] & 0x000000FF ) << 16 ) ) 36 | 37 | #define BYTE4INT(X) ( ( X[0] & 0x000000FF ) \ 38 | | ( ( X[1] & 0x000000FF ) << 8 ) \ 39 | | ( ( X[2] & 0x000000FF ) << 16 ) \ 40 | | ( ( X[3] & 0x000000FF ) << 24 ) ) 41 | #define BYTE1INT(X) ( ( X[0] & 0x000000FF ) ) 42 | 43 | typedef struct IndexIp { 44 | unsigned char ip[4]; 45 | unsigned char local[3]; 46 | } IpStruct; 47 | 48 | 49 | #define ipsize (int)sizeof(IpStruct) 50 | 51 | #ifdef HAVE_INET_NTOP 52 | ZEND_BEGIN_ARG_INFO(arginfo_inet_ntop, 0) 53 | ZEND_ARG_INFO(0, in_addr) 54 | ZEND_END_ARG_INFO() 55 | #endif 56 | 57 | /* If you declare any globals in php_ip2city.h uncomment this: 58 | ZEND_DECLARE_MODULE_GLOBALS(ip2city) 59 | */ 60 | 61 | /* True global resources - no need for thread safety here */ 62 | static int le_ip2city; 63 | FILE * in; 64 | /* {{{ ip2city_functions[] 65 | * 66 | * Every user visible function must have an entry in ip2city_functions[]. 67 | */ 68 | const zend_function_entry ip2city_functions[] = { 69 | PHP_FE(ip_city, NULL) /* For testing, remove later. */ 70 | PHP_FE_END /* Must be the last line in ip2city_functions[] */ 71 | }; 72 | /* }}} */ 73 | 74 | /* {{{ ip2city_module_entry 75 | */ 76 | zend_module_entry ip2city_module_entry = { 77 | #if ZEND_MODULE_API_NO >= 20010901 78 | STANDARD_MODULE_HEADER, 79 | #endif 80 | "ip2city", 81 | ip2city_functions, 82 | PHP_MINIT(ip2city), 83 | PHP_MSHUTDOWN(ip2city), 84 | PHP_RINIT(ip2city), /* Replace with NULL if there's nothing to do at request start */ 85 | PHP_RSHUTDOWN(ip2city), /* Replace with NULL if there's nothing to do at request end */ 86 | PHP_MINFO(ip2city), 87 | #if ZEND_MODULE_API_NO >= 20010901 88 | "0.1", /* Replace with version number for your extension */ 89 | #endif 90 | STANDARD_MODULE_PROPERTIES 91 | }; 92 | /* }}} */ 93 | 94 | #ifdef COMPILE_DL_IP2CITY 95 | ZEND_GET_MODULE(ip2city) 96 | #endif 97 | 98 | /* {{{ PHP_INI 99 | */ 100 | PHP_INI_BEGIN() 101 | PHP_INI_ENTRY("ip2city.data", "foobar", PHP_INI_ALL, NULL) 102 | PHP_INI_END() 103 | /* }}} */ 104 | 105 | /* {{{ php_ip2city_init_globals 106 | */ 107 | /* Uncomment this function if you have INI entries 108 | static void php_ip2city_init_globals(zend_ip2city_globals *ip2city_globals) 109 | { 110 | ip2city_globals->global_value = 0; 111 | ip2city_globals->global_string = NULL; 112 | } 113 | */ 114 | /* }}} */ 115 | 116 | /* {{{ PHP_MINIT_FUNCTION 117 | */ 118 | PHP_MINIT_FUNCTION(ip2city) 119 | { 120 | REGISTER_INI_ENTRIES(); 121 | in = fopen(INI_STR("ip2city.data"), "rb"); 122 | // php_printf("php_minit_function\n"); 123 | 124 | return SUCCESS; 125 | } 126 | /* }}} */ 127 | 128 | /* {{{ PHP_MSHUTDOWN_FUNCTION 129 | */ 130 | PHP_MSHUTDOWN_FUNCTION(ip2city) 131 | { 132 | UNREGISTER_INI_ENTRIES(); 133 | return SUCCESS; 134 | } 135 | /* }}} */ 136 | 137 | /* Remove if there's nothing to do at request start */ 138 | /* {{{ PHP_RINIT_FUNCTION 139 | */ 140 | PHP_RINIT_FUNCTION(ip2city) 141 | { 142 | 143 | return SUCCESS; 144 | } 145 | /* }}} */ 146 | 147 | /* Remove if there's nothing to do at request end */ 148 | /* {{{ PHP_RSHUTDOWN_FUNCTION 149 | */ 150 | PHP_RSHUTDOWN_FUNCTION(ip2city) 151 | { 152 | return SUCCESS; 153 | } 154 | /* }}} */ 155 | 156 | /* {{{ PHP_MINFO_FUNCTION 157 | */ 158 | PHP_MINFO_FUNCTION(ip2city) 159 | { 160 | php_info_print_table_start(); 161 | php_info_print_table_header(2, "ip2city support", "enabled"); 162 | php_info_print_table_end(); 163 | 164 | DISPLAY_INI_ENTRIES(); 165 | } 166 | /* }}} */ 167 | 168 | 169 | /* Remove the following function when you have successfully modified config.m4 170 | so that your module can be compiled into PHP, it exists only for testing 171 | purposes. */ 172 | 173 | /* Every user-visible function in PHP should document itself in the source */ 174 | /* {{{ proto string ip_city(string arg) 175 | Return a string to confirm that the module is compiled in */ 176 | PHP_FUNCTION(ip_city) 177 | { 178 | char *addr; 179 | size_t addr_len; 180 | unsigned int iplong = 0; 181 | #ifdef HAVE_INET_PTON 182 | struct in_addr ip; 183 | #else 184 | zend_ulong ip; 185 | #endif 186 | 187 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == FAILURE) { 188 | return; 189 | } 190 | 191 | #ifdef HAVE_INET_PTON 192 | if (addr_len == 0 || inet_pton(AF_INET, addr, &ip) != 1) { 193 | RETURN_FALSE; 194 | } 195 | iplong = ntohl(ip.s_addr); 196 | #else 197 | if (addr_len ==0 || (ip = inet_addr(addr)) == INADDR_NONE) { 198 | RETURN_FLASE; 199 | } 200 | iplong = ntohl(ip); 201 | #endif 202 | unsigned int indexHead = 0; 203 | unsigned int indexTail = 0; 204 | fseek(in, 0, SEEK_SET); 205 | fread(&indexHead, sizeof(indexHead), 1, in); 206 | fread(&indexTail, sizeof(indexTail), 1, in); 207 | 208 | IpStruct tmp; 209 | char local[255] = {0}; 210 | 211 | int pos = searchIndex(iplong, in, indexHead, indexTail); 212 | fseek(in, indexHead+pos*ipsize, SEEK_SET); 213 | fread(&tmp, ipsize, 1, in); 214 | searchLocal(tmp, iplong, in, local); 215 | RETURN_STRING(local, 1); 216 | 217 | } 218 | 219 | int searchIndex(unsigned int iplong, FILE * db, int startPos, int mount) { 220 | int i = 0; 221 | int j = mount; 222 | int m = 0; 223 | int c = 0; 224 | IpStruct tmpA; 225 | while (i < j - 1) { 226 | m = (int) (i + j) / 2; 227 | fseek(db, startPos+m*ipsize, SEEK_SET); 228 | fread(&tmpA, ipsize, 1, db); 229 | c = compare(tmpA.ip, iplong); 230 | // printf("compare out: %d m= %d %s\n", c, m, c < 0 ? "c < 0" : "c not < 0"); 231 | if (c < 0) { 232 | i = m; 233 | } else if (c > 0) { 234 | j = m; 235 | } else { 236 | i = j = m; 237 | } 238 | } 239 | return i; 240 | 241 | } 242 | 243 | int compare(unsigned char a[4], unsigned int tb) { 244 | unsigned int ta = BYTE4INT(a); 245 | if (ta > tb) { 246 | return 1; 247 | } else if (ta < tb) { 248 | return -1; 249 | } else { 250 | return 0; 251 | } 252 | } 253 | 254 | void GetData(unsigned char* str, FILE* db, int max) { 255 | int i = 0; 256 | unsigned char byte[1] = {0};; 257 | int c = 0; 258 | while ( (c = fgetc(db)) != '\0') { 259 | *(str+i)=c; 260 | i++; 261 | } 262 | str[i] = 0; 263 | } 264 | 265 | int searchAreaAddr(FILE * db, char * local) { 266 | int pos = 0; 267 | char buf[80] = {0}; 268 | fread(buf, 1, 1, db); 269 | if (buf[0] == 0x02 ) { 270 | // 获取地区偏移 271 | fread(buf, 3, 1, db); 272 | pos = BYTE3INT(buf); 273 | fseek(db, pos, SEEK_SET); 274 | fread(buf, 1, 1, db); 275 | } 276 | if ( buf[0] == 0x01 || buf[0] == 0x02 ) { 277 | strcat(local, "未知"); 278 | return 0; 279 | } 280 | if ( buf[0] ) { 281 | GetData(buf+1, db, 40); 282 | } 283 | strcat(local, (char*)buf); 284 | } 285 | int searchLocal(IpStruct tmp, unsigned int iplong, FILE * db, char * local) { 286 | char buf[80] = {0}; 287 | int first = 0; 288 | int offset = 0; 289 | int countPos= 0; 290 | int tmpCount = 0; 291 | int pos = BYTE3INT(tmp.local); 292 | fseek(db, pos, SEEK_SET); 293 | fread(buf, 4, 1, db); 294 | int c = compare(buf, iplong); 295 | 296 | // 获取资料 297 | fread(buf, 1, 1, db); 298 | if ( buf[0] == 0x01 ) { // 国家地区均重复, 跳转至新地址 299 | fread(buf, 3, 1, db); 300 | pos = BYTE3INT(buf); 301 | fseek(db, pos, SEEK_SET); 302 | fread(buf, 1, 1, db); 303 | } 304 | 305 | // 获取国家 306 | if ( buf[0] == 0x02 ) { 307 | // 获取国家偏移 308 | fread(buf, 3, 1, db); 309 | // 保存地区信息 310 | tmpCount = ftell(db); 311 | pos = BYTE3INT(buf); 312 | fseek(db, pos, SEEK_SET); 313 | fread(buf, 1, 1, db); 314 | } 315 | if ( buf[0] == 0x01 || buf[0] == 0x02 ) { 316 | strcat(local, "未知"); 317 | return; 318 | } 319 | 320 | if ( buf[0] ) { 321 | GetData(buf+1, db, 40); 322 | } 323 | strcat(local, (char*)buf); 324 | strcat(local, " "); 325 | 326 | // 获取地区 327 | if ( tmpCount ) { 328 | fseek(db, tmpCount, SEEK_SET); 329 | } 330 | fread(buf, 1, 1, db); 331 | while ( buf[0] == 0x02 ) { 332 | // 获取地区偏移 333 | fread(buf, 3, 1, db); 334 | pos = BYTE3INT(buf); 335 | fseek(db, pos, SEEK_SET); 336 | fread(buf, 1, 1, db); 337 | } 338 | if ( buf[0] == 0x01 || buf[0] == 0x02 ) { 339 | strcat(local, "未知"); 340 | return; 341 | } 342 | if ( buf[0] ) { 343 | GetData(buf+1, db, 40); 344 | } 345 | strcat(local, (char*)buf); 346 | return; 347 | } 348 | /* }}} */ 349 | /* The previous line is meant for vim and emacs, so it can correctly fold and 350 | unfold functions in source code. See the corresponding marks just before 351 | function definition, where the functions purpose is also documented. Please 352 | follow this convention for the convenience of others editing your code. 353 | */ 354 | 355 | 356 | /* 357 | * Local variables: 358 | * tab-width: 4 359 | * c-basic-offset: 4 360 | * End: 361 | * vim600: noet sw=4 ts=4 fdm=marker 362 | * vim<600: noet sw=4 ts=4 363 | */ 364 | --------------------------------------------------------------------------------