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 *
php_maxminddb_fetch_object(zend_object * obj TSRMLS_DC)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)
ZEND_END_ARG_INFO()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)
ZEND_END_ARG_INFO()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
PHP_METHOD(MaxMind_Db_Reader,getWithPrefixLen)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
get_record(INTERNAL_FUNCTION_PARAMETERS,zval * record,int * prefix_len)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)
ZEND_END_ARG_INFO()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
PHP_METHOD(MaxMind_Db_Reader,close)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 *
handle_entry_data_list(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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 *
handle_map(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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 *
handle_array(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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
handle_uint128(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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
handle_uint32(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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
handle_uint64(const MMDB_entry_data_list_s * entry_data_list,zval * z_value TSRMLS_DC)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
lookup_class(const char * name TSRMLS_DC)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
maxminddb_free_storage(free_obj_t * object TSRMLS_DC)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
maxminddb_create_handler(zend_class_entry * type TSRMLS_DC)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
maxminddb_create_handler(zend_class_entry * type TSRMLS_DC)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
PHP_MINIT_FUNCTION(maxminddb)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
PHP_MINFO_FUNCTION(maxminddb)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