xref: /PHP-8.2/ext/mysqli/mysqli.c (revision a59868ae)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Georg Richter <georg@php.net>                               |
14   |          Andrey Hristov <andrey@php.net>                             |
15   |          Ulf Wendel <uw@php.net>                                     |
16   +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include <signal.h>
24 
25 #include "php.h"
26 #include "php_ini.h"
27 #include "ext/standard/info.h"
28 #include "ext/standard/php_string.h"
29 #include "php_mysqli.h"
30 #include "php_mysqli_structs.h"
31 #include "mysqli_priv.h"
32 #include "zend_attributes.h"
33 #include "zend_exceptions.h"
34 #include "ext/spl/spl_exceptions.h"
35 #include "zend_interfaces.h"
36 #include "zend_attributes.h"
37 #include "mysqli_arginfo.h"
38 
39 ZEND_DECLARE_MODULE_GLOBALS(mysqli)
40 static PHP_GINIT_FUNCTION(mysqli);
41 
42 #define MYSQLI_ADD_PROPERTIES(a, b) \
43 { \
44 	int i = 0; \
45 	while (b[i].pname != NULL) { \
46 		mysqli_add_property((a), (b)[i].pname, (b)[i].pname_length, \
47 							(mysqli_read_t)(b)[i].r_func, (mysqli_write_t)(b)[i].w_func); \
48 		i++; \
49 	} \
50 }
51 
52 #define ERROR_ARG_POS(arg_num) (getThis() ? (arg_num-1) : (arg_num))
53 
54 static HashTable classes;
55 static zend_object_handlers mysqli_object_handlers;
56 static zend_object_handlers mysqli_object_driver_handlers;
57 static zend_object_handlers mysqli_object_link_handlers;
58 static zend_object_handlers mysqli_object_result_handlers;
59 static zend_object_handlers mysqli_object_stmt_handlers;
60 static zend_object_handlers mysqli_object_warning_handlers;
61 static HashTable mysqli_driver_properties;
62 static HashTable mysqli_link_properties;
63 static HashTable mysqli_result_properties;
64 static HashTable mysqli_stmt_properties;
65 static HashTable mysqli_warning_properties;
66 
67 zend_class_entry *mysqli_link_class_entry;
68 zend_class_entry *mysqli_stmt_class_entry;
69 zend_class_entry *mysqli_result_class_entry;
70 zend_class_entry *mysqli_driver_class_entry;
71 zend_class_entry *mysqli_warning_class_entry;
72 zend_class_entry *mysqli_exception_class_entry;
73 
74 
75 typedef int (*mysqli_read_t)(mysqli_object *obj, zval *rv, bool quiet);
76 typedef int (*mysqli_write_t)(mysqli_object *obj, zval *newval);
77 
78 typedef struct _mysqli_prop_handler {
79 	zend_string *name;
80 	mysqli_read_t read_func;
81 	mysqli_write_t write_func;
82 } mysqli_prop_handler;
83 
84 static int le_pmysqli;
85 
free_prop_handler(zval * el)86 static void free_prop_handler(zval *el) {
87 	pefree(Z_PTR_P(el), 1);
88 }
89 
90 /* Destructor for mysqli entries in free_links/used_links */
php_mysqli_dtor_p_elements(void * data)91 void php_mysqli_dtor_p_elements(void *data)
92 {
93 	MYSQL *mysql = (MYSQL *)data;
94 	mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT);
95 }
96 
97 
ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor)98 ZEND_RSRC_DTOR_FUNC(php_mysqli_dtor)
99 {
100 	if (res->ptr) {
101 		mysqli_plist_entry *plist = (mysqli_plist_entry *)res->ptr;
102 		zend_ptr_stack_clean(&plist->free_links, php_mysqli_dtor_p_elements, 0);
103 		zend_ptr_stack_destroy(&plist->free_links);
104 		free(plist);
105 	}
106 }
107 
108 
php_le_pmysqli(void)109 int php_le_pmysqli(void)
110 {
111 	return le_pmysqli;
112 }
113 
114 /* {{{ php_clear_stmt_bind */
php_clear_stmt_bind(MY_STMT * stmt)115 void php_clear_stmt_bind(MY_STMT *stmt)
116 {
117 	if (stmt->stmt) {
118 		if (mysqli_stmt_close(stmt->stmt, true)) {
119 			php_error_docref(NULL, E_WARNING, "Error occurred while closing statement");
120 			return;
121 		}
122 	}
123 
124 	/*
125 	  mysqlnd keeps track of the binding and has freed its
126 	  structures in stmt_close() above
127 	*/
128 	if (stmt->query) {
129 		efree(stmt->query);
130 	}
131 	efree(stmt);
132 }
133 /* }}} */
134 
135 /* {{{ php_clear_mysql */
php_clear_mysql(MY_MYSQL * mysql)136 void php_clear_mysql(MY_MYSQL *mysql) {
137 	if (mysql->hash_key) {
138 		zend_string_release_ex(mysql->hash_key, 0);
139 		mysql->hash_key = NULL;
140 	}
141 	if (!Z_ISUNDEF(mysql->li_read)) {
142 		zval_ptr_dtor(&(mysql->li_read));
143 		ZVAL_UNDEF(&mysql->li_read);
144 	}
145 }
146 /* }}} */
147 
148 /* {{{ mysqli_objects_free_storage */
mysqli_objects_free_storage(zend_object * object)149 static void mysqli_objects_free_storage(zend_object	*object)
150 {
151 	mysqli_object 	*intern = php_mysqli_fetch_object(object);
152 	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr;
153 
154 	if (my_res) {
155 		efree(my_res);
156 	}
157 	zend_object_std_dtor(&intern->zo);
158 }
159 /* }}} */
160 
161 /* mysqli_link_free_storage partly doubles the work of PHP_FUNCTION(mysqli_close) */
162 
163 /* {{{ mysqli_link_free_storage */
mysqli_link_free_storage(zend_object * object)164 static void mysqli_link_free_storage(zend_object *object)
165 {
166 	mysqli_object 	*intern = php_mysqli_fetch_object(object);
167 	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr;
168 
169 	if (my_res && my_res->ptr) {
170 		MY_MYSQL *mysql = (MY_MYSQL *)my_res->ptr;
171 		if (mysql->mysql) {
172 			php_mysqli_close(mysql, MYSQLI_CLOSE_EXPLICIT, my_res->status);
173 		}
174 		php_clear_mysql(mysql);
175 		efree(mysql);
176 		my_res->status = MYSQLI_STATUS_UNKNOWN;
177 	}
178 	mysqli_objects_free_storage(object);
179 }
180 /* }}} */
181 
182 /* {{{ mysql_driver_free_storage */
mysqli_driver_free_storage(zend_object * object)183 static void mysqli_driver_free_storage(zend_object *object)
184 {
185 	mysqli_objects_free_storage(object);
186 }
187 /* }}} */
188 
189 /* {{{ mysqli_stmt_free_storage */
mysqli_stmt_free_storage(zend_object * object)190 static void mysqli_stmt_free_storage(zend_object *object)
191 {
192 	mysqli_object 	*intern = php_mysqli_fetch_object(object);
193 	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr;
194 
195 	if (my_res && my_res->ptr) {
196 		MY_STMT *stmt = (MY_STMT *)my_res->ptr;
197 		php_clear_stmt_bind(stmt);
198 	}
199 	mysqli_objects_free_storage(object);
200 }
201 /* }}} */
202 
203 /* {{{ mysqli_result_free_storage */
mysqli_result_free_storage(zend_object * object)204 static void mysqli_result_free_storage(zend_object *object)
205 {
206 	mysqli_object 	*intern = php_mysqli_fetch_object(object);
207 	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr;
208 
209 	if (my_res && my_res->ptr) {
210 		mysql_free_result(my_res->ptr);
211 	}
212 	mysqli_objects_free_storage(object);
213 }
214 /* }}} */
215 
216 /* {{{ mysqli_warning_free_storage */
mysqli_warning_free_storage(zend_object * object)217 static void mysqli_warning_free_storage(zend_object *object)
218 {
219 	mysqli_object 	*intern = php_mysqli_fetch_object(object);
220 	MYSQLI_RESOURCE	*my_res = (MYSQLI_RESOURCE *)intern->ptr;
221 
222 	if (my_res && my_res->ptr) {
223 		php_clear_warnings((MYSQLI_WARNING *)my_res->info);
224 		my_res->ptr = NULL;
225 	}
226 	mysqli_objects_free_storage(object);
227 }
228 /* }}} */
229 
230 /* {{{ mysqli_read_na */
mysqli_read_na(mysqli_object * obj,zval * retval,bool quiet)231 static int mysqli_read_na(mysqli_object *obj, zval *retval, bool quiet)
232 {
233 	if (!quiet) {
234 		zend_throw_error(NULL, "Cannot read property");
235 	}
236 
237 	return FAILURE;
238 }
239 /* }}} */
240 
241 /* {{{ mysqli_read_property */
mysqli_read_property(zend_object * object,zend_string * name,int type,void ** cache_slot,zval * rv)242 zval *mysqli_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv)
243 {
244 	mysqli_object *obj = php_mysqli_fetch_object(object);
245 	if (obj->prop_handler) {
246 		mysqli_prop_handler *hnd = zend_hash_find_ptr(obj->prop_handler, name);
247 		if (hnd) {
248 			if (hnd->read_func(obj, rv, type == BP_VAR_IS) == SUCCESS) {
249 				return rv;
250 			} else {
251 				return &EG(uninitialized_zval);
252 			}
253 		}
254 	}
255 
256 	return zend_std_read_property(object, name, type, cache_slot, rv);
257 }
258 /* }}} */
259 
260 /* {{{ mysqli_write_property */
mysqli_write_property(zend_object * object,zend_string * name,zval * value,void ** cache_slot)261 zval *mysqli_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
262 {
263 	mysqli_object *obj = php_mysqli_fetch_object(object);
264 	if (obj->prop_handler) {
265 		const mysqli_prop_handler *hnd = zend_hash_find_ptr(obj->prop_handler, name);
266 		if (hnd) {
267 			if (!hnd->write_func) {
268 				zend_throw_error(NULL, "Cannot write read-only property %s::$%s",
269 					ZSTR_VAL(object->ce->name), ZSTR_VAL(name));
270 				return &EG(error_zval);
271 			}
272 
273 			zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true);
274 			if (prop && ZEND_TYPE_IS_SET(prop->type)) {
275 				zval tmp;
276 				ZVAL_COPY(&tmp, value);
277 				if (!zend_verify_property_type(prop, &tmp,
278 							ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)))) {
279 					zval_ptr_dtor(&tmp);
280 					return &EG(error_zval);
281 				}
282 				hnd->write_func(obj, &tmp);
283 				zval_ptr_dtor(&tmp);
284 			} else {
285 				hnd->write_func(obj, value);
286 			}
287 			return value;
288 		}
289 	}
290 	return zend_std_write_property(object, name, value, cache_slot);
291 }
292 /* }}} */
293 
294 /* {{{ void mysqli_add_property(HashTable *h, char *pname, mysqli_read_t r_func, mysqli_write_t w_func) */
mysqli_add_property(HashTable * h,const char * pname,size_t pname_len,mysqli_read_t r_func,mysqli_write_t w_func)295 void mysqli_add_property(HashTable *h, const char *pname, size_t pname_len, mysqli_read_t r_func, mysqli_write_t w_func) {
296 	mysqli_prop_handler	p;
297 
298 	p.name = zend_string_init_interned(pname, pname_len, 1);
299 	p.read_func = (r_func) ? r_func : mysqli_read_na;
300 	p.write_func = w_func;
301 	zend_hash_add_mem(h, p.name, &p, sizeof(mysqli_prop_handler));
302 	zend_string_release_ex(p.name, 1);
303 }
304 /* }}} */
305 
mysqli_object_has_property(zend_object * object,zend_string * name,int has_set_exists,void ** cache_slot)306 static int mysqli_object_has_property(zend_object *object, zend_string *name, int has_set_exists, void **cache_slot) /* {{{ */
307 {
308 	mysqli_object *obj = php_mysqli_fetch_object(object);
309 	mysqli_prop_handler	*p;
310 	int ret = 0;
311 
312 	if ((p = zend_hash_find_ptr(obj->prop_handler, name)) != NULL) {
313 		switch (has_set_exists) {
314 			case ZEND_PROPERTY_EXISTS:
315 				ret = 1;
316 				break;
317 			case ZEND_PROPERTY_NOT_EMPTY: {
318 				zval rv;
319 				zval *value = mysqli_read_property(object, name, BP_VAR_IS, cache_slot, &rv);
320 				if (value != &EG(uninitialized_zval)) {
321 					convert_to_boolean(value);
322 					ret = Z_TYPE_P(value) == IS_TRUE ? 1 : 0;
323 				}
324 				break;
325 			}
326 			case ZEND_PROPERTY_ISSET: {
327 				zval rv;
328 				zval *value = mysqli_read_property(object, name, BP_VAR_IS, cache_slot, &rv);
329 				if (value != &EG(uninitialized_zval)) {
330 					ret = Z_TYPE_P(value) != IS_NULL? 1 : 0;
331 					zval_ptr_dtor(value);
332 				}
333 				break;
334 			}
335 			EMPTY_SWITCH_DEFAULT_CASE();
336 		}
337 	} else {
338 		ret = zend_std_has_property(object, name, has_set_exists, cache_slot);
339 	}
340 
341 	return ret;
342 } /* }}} */
343 
mysqli_object_get_debug_info(zend_object * object,int * is_temp)344 HashTable *mysqli_object_get_debug_info(zend_object *object, int *is_temp)
345 {
346 	mysqli_object *obj = php_mysqli_fetch_object(object);
347 	HashTable *retval, *props = obj->prop_handler;
348 	mysqli_prop_handler *entry;
349 
350 	retval = zend_new_array(zend_hash_num_elements(props) + 1);
351 
352 	ZEND_HASH_MAP_FOREACH_PTR(props, entry) {
353 		zval rv;
354 		zval *value;
355 
356 		value = mysqli_read_property(object, entry->name, BP_VAR_IS, 0, &rv);
357 		if (value != &EG(uninitialized_zval)) {
358 			zend_hash_add(retval, entry->name, value);
359 		}
360 	} ZEND_HASH_FOREACH_END();
361 
362 	*is_temp = 1;
363 	return retval;
364 }
365 
366 /* {{{ mysqli_objects_new */
mysqli_objects_new(zend_class_entry * class_type)367 PHP_MYSQLI_EXPORT(zend_object *) mysqli_objects_new(zend_class_entry *class_type)
368 {
369 	mysqli_object *intern;
370 	zend_class_entry *mysqli_base_class;
371 	zend_object_handlers *handlers;
372 
373 	intern = zend_object_alloc(sizeof(mysqli_object), class_type);
374 
375 	mysqli_base_class = class_type;
376 	while (mysqli_base_class->type != ZEND_INTERNAL_CLASS &&
377 		   mysqli_base_class->parent != NULL) {
378 		mysqli_base_class = mysqli_base_class->parent;
379 	}
380 	intern->prop_handler = zend_hash_find_ptr(&classes, mysqli_base_class->name);
381 
382 	zend_object_std_init(&intern->zo, class_type);
383 	object_properties_init(&intern->zo, class_type);
384 
385 	/* link object */
386 	if (instanceof_function(class_type, mysqli_link_class_entry)) {
387 		handlers = &mysqli_object_link_handlers;
388 	} else if (instanceof_function(class_type, mysqli_driver_class_entry)) { /* driver object */
389 		handlers = &mysqli_object_driver_handlers;
390 	} else if (instanceof_function(class_type, mysqli_stmt_class_entry)) { /* stmt object */
391 		handlers = &mysqli_object_stmt_handlers;
392 	} else if (instanceof_function(class_type, mysqli_result_class_entry)) { /* result object */
393 		handlers = &mysqli_object_result_handlers;
394 	} else if (instanceof_function(class_type, mysqli_warning_class_entry)) { /* warning object */
395 		handlers = &mysqli_object_warning_handlers;
396 	} else {
397 		handlers = &mysqli_object_handlers;
398 	}
399 
400 	intern->zo.handlers = handlers;
401 
402 	return &intern->zo;
403 }
404 /* }}} */
405 
406 #include "ext/mysqlnd/mysqlnd_reverse_api.h"
mysqli_convert_zv_to_mysqlnd(zval * zv)407 static MYSQLND *mysqli_convert_zv_to_mysqlnd(zval * zv)
408 {
409 	if (Z_TYPE_P(zv) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zv), mysqli_link_class_entry)) {
410 		MY_MYSQL *mysql;
411 		MYSQLI_RESOURCE  *my_res;
412 		mysqli_object *intern = Z_MYSQLI_P(zv);
413 		if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
414 			/* We know that we have a mysqli object, so this failure should be emitted */
415 			zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name));
416 			return NULL;
417 		}
418 		mysql = (MY_MYSQL *)(my_res->ptr);
419 		return mysql ? mysql->mysql : NULL;
420 	}
421 	return NULL;
422 }
423 
424 static const MYSQLND_REVERSE_API mysqli_reverse_api = {
425 	&mysqli_module_entry,
426 	mysqli_convert_zv_to_mysqlnd
427 };
428 
429 /* {{{ PHP_INI_BEGIN */
430 PHP_INI_BEGIN()
431 	STD_PHP_INI_ENTRY_EX("mysqli.max_links",			"-1",	PHP_INI_SYSTEM,		OnUpdateLong,		max_links,			zend_mysqli_globals,		mysqli_globals, display_link_numbers)
432 	STD_PHP_INI_ENTRY_EX("mysqli.max_persistent",		"-1",	PHP_INI_SYSTEM,		OnUpdateLong,		max_persistent,		zend_mysqli_globals,		mysqli_globals,	display_link_numbers)
433 	STD_PHP_INI_BOOLEAN("mysqli.allow_persistent",		"1",	PHP_INI_SYSTEM,		OnUpdateBool,		allow_persistent,	zend_mysqli_globals,		mysqli_globals)
434 	STD_PHP_INI_BOOLEAN("mysqli.rollback_on_cached_plink",	"0",PHP_INI_SYSTEM,		OnUpdateBool,		rollback_on_cached_plink,	zend_mysqli_globals,		mysqli_globals)
435 	STD_PHP_INI_ENTRY("mysqli.default_host",			NULL,	PHP_INI_ALL,		OnUpdateString,		default_host,		zend_mysqli_globals,		mysqli_globals)
436 	STD_PHP_INI_ENTRY("mysqli.default_user",			NULL,	PHP_INI_ALL,		OnUpdateString,		default_user,		zend_mysqli_globals,		mysqli_globals)
437 	STD_PHP_INI_ENTRY("mysqli.default_pw",				NULL,	PHP_INI_ALL,		OnUpdateString,		default_pw,			zend_mysqli_globals,		mysqli_globals)
438 	STD_PHP_INI_ENTRY("mysqli.default_port",			"3306",	PHP_INI_ALL,		OnUpdateLong,		default_port,		zend_mysqli_globals,		mysqli_globals)
439 #ifdef PHP_MYSQL_UNIX_SOCK_ADDR
440 	STD_PHP_INI_ENTRY("mysqli.default_socket",			MYSQL_UNIX_ADDR,PHP_INI_ALL,OnUpdateStringUnempty,	default_socket,	zend_mysqli_globals,		mysqli_globals)
441 #else
442 	STD_PHP_INI_ENTRY("mysqli.default_socket",			NULL,	PHP_INI_ALL,		OnUpdateStringUnempty,	default_socket,	zend_mysqli_globals,		mysqli_globals)
443 #endif
444 	STD_PHP_INI_BOOLEAN("mysqli.allow_local_infile",	"0",	PHP_INI_SYSTEM,		OnUpdateBool,		allow_local_infile,	zend_mysqli_globals,		mysqli_globals)
445 	STD_PHP_INI_ENTRY("mysqli.local_infile_directory",	NULL,	PHP_INI_SYSTEM,		OnUpdateString,		local_infile_directory,	zend_mysqli_globals,	mysqli_globals)
PHP_INI_END()446 PHP_INI_END()
447 /* }}} */
448 
449 /* {{{ PHP_GINIT_FUNCTION */
450 static PHP_GINIT_FUNCTION(mysqli)
451 {
452 #if defined(COMPILE_DL_MYSQLI) && defined(ZTS)
453 	ZEND_TSRMLS_CACHE_UPDATE();
454 #endif
455 	mysqli_globals->num_links = 0;
456 	mysqli_globals->max_links = -1;
457 	mysqli_globals->num_active_persistent = 0;
458 	mysqli_globals->num_inactive_persistent = 0;
459 	mysqli_globals->max_persistent = -1;
460 	mysqli_globals->allow_persistent = 1;
461 	mysqli_globals->default_port = 0;
462 	mysqli_globals->default_host = NULL;
463 	mysqli_globals->default_user = NULL;
464 	mysqli_globals->default_pw = NULL;
465 	mysqli_globals->default_socket = NULL;
466 	mysqli_globals->report_mode = MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT;;
467 	mysqli_globals->allow_local_infile = 0;
468 	mysqli_globals->local_infile_directory = NULL;
469 	mysqli_globals->rollback_on_cached_plink = false;
470 }
471 /* }}} */
472 
473 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(mysqli)474 PHP_MINIT_FUNCTION(mysqli)
475 {
476 	REGISTER_INI_ENTRIES();
477 
478 	memcpy(&mysqli_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
479 	mysqli_object_handlers.offset = XtOffsetOf(mysqli_object, zo);
480 	mysqli_object_handlers.free_obj = mysqli_objects_free_storage;
481 	mysqli_object_handlers.clone_obj = NULL;
482 	mysqli_object_handlers.read_property = mysqli_read_property;
483 	mysqli_object_handlers.write_property = mysqli_write_property;
484 	mysqli_object_handlers.has_property = mysqli_object_has_property;
485 	mysqli_object_handlers.get_debug_info = mysqli_object_get_debug_info;
486 	memcpy(&mysqli_object_driver_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
487 	mysqli_object_driver_handlers.free_obj = mysqli_driver_free_storage;
488 	memcpy(&mysqli_object_link_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
489 	mysqli_object_link_handlers.free_obj = mysqli_link_free_storage;
490 	memcpy(&mysqli_object_result_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
491 	mysqli_object_result_handlers.free_obj = mysqli_result_free_storage;
492 	memcpy(&mysqli_object_stmt_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
493 	mysqli_object_stmt_handlers.free_obj = mysqli_stmt_free_storage;
494 	memcpy(&mysqli_object_warning_handlers, &mysqli_object_handlers, sizeof(zend_object_handlers));
495 	mysqli_object_warning_handlers.free_obj = mysqli_warning_free_storage;
496 
497 	zend_hash_init(&classes, 0, NULL, NULL, 1);
498 
499 	/* persistent connections */
500 	le_pmysqli = zend_register_list_destructors_ex(NULL, php_mysqli_dtor,
501 		"MySqli persistent connection", module_number);
502 
503 	mysqli_exception_class_entry = register_class_mysqli_sql_exception(spl_ce_RuntimeException);
504 
505 	mysqli_driver_class_entry = register_class_mysqli_driver();
506 	mysqli_driver_class_entry->create_object = mysqli_objects_new;
507 	zend_hash_init(&mysqli_driver_properties, 0, NULL, free_prop_handler, 1);
508 	MYSQLI_ADD_PROPERTIES(&mysqli_driver_properties, mysqli_driver_property_entries);
509 	zend_hash_add_ptr(&classes, mysqli_driver_class_entry->name, &mysqli_driver_properties);
510 
511 	mysqli_link_class_entry = register_class_mysqli();
512 	mysqli_link_class_entry->create_object = mysqli_objects_new;
513 	zend_hash_init(&mysqli_link_properties, 0, NULL, free_prop_handler, 1);
514 	MYSQLI_ADD_PROPERTIES(&mysqli_link_properties, mysqli_link_property_entries);
515 	zend_hash_add_ptr(&classes, mysqli_link_class_entry->name, &mysqli_link_properties);
516 
517 	mysqli_warning_class_entry = register_class_mysqli_warning();
518 	mysqli_warning_class_entry->create_object = mysqli_objects_new;
519 	zend_hash_init(&mysqli_warning_properties, 0, NULL, free_prop_handler, 1);
520 	MYSQLI_ADD_PROPERTIES(&mysqli_warning_properties, mysqli_warning_property_entries);
521 	zend_hash_add_ptr(&classes, mysqli_warning_class_entry->name, &mysqli_warning_properties);
522 
523 	mysqli_result_class_entry = register_class_mysqli_result(zend_ce_aggregate);
524 	mysqli_result_class_entry->create_object = mysqli_objects_new;
525 	mysqli_result_class_entry->get_iterator = php_mysqli_result_get_iterator;
526 	zend_hash_init(&mysqli_result_properties, 0, NULL, free_prop_handler, 1);
527 	MYSQLI_ADD_PROPERTIES(&mysqli_result_properties, mysqli_result_property_entries);
528 	zend_hash_add_ptr(&classes, mysqli_result_class_entry->name, &mysqli_result_properties);
529 
530 	mysqli_stmt_class_entry = register_class_mysqli_stmt();
531 	mysqli_stmt_class_entry->create_object = mysqli_objects_new;
532 	zend_hash_init(&mysqli_stmt_properties, 0, NULL, free_prop_handler, 1);
533 	MYSQLI_ADD_PROPERTIES(&mysqli_stmt_properties, mysqli_stmt_property_entries);
534 	zend_hash_add_ptr(&classes, mysqli_stmt_class_entry->name, &mysqli_stmt_properties);
535 
536 	register_mysqli_symbols(module_number);
537 
538 	mysqlnd_reverse_api_register_api(&mysqli_reverse_api);
539 
540 	return SUCCESS;
541 }
542 /* }}} */
543 
544 /* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(mysqli)545 PHP_MSHUTDOWN_FUNCTION(mysqli)
546 {
547 	zend_hash_destroy(&mysqli_driver_properties);
548 	zend_hash_destroy(&mysqli_result_properties);
549 	zend_hash_destroy(&mysqli_stmt_properties);
550 	zend_hash_destroy(&mysqli_warning_properties);
551 	zend_hash_destroy(&mysqli_link_properties);
552 	zend_hash_destroy(&classes);
553 
554 	UNREGISTER_INI_ENTRIES();
555 	return SUCCESS;
556 }
557 /* }}} */
558 
559 /* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(mysqli)560 PHP_RINIT_FUNCTION(mysqli)
561 {
562 	MyG(error_msg) = NULL;
563 	MyG(error_no) = 0;
564 	MyG(report_mode) = MYSQLI_REPORT_ERROR|MYSQLI_REPORT_STRICT;
565 
566 	return SUCCESS;
567 }
568 /* }}} */
569 
570 
571 /* {{{ PHP_RSHUTDOWN_FUNCTION */
PHP_RSHUTDOWN_FUNCTION(mysqli)572 PHP_RSHUTDOWN_FUNCTION(mysqli)
573 {
574 	/* check persistent connections, move used to free */
575 
576 	if (MyG(error_msg)) {
577 		efree(MyG(error_msg));
578 	}
579 
580 	return SUCCESS;
581 }
582 /* }}} */
583 
584 
585 /* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(mysqli)586 PHP_MINFO_FUNCTION(mysqli)
587 {
588 	char buf[32];
589 
590 	php_info_print_table_start();
591 	php_info_print_table_header(2, "MysqlI Support", "enabled");
592 	php_info_print_table_row(2, "Client API library version", mysql_get_client_info());
593 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, MyG(num_active_persistent));
594 	php_info_print_table_row(2, "Active Persistent Links", buf);
595 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, MyG(num_inactive_persistent));
596 	php_info_print_table_row(2, "Inactive Persistent Links", buf);
597 	snprintf(buf, sizeof(buf), ZEND_LONG_FMT, MyG(num_links));
598 	php_info_print_table_row(2, "Active Links", buf);
599 	php_info_print_table_end();
600 
601 	DISPLAY_INI_ENTRIES();
602 }
603 /* }}} */
604 
605 
606 /* Dependencies */
607 static const  zend_module_dep mysqli_deps[] = {
608 	ZEND_MOD_REQUIRED("spl")
609 	ZEND_MOD_REQUIRED("mysqlnd")
610 	ZEND_MOD_END
611 };
612 
613 /* {{{ mysqli_module_entry */
614 zend_module_entry mysqli_module_entry = {
615 	STANDARD_MODULE_HEADER_EX, NULL,
616 	mysqli_deps,
617 	"mysqli",
618 	ext_functions,
619 	PHP_MINIT(mysqli),
620 	PHP_MSHUTDOWN(mysqli),
621 	PHP_RINIT(mysqli),
622 	PHP_RSHUTDOWN(mysqli),
623 	PHP_MINFO(mysqli),
624 	PHP_MYSQLI_VERSION,
625 	PHP_MODULE_GLOBALS(mysqli),
626 	PHP_GINIT(mysqli),
627 	NULL,
628 	NULL,
629 	STANDARD_MODULE_PROPERTIES_EX
630 };
631 /* }}} */
632 
633 #ifdef COMPILE_DL_MYSQLI
634 #ifdef ZTS
635 ZEND_TSRMLS_CACHE_DEFINE()
636 #endif
ZEND_GET_MODULE(mysqli)637 ZEND_GET_MODULE(mysqli)
638 #endif
639 
640 
641 PHP_METHOD(mysqli_stmt, __construct)
642 {
643 	MY_MYSQL			*mysql;
644 	zval				*mysql_link;
645 	MY_STMT				*stmt;
646 	MYSQLI_RESOURCE		*mysqli_resource;
647 	char				*statement = NULL;
648 	size_t					statement_len;
649 
650 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!", &mysql_link, mysqli_link_class_entry, &statement, &statement_len) == FAILURE) {
651 		RETURN_THROWS();
652 	}
653 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
654 
655 	stmt = (MY_STMT *) ecalloc(1, sizeof(MY_STMT));
656 
657 	if (!(stmt->stmt = mysql_stmt_init(mysql->mysql))) {
658 		efree(stmt);
659 		RETURN_FALSE;
660 	}
661 
662 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
663 	mysqli_resource->ptr = (void *)stmt;
664 	mysqli_resource->status = MYSQLI_STATUS_INITIALIZED;
665 
666 	MYSQLI_REGISTER_RESOURCE_EX(mysqli_resource, getThis());
667 
668 	if (statement) {
669 		if(mysql_stmt_prepare(stmt->stmt, statement, statement_len)) {
670 			MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
671 			RETURN_FALSE;
672 		}
673 		mysqli_resource->status = MYSQLI_STATUS_VALID;
674 	}
675 }
676 
PHP_METHOD(mysqli_result,__construct)677 PHP_METHOD(mysqli_result, __construct)
678 {
679 	MY_MYSQL			*mysql;
680 	MYSQL_RES			*result = NULL;
681 	zval				*mysql_link;
682 	MYSQLI_RESOURCE		*mysqli_resource;
683 	zend_long				resmode = MYSQLI_STORE_RESULT;
684 
685 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &mysql_link, mysqli_link_class_entry, &resmode) == FAILURE) {
686 		RETURN_THROWS();
687 	}
688 
689 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
690 
691 	switch (resmode) {
692 		case MYSQLI_STORE_RESULT:
693 			result = mysql_store_result(mysql->mysql);
694 			break;
695 		case MYSQLI_USE_RESULT:
696 			result = mysql_use_result(mysql->mysql);
697 			break;
698 		default:
699 			zend_argument_value_error(2, "must be either MYSQLI_STORE_RESULT or MYSQLI_USE_RESULT");
700 			RETURN_THROWS();
701 	}
702 
703 	if (!result) {
704 		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
705 		RETURN_FALSE;
706 	}
707 	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
708 		php_mysqli_report_index("from previous query", mysqli_server_status(mysql->mysql));
709 	}
710 
711 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
712 	mysqli_resource->ptr = (void *)result;
713 	mysqli_resource->status = MYSQLI_STATUS_VALID;
714 
715 	MYSQLI_REGISTER_RESOURCE_EX(mysqli_resource, getThis());
716 }
717 
PHP_METHOD(mysqli_result,getIterator)718 PHP_METHOD(mysqli_result, getIterator)
719 {
720 	if (zend_parse_parameters_none() == FAILURE) {
721 		RETURN_THROWS();
722 	}
723 
724 	zend_create_internal_iterator_zval(return_value, ZEND_THIS);
725 }
726 
727 /* {{{ php_mysqli_fetch_into_hash_aux */
php_mysqli_fetch_into_hash_aux(zval * return_value,MYSQL_RES * result,zend_long fetchtype)728 void php_mysqli_fetch_into_hash_aux(zval *return_value, MYSQL_RES * result, zend_long fetchtype)
729 {
730 	mysqlnd_fetch_into(result, ((fetchtype & MYSQLI_NUM)? MYSQLND_FETCH_NUM:0) | ((fetchtype & MYSQLI_ASSOC)? MYSQLND_FETCH_ASSOC:0), return_value);
731 	/* TODO: We don't have access to the connection object at this point, so we use low-level
732 	 * mysqlnd APIs to access the error information. We should try to pass through the connection
733 	 * object instead. */
734 	if (MyG(report_mode) & MYSQLI_REPORT_ERROR && result->conn) {
735 		MYSQLND_CONN_DATA *conn = result->conn;
736 		unsigned error_no = conn->m->get_error_no(conn);
737 		if (error_no) {
738 			php_mysqli_report_error(
739 				conn->m->get_sqlstate(conn), error_no, conn->m->get_error_str(conn));
740 		}
741 	}
742 }
743 /* }}} */
744 
745 /* TODO Split this up */
746 /* {{{ php_mysqli_fetch_into_hash */
php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS,int override_flags,int into_object)747 void php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAMETERS, int override_flags, int into_object)
748 {
749 	MYSQL_RES		*result;
750 	zval			*mysql_result;
751 	zend_long			fetchtype;
752 	zval			*ctor_params = NULL;
753 	zend_class_entry *ce = NULL;
754 
755 	if (into_object) {
756 		if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|Ca", &mysql_result, mysqli_result_class_entry, &ce, &ctor_params) == FAILURE) {
757 			RETURN_THROWS();
758 		}
759 		if (ce == NULL) {
760 			ce = zend_standard_class_def;
761 		}
762 		if (UNEXPECTED(ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) {
763 			zend_throw_error(NULL, "Class %s cannot be instantiated", ZSTR_VAL(ce->name));
764 			RETURN_THROWS();
765 		}
766 		fetchtype = MYSQLI_ASSOC;
767 	} else {
768 		if (override_flags) {
769 			if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_result, mysqli_result_class_entry) == FAILURE) {
770 				RETURN_THROWS();
771 			}
772 			fetchtype = override_flags;
773 		} else {
774 			fetchtype = MYSQLI_BOTH;
775 			if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &fetchtype) == FAILURE) {
776 				RETURN_THROWS();
777 			}
778 		}
779 	}
780 	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
781 
782 	if (fetchtype < MYSQLI_ASSOC || fetchtype > MYSQLI_BOTH) {
783 		zend_argument_value_error(ERROR_ARG_POS(2), "must be one of MYSQLI_NUM, MYSQLI_ASSOC, or MYSQLI_BOTH");
784 		RETURN_THROWS();
785 	}
786 
787 	php_mysqli_fetch_into_hash_aux(return_value, result, fetchtype);
788 
789 	if (into_object && Z_TYPE_P(return_value) == IS_ARRAY) {
790 		zval dataset, retval;
791 		zend_fcall_info fci;
792 		zend_fcall_info_cache fcc;
793 
794 		ZVAL_COPY_VALUE(&dataset, return_value);
795 
796 		object_init_ex(return_value, ce);
797 		HashTable *prop_table = zend_symtable_to_proptable(Z_ARR(dataset));
798 		zval_ptr_dtor(&dataset);
799 		if (!ce->default_properties_count && !ce->__set) {
800 			Z_OBJ_P(return_value)->properties = prop_table;
801 		} else {
802 			zend_merge_properties(return_value, prop_table);
803 			zend_array_release(prop_table);
804 		}
805 
806 		if (ce->constructor) {
807 			fci.size = sizeof(fci);
808 			ZVAL_UNDEF(&fci.function_name);
809 			fci.object = Z_OBJ_P(return_value);
810 			fci.retval = &retval;
811 			fci.params = NULL;
812 			fci.param_count = 0;
813 			fci.named_params = NULL;
814 
815 			if (ctor_params) {
816 				if (zend_fcall_info_args(&fci, ctor_params) == FAILURE) {
817 					ZEND_UNREACHABLE();
818 				}
819 			}
820 
821 			fcc.function_handler = ce->constructor;
822 			fcc.called_scope = Z_OBJCE_P(return_value);
823 			fcc.object = Z_OBJ_P(return_value);
824 
825 			if (zend_call_function(&fci, &fcc) == FAILURE) {
826 				zend_throw_exception_ex(zend_ce_exception, 0, "Could not execute %s::%s()", ZSTR_VAL(ce->name), ZSTR_VAL(ce->constructor->common.function_name));
827 			} else {
828 				zval_ptr_dtor(&retval);
829 			}
830 			zend_fcall_info_args_clear(&fci, 1);
831 		} else if (ctor_params && zend_hash_num_elements(Z_ARRVAL_P(ctor_params)) > 0) {
832 			zend_argument_error(zend_ce_exception, ERROR_ARG_POS(3),
833 				"must be empty when the specified class (%s) does not have a constructor",
834 				ZSTR_VAL(ce->name)
835 			);
836 		}
837 	}
838 }
839 /* }}} */
840