1 /* MaxMind, Inc., licenses this file to you under the Apache License, Version 2 * 2.0 (the "License"); you may not use this file except in compliance with 3 * the License. You may obtain a copy of the License at 4 * 5 * http://www.apache.org/licenses/LICENSE-2.0 6 * 7 * Unless required by applicable law or agreed to in writing, software 8 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 * License for the specific language governing permissions and limitations 11 * under the License. 12 */ 13 14 #include "php_maxminddb.h" 15 16 #ifdef HAVE_CONFIG_H 17 #include "config.h" 18 #endif 19 20 #include <php.h> 21 #include <zend.h> 22 23 #include "Zend/zend_exceptions.h" 24 #include "ext/standard/info.h" 25 #include <maxminddb.h> 26 27 #ifdef ZTS 28 #include <TSRM.h> 29 #endif 30 31 #define __STDC_FORMAT_MACROS 32 #include <inttypes.h> 33 34 #define PHP_MAXMINDDB_NS ZEND_NS_NAME("MaxMind", "Db") 35 #define PHP_MAXMINDDB_READER_NS ZEND_NS_NAME(PHP_MAXMINDDB_NS, "Reader") 36 #define PHP_MAXMINDDB_READER_EX_NS \ 37 ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "InvalidDatabaseException") 38 39 #ifdef ZEND_ENGINE_3 40 #define Z_MAXMINDDB_P(zv) php_maxminddb_fetch_object(Z_OBJ_P(zv)) 41 #define _ZVAL_STRING ZVAL_STRING 42 #define _ZVAL_STRINGL ZVAL_STRINGL 43 typedef size_t strsize_t; 44 typedef zend_object free_obj_t; 45 #else 46 #define Z_MAXMINDDB_P(zv) \ 47 (maxminddb_obj *)zend_object_store_get_object(zv TSRMLS_CC) 48 #define _ZVAL_STRING(a, b) ZVAL_STRING(a, b, 1) 49 #define _ZVAL_STRINGL(a, b, c) ZVAL_STRINGL(a, b, c, 1) 50 typedef int strsize_t; 51 typedef void free_obj_t; 52 #endif 53 54 /* For PHP 8 compatibility */ 55 #ifndef TSRMLS_C 56 #define TSRMLS_C 57 #endif 58 #ifndef TSRMLS_CC 59 #define TSRMLS_CC 60 #endif 61 #ifndef TSRMLS_DC 62 #define TSRMLS_DC 63 #endif 64 #ifndef ZEND_ACC_CTOR 65 #define ZEND_ACC_CTOR 0 66 #endif 67 68 #ifdef ZEND_ENGINE_3 69 typedef struct _maxminddb_obj { 70 MMDB_s *mmdb; 71 zend_object std; 72 } maxminddb_obj; 73 #else 74 typedef struct _maxminddb_obj { 75 zend_object std; 76 MMDB_s *mmdb; 77 } maxminddb_obj; 78 #endif 79 80 PHP_FUNCTION(maxminddb); 81 82 static void 83 get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len); 84 static const MMDB_entry_data_list_s * 85 handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, 86 zval *z_value TSRMLS_DC); 87 static const MMDB_entry_data_list_s * 88 handle_array(const MMDB_entry_data_list_s *entry_data_list, 89 zval *z_value TSRMLS_DC); 90 static const MMDB_entry_data_list_s * 91 handle_map(const MMDB_entry_data_list_s *entry_data_list, 92 zval *z_value TSRMLS_DC); 93 static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, 94 zval *z_value TSRMLS_DC); 95 static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, 96 zval *z_value TSRMLS_DC); 97 static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, 98 zval *z_value TSRMLS_DC); 99 static zend_class_entry *lookup_class(const char *name TSRMLS_DC); 100 101 #define CHECK_ALLOCATED(val) \ 102 if (!val) { \ 103 zend_error(E_ERROR, "Out of memory"); \ 104 return; \ 105 } 106 107 #define THROW_EXCEPTION(name, ...) \ 108 { \ 109 zend_class_entry *exception_ce = lookup_class(name TSRMLS_CC); \ 110 zend_throw_exception_ex(exception_ce, 0 TSRMLS_CC, __VA_ARGS__); \ 111 } 112 113 #if PHP_VERSION_ID < 50399 114 #define object_properties_init(zo, class_type) \ 115 { \ 116 zval *tmp; \ 117 zend_hash_copy((*zo).properties, \ 118 &class_type->default_properties, \ 119 (copy_ctor_func_t)zval_add_ref, \ 120 (void *)&tmp, \ 121 sizeof(zval *)); \ 122 } 123 #endif 124 125 static zend_object_handlers maxminddb_obj_handlers; 126 static zend_class_entry *maxminddb_ce; 127 128 static inline maxminddb_obj * 129 php_maxminddb_fetch_object(zend_object *obj TSRMLS_DC) { 130 #ifdef ZEND_ENGINE_3 131 return (maxminddb_obj *)((char *)(obj)-XtOffsetOf(maxminddb_obj, std)); 132 #else 133 return (maxminddb_obj *)obj; 134 #endif 135 } 136 137 ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_construct, 0, 0, 1) 138 ZEND_ARG_INFO(0, db_file) 139 ZEND_END_ARG_INFO() 140 141 PHP_METHOD(MaxMind_Db_Reader, __construct) { 142 char *db_file = NULL; 143 strsize_t name_len; 144 zval *_this_zval = NULL; 145 146 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 147 getThis(), 148 "Os", 149 &_this_zval, 150 maxminddb_ce, 151 &db_file, 152 &name_len) == FAILURE) { 153 THROW_EXCEPTION("InvalidArgumentException", 154 "The constructor takes exactly one argument."); 155 return; 156 } 157 158 if (0 != php_check_open_basedir(db_file TSRMLS_CC) || 159 0 != access(db_file, R_OK)) { 160 THROW_EXCEPTION("InvalidArgumentException", 161 "The file \"%s\" does not exist or is not readable.", 162 db_file); 163 return; 164 } 165 166 MMDB_s *mmdb = (MMDB_s *)emalloc(sizeof(MMDB_s)); 167 uint16_t status = MMDB_open(db_file, MMDB_MODE_MMAP, mmdb); 168 169 if (MMDB_SUCCESS != status) { 170 THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS, 171 "Error opening database file (%s). Is this a valid " 172 "MaxMind DB file?", 173 db_file); 174 efree(mmdb); 175 return; 176 } 177 178 maxminddb_obj *mmdb_obj = Z_MAXMINDDB_P(getThis()); 179 mmdb_obj->mmdb = mmdb; 180 } 181 182 ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_get, 0, 0, 1) 183 ZEND_ARG_INFO(0, ip_address) 184 ZEND_END_ARG_INFO() 185 186 PHP_METHOD(MaxMind_Db_Reader, get) { 187 int prefix_len = 0; 188 get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, return_value, &prefix_len); 189 } 190 191 PHP_METHOD(MaxMind_Db_Reader, getWithPrefixLen) { 192 zval *record, *z_prefix_len; 193 #ifdef ZEND_ENGINE_3 194 zval _record, _z_prefix_len; 195 record = &_record; 196 z_prefix_len = &_z_prefix_len; 197 #else 198 ALLOC_INIT_ZVAL(record); 199 ALLOC_INIT_ZVAL(z_prefix_len); 200 #endif 201 202 int prefix_len = 0; 203 get_record(INTERNAL_FUNCTION_PARAM_PASSTHRU, record, &prefix_len); 204 205 array_init(return_value); 206 add_next_index_zval(return_value, record); 207 208 ZVAL_LONG(z_prefix_len, prefix_len); 209 add_next_index_zval(return_value, z_prefix_len); 210 } 211 212 static void 213 get_record(INTERNAL_FUNCTION_PARAMETERS, zval *record, int *prefix_len) { 214 char *ip_address = NULL; 215 strsize_t name_len; 216 zval *_this_zval = NULL; 217 218 if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, 219 getThis(), 220 "Os", 221 &_this_zval, 222 maxminddb_ce, 223 &ip_address, 224 &name_len) == FAILURE) { 225 THROW_EXCEPTION("InvalidArgumentException", 226 "Method takes exactly one argument."); 227 return; 228 } 229 230 const maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(getThis()); 231 232 MMDB_s *mmdb = mmdb_obj->mmdb; 233 234 if (NULL == mmdb) { 235 THROW_EXCEPTION("BadMethodCallException", 236 "Attempt to read from a closed MaxMind DB."); 237 return; 238 } 239 240 struct addrinfo hints = { 241 .ai_family = AF_UNSPEC, 242 .ai_flags = AI_NUMERICHOST, 243 // We set ai_socktype so that we only get one result back 244 .ai_socktype = SOCK_STREAM}; 245 246 struct addrinfo *addresses = NULL; 247 int gai_status = getaddrinfo(ip_address, NULL, &hints, &addresses); 248 if (gai_status) { 249 THROW_EXCEPTION("InvalidArgumentException", 250 "The value \"%s\" is not a valid IP address.", 251 ip_address); 252 return; 253 } 254 if (!addresses || !addresses->ai_addr) { 255 THROW_EXCEPTION( 256 "InvalidArgumentException", 257 "getaddrinfo was successful but failed to set the addrinfo"); 258 return; 259 } 260 261 int sa_family = addresses->ai_addr->sa_family; 262 263 int mmdb_error = MMDB_SUCCESS; 264 MMDB_lookup_result_s result = 265 MMDB_lookup_sockaddr(mmdb, addresses->ai_addr, &mmdb_error); 266 267 freeaddrinfo(addresses); 268 269 if (MMDB_SUCCESS != mmdb_error) { 270 char *exception_name; 271 if (MMDB_IPV6_LOOKUP_IN_IPV4_DATABASE_ERROR == mmdb_error) { 272 exception_name = "InvalidArgumentException"; 273 } else { 274 exception_name = PHP_MAXMINDDB_READER_EX_NS; 275 } 276 THROW_EXCEPTION(exception_name, 277 "Error looking up %s. %s", 278 ip_address, 279 MMDB_strerror(mmdb_error)); 280 return; 281 } 282 283 *prefix_len = result.netmask; 284 285 if (sa_family == AF_INET && mmdb->metadata.ip_version == 6) { 286 // We return the prefix length given the IPv4 address. If there is 287 // no IPv4 subtree, we return a prefix length of 0. 288 *prefix_len = *prefix_len >= 96 ? *prefix_len - 96 : 0; 289 } 290 291 if (!result.found_entry) { 292 ZVAL_NULL(record); 293 return; 294 } 295 296 MMDB_entry_data_list_s *entry_data_list = NULL; 297 int status = MMDB_get_entry_data_list(&result.entry, &entry_data_list); 298 299 if (MMDB_SUCCESS != status) { 300 THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS, 301 "Error while looking up data for %s. %s", 302 ip_address, 303 MMDB_strerror(status)); 304 MMDB_free_entry_data_list(entry_data_list); 305 return; 306 } else if (NULL == entry_data_list) { 307 THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS, 308 "Error while looking up data for %s. Your database may " 309 "be corrupt or you have found a bug in libmaxminddb.", 310 ip_address); 311 return; 312 } 313 314 handle_entry_data_list(entry_data_list, record TSRMLS_CC); 315 MMDB_free_entry_data_list(entry_data_list); 316 } 317 318 ZEND_BEGIN_ARG_INFO_EX(arginfo_maxmindbreader_void, 0, 0, 0) 319 ZEND_END_ARG_INFO() 320 321 PHP_METHOD(MaxMind_Db_Reader, metadata) { 322 if (ZEND_NUM_ARGS() != 0) { 323 THROW_EXCEPTION("InvalidArgumentException", 324 "Method takes no arguments."); 325 return; 326 } 327 328 const maxminddb_obj *const mmdb_obj = 329 (maxminddb_obj *)Z_MAXMINDDB_P(getThis()); 330 331 if (NULL == mmdb_obj->mmdb) { 332 THROW_EXCEPTION("BadMethodCallException", 333 "Attempt to read from a closed MaxMind DB."); 334 return; 335 } 336 337 const char *const name = ZEND_NS_NAME(PHP_MAXMINDDB_READER_NS, "Metadata"); 338 zend_class_entry *metadata_ce = lookup_class(name TSRMLS_CC); 339 340 object_init_ex(return_value, metadata_ce); 341 342 #ifdef ZEND_ENGINE_3 343 zval _metadata_array; 344 zval *metadata_array = &_metadata_array; 345 ZVAL_NULL(metadata_array); 346 #else 347 zval *metadata_array; 348 ALLOC_INIT_ZVAL(metadata_array); 349 #endif 350 351 MMDB_entry_data_list_s *entry_data_list; 352 MMDB_get_metadata_as_entry_data_list(mmdb_obj->mmdb, &entry_data_list); 353 354 handle_entry_data_list(entry_data_list, metadata_array TSRMLS_CC); 355 MMDB_free_entry_data_list(entry_data_list); 356 #if PHP_VERSION_ID >= 80000 357 zend_call_method_with_1_params(Z_OBJ_P(return_value), 358 metadata_ce, 359 &metadata_ce->constructor, 360 ZEND_CONSTRUCTOR_FUNC_NAME, 361 NULL, 362 metadata_array); 363 zval_ptr_dtor(metadata_array); 364 #elif defined(ZEND_ENGINE_3) 365 zend_call_method_with_1_params(return_value, 366 metadata_ce, 367 &metadata_ce->constructor, 368 ZEND_CONSTRUCTOR_FUNC_NAME, 369 NULL, 370 metadata_array); 371 zval_ptr_dtor(metadata_array); 372 #else 373 zend_call_method_with_1_params(&return_value, 374 metadata_ce, 375 &metadata_ce->constructor, 376 ZEND_CONSTRUCTOR_FUNC_NAME, 377 NULL, 378 metadata_array); 379 zval_ptr_dtor(&metadata_array); 380 #endif 381 } 382 383 PHP_METHOD(MaxMind_Db_Reader, close) { 384 if (ZEND_NUM_ARGS() != 0) { 385 THROW_EXCEPTION("InvalidArgumentException", 386 "Method takes no arguments."); 387 return; 388 } 389 390 maxminddb_obj *mmdb_obj = (maxminddb_obj *)Z_MAXMINDDB_P(getThis()); 391 392 if (NULL == mmdb_obj->mmdb) { 393 THROW_EXCEPTION("BadMethodCallException", 394 "Attempt to close a closed MaxMind DB."); 395 return; 396 } 397 MMDB_close(mmdb_obj->mmdb); 398 efree(mmdb_obj->mmdb); 399 mmdb_obj->mmdb = NULL; 400 } 401 402 static const MMDB_entry_data_list_s * 403 handle_entry_data_list(const MMDB_entry_data_list_s *entry_data_list, 404 zval *z_value TSRMLS_DC) { 405 switch (entry_data_list->entry_data.type) { 406 case MMDB_DATA_TYPE_MAP: 407 return handle_map(entry_data_list, z_value TSRMLS_CC); 408 case MMDB_DATA_TYPE_ARRAY: 409 return handle_array(entry_data_list, z_value TSRMLS_CC); 410 case MMDB_DATA_TYPE_UTF8_STRING: 411 _ZVAL_STRINGL(z_value, 412 (char *)entry_data_list->entry_data.utf8_string, 413 entry_data_list->entry_data.data_size); 414 break; 415 case MMDB_DATA_TYPE_BYTES: 416 _ZVAL_STRINGL(z_value, 417 (char *)entry_data_list->entry_data.bytes, 418 entry_data_list->entry_data.data_size); 419 break; 420 case MMDB_DATA_TYPE_DOUBLE: 421 ZVAL_DOUBLE(z_value, entry_data_list->entry_data.double_value); 422 break; 423 case MMDB_DATA_TYPE_FLOAT: 424 ZVAL_DOUBLE(z_value, entry_data_list->entry_data.float_value); 425 break; 426 case MMDB_DATA_TYPE_UINT16: 427 ZVAL_LONG(z_value, entry_data_list->entry_data.uint16); 428 break; 429 case MMDB_DATA_TYPE_UINT32: 430 handle_uint32(entry_data_list, z_value TSRMLS_CC); 431 break; 432 case MMDB_DATA_TYPE_BOOLEAN: 433 ZVAL_BOOL(z_value, entry_data_list->entry_data.boolean); 434 break; 435 case MMDB_DATA_TYPE_UINT64: 436 handle_uint64(entry_data_list, z_value TSRMLS_CC); 437 break; 438 case MMDB_DATA_TYPE_UINT128: 439 handle_uint128(entry_data_list, z_value TSRMLS_CC); 440 break; 441 case MMDB_DATA_TYPE_INT32: 442 ZVAL_LONG(z_value, entry_data_list->entry_data.int32); 443 break; 444 default: 445 THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS, 446 "Invalid data type arguments: %d", 447 entry_data_list->entry_data.type); 448 return NULL; 449 } 450 return entry_data_list; 451 } 452 453 static const MMDB_entry_data_list_s * 454 handle_map(const MMDB_entry_data_list_s *entry_data_list, 455 zval *z_value TSRMLS_DC) { 456 array_init(z_value); 457 const uint32_t map_size = entry_data_list->entry_data.data_size; 458 459 uint i; 460 for (i = 0; i < map_size && entry_data_list; i++) { 461 entry_data_list = entry_data_list->next; 462 463 char *key = estrndup((char *)entry_data_list->entry_data.utf8_string, 464 entry_data_list->entry_data.data_size); 465 if (NULL == key) { 466 THROW_EXCEPTION(PHP_MAXMINDDB_READER_EX_NS, 467 "Invalid data type arguments"); 468 return NULL; 469 } 470 471 entry_data_list = entry_data_list->next; 472 #ifdef ZEND_ENGINE_3 473 zval _new_value; 474 zval *new_value = &_new_value; 475 ZVAL_NULL(new_value); 476 #else 477 zval *new_value; 478 ALLOC_INIT_ZVAL(new_value); 479 #endif 480 entry_data_list = 481 handle_entry_data_list(entry_data_list, new_value TSRMLS_CC); 482 add_assoc_zval(z_value, key, new_value); 483 efree(key); 484 } 485 return entry_data_list; 486 } 487 488 static const MMDB_entry_data_list_s * 489 handle_array(const MMDB_entry_data_list_s *entry_data_list, 490 zval *z_value TSRMLS_DC) { 491 const uint32_t size = entry_data_list->entry_data.data_size; 492 493 array_init(z_value); 494 495 uint i; 496 for (i = 0; i < size && entry_data_list; i++) { 497 entry_data_list = entry_data_list->next; 498 #ifdef ZEND_ENGINE_3 499 zval _new_value; 500 zval *new_value = &_new_value; 501 ZVAL_NULL(new_value); 502 #else 503 zval *new_value; 504 ALLOC_INIT_ZVAL(new_value); 505 #endif 506 entry_data_list = 507 handle_entry_data_list(entry_data_list, new_value TSRMLS_CC); 508 add_next_index_zval(z_value, new_value); 509 } 510 return entry_data_list; 511 } 512 513 static void handle_uint128(const MMDB_entry_data_list_s *entry_data_list, 514 zval *z_value TSRMLS_DC) { 515 uint64_t high = 0; 516 uint64_t low = 0; 517 #if MMDB_UINT128_IS_BYTE_ARRAY 518 int i; 519 for (i = 0; i < 8; i++) { 520 high = (high << 8) | entry_data_list->entry_data.uint128[i]; 521 } 522 523 for (i = 8; i < 16; i++) { 524 low = (low << 8) | entry_data_list->entry_data.uint128[i]; 525 } 526 #else 527 high = entry_data_list->entry_data.uint128 >> 64; 528 low = (uint64_t)entry_data_list->entry_data.uint128; 529 #endif 530 531 char *num_str; 532 spprintf(&num_str, 0, "0x%016" PRIX64 "%016" PRIX64, high, low); 533 CHECK_ALLOCATED(num_str); 534 535 _ZVAL_STRING(z_value, num_str); 536 efree(num_str); 537 } 538 539 static void handle_uint32(const MMDB_entry_data_list_s *entry_data_list, 540 zval *z_value TSRMLS_DC) { 541 uint32_t val = entry_data_list->entry_data.uint32; 542 543 #if LONG_MAX >= UINT32_MAX 544 ZVAL_LONG(z_value, val); 545 return; 546 #else 547 if (val <= LONG_MAX) { 548 ZVAL_LONG(z_value, val); 549 return; 550 } 551 552 char *int_str; 553 spprintf(&int_str, 0, "%" PRIu32, val); 554 CHECK_ALLOCATED(int_str); 555 556 _ZVAL_STRING(z_value, int_str); 557 efree(int_str); 558 #endif 559 } 560 561 static void handle_uint64(const MMDB_entry_data_list_s *entry_data_list, 562 zval *z_value TSRMLS_DC) { 563 uint64_t val = entry_data_list->entry_data.uint64; 564 565 #if LONG_MAX >= UINT64_MAX 566 ZVAL_LONG(z_value, val); 567 return; 568 #else 569 if (val <= LONG_MAX) { 570 ZVAL_LONG(z_value, val); 571 return; 572 } 573 574 char *int_str; 575 spprintf(&int_str, 0, "%" PRIu64, val); 576 CHECK_ALLOCATED(int_str); 577 578 _ZVAL_STRING(z_value, int_str); 579 efree(int_str); 580 #endif 581 } 582 583 static zend_class_entry *lookup_class(const char *name TSRMLS_DC) { 584 #ifdef ZEND_ENGINE_3 585 zend_string *n = zend_string_init(name, strlen(name), 0); 586 zend_class_entry *ce = zend_lookup_class(n); 587 zend_string_release(n); 588 if (NULL == ce) { 589 zend_error(E_ERROR, "Class %s not found", name); 590 } 591 return ce; 592 #else 593 zend_class_entry **ce; 594 if (FAILURE == zend_lookup_class(name, strlen(name), &ce TSRMLS_CC)) { 595 zend_error(E_ERROR, "Class %s not found", name); 596 } 597 return *ce; 598 #endif 599 } 600 601 static void maxminddb_free_storage(free_obj_t *object TSRMLS_DC) { 602 maxminddb_obj *obj = 603 php_maxminddb_fetch_object((zend_object *)object TSRMLS_CC); 604 if (obj->mmdb != NULL) { 605 MMDB_close(obj->mmdb); 606 efree(obj->mmdb); 607 } 608 609 zend_object_std_dtor(&obj->std TSRMLS_CC); 610 #ifndef ZEND_ENGINE_3 611 efree(object); 612 #endif 613 } 614 615 #ifdef ZEND_ENGINE_3 616 static zend_object *maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) { 617 maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj)); 618 zend_object_std_init(&obj->std, type TSRMLS_CC); 619 object_properties_init(&(obj->std), type); 620 621 obj->std.handlers = &maxminddb_obj_handlers; 622 623 return &obj->std; 624 } 625 #else 626 static zend_object_value 627 maxminddb_create_handler(zend_class_entry *type TSRMLS_DC) { 628 zend_object_value retval; 629 630 maxminddb_obj *obj = (maxminddb_obj *)ecalloc(1, sizeof(maxminddb_obj)); 631 zend_object_std_init(&obj->std, type TSRMLS_CC); 632 object_properties_init(&(obj->std), type); 633 634 retval.handle = zend_objects_store_put( 635 obj, NULL, maxminddb_free_storage, NULL TSRMLS_CC); 636 retval.handlers = &maxminddb_obj_handlers; 637 638 return retval; 639 } 640 #endif 641 642 // clang-format off 643 static zend_function_entry maxminddb_methods[] = { 644 PHP_ME(MaxMind_Db_Reader, __construct, arginfo_maxmindbreader_construct, 645 ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) 646 PHP_ME(MaxMind_Db_Reader, close, arginfo_maxmindbreader_void, ZEND_ACC_PUBLIC) 647 PHP_ME(MaxMind_Db_Reader, get, arginfo_maxmindbreader_get, ZEND_ACC_PUBLIC) 648 PHP_ME(MaxMind_Db_Reader, getWithPrefixLen, arginfo_maxmindbreader_get, ZEND_ACC_PUBLIC) 649 PHP_ME(MaxMind_Db_Reader, metadata, arginfo_maxmindbreader_void, ZEND_ACC_PUBLIC) 650 { NULL, NULL, NULL } 651 }; 652 // clang-format on 653 654 PHP_MINIT_FUNCTION(maxminddb) { 655 zend_class_entry ce; 656 657 INIT_CLASS_ENTRY(ce, PHP_MAXMINDDB_READER_NS, maxminddb_methods); 658 maxminddb_ce = zend_register_internal_class(&ce TSRMLS_CC); 659 maxminddb_ce->create_object = maxminddb_create_handler; 660 memcpy(&maxminddb_obj_handlers, 661 zend_get_std_object_handlers(), 662 sizeof(zend_object_handlers)); 663 maxminddb_obj_handlers.clone_obj = NULL; 664 #ifdef ZEND_ENGINE_3 665 maxminddb_obj_handlers.offset = XtOffsetOf(maxminddb_obj, std); 666 maxminddb_obj_handlers.free_obj = maxminddb_free_storage; 667 #endif 668 669 return SUCCESS; 670 } 671 672 static PHP_MINFO_FUNCTION(maxminddb) { 673 php_info_print_table_start(); 674 675 php_info_print_table_row(2, "MaxMind DB Reader", "enabled"); 676 php_info_print_table_row( 677 2, "maxminddb extension version", PHP_MAXMINDDB_VERSION); 678 php_info_print_table_row( 679 2, "libmaxminddb library version", MMDB_lib_version()); 680 681 php_info_print_table_end(); 682 } 683 684 zend_module_entry maxminddb_module_entry = {STANDARD_MODULE_HEADER, 685 PHP_MAXMINDDB_EXTNAME, 686 NULL, 687 PHP_MINIT(maxminddb), 688 NULL, 689 NULL, 690 NULL, 691 PHP_MINFO(maxminddb), 692 PHP_MAXMINDDB_VERSION, 693 STANDARD_MODULE_PROPERTIES}; 694 695 #ifdef COMPILE_DL_MAXMINDDB 696 ZEND_GET_MODULE(maxminddb) 697 #endif 698