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   | http://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 "zend_smart_str.h"
29 #if defined(MYSQLI_USE_MYSQLND)
30 #include "php_mysqli_structs.h"
31 #endif
32 #include "mysqli_priv.h"
33 #define ERROR_ARG_POS(arg_num) (getThis() ? (arg_num-1) : (arg_num))
34 
35 #define SAFE_STR(a) ((a)?a:"")
36 
37 /* {{{ php_mysqli_set_error */
php_mysqli_set_error(zend_long mysql_errno,char * mysql_err)38 static void php_mysqli_set_error(zend_long mysql_errno, char *mysql_err)
39 {
40 	MyG(error_no) = mysql_errno;
41 	if (MyG(error_msg)) {
42 		efree(MyG(error_msg));
43 	}
44 	if(mysql_err && *mysql_err) {
45 		MyG(error_msg) = estrdup(mysql_err);
46 	} else {
47 		MyG(error_msg) = NULL;
48 	}
49 }
50 /* }}} */
51 
mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS,zend_bool is_real_connect,zend_bool in_ctor)52 void mysqli_common_connect(INTERNAL_FUNCTION_PARAMETERS, zend_bool is_real_connect, zend_bool in_ctor) /* {{{ */
53 {
54 	MY_MYSQL			*mysql = NULL;
55 	MYSQLI_RESOURCE		*mysqli_resource = NULL;
56 	zval				*object = getThis();
57 	char				*hostname = NULL, *username=NULL, *passwd=NULL, *dbname=NULL, *socket=NULL,
58 						*ssl_key = NULL, *ssl_cert = NULL, *ssl_ca = NULL, *ssl_capath = NULL,
59 						*ssl_cipher = NULL;
60 	size_t				hostname_len = 0, username_len = 0, passwd_len = 0, dbname_len = 0, socket_len = 0;
61 	zend_bool			persistent = FALSE, ssl = FALSE;
62 	zend_long			port = 0, flags = 0;
63 	zend_bool           port_is_null = 1;
64 	zend_string			*hash_key = NULL;
65 	zend_bool			new_connection = FALSE;
66 	zend_resource		*le;
67 	mysqli_plist_entry *plist = NULL;
68 	zend_bool			self_alloced = 0;
69 
70 
71 #if !defined(MYSQL_USE_MYSQLND)
72 	if ((MYSQL_VERSION_ID / 100) != (mysql_get_client_version() / 100)) {
73 		php_error_docref(NULL, E_WARNING,
74 						"Headers and client library minor version mismatch. Headers:%d Library:%ld",
75 						MYSQL_VERSION_ID, mysql_get_client_version());
76 	}
77 #endif
78 
79 	if (getThis() && !ZEND_NUM_ARGS() && in_ctor) {
80 		php_mysqli_init(INTERNAL_FUNCTION_PARAM_PASSTHRU, in_ctor);
81 		return;
82 	}
83 	hostname = username = dbname = passwd = socket = NULL;
84 
85 	if (!is_real_connect) {
86 		if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!s!s!s!l!s!", &hostname, &hostname_len, &username, &username_len,
87 				&passwd, &passwd_len, &dbname, &dbname_len, &port, &port_is_null, &socket, &socket_len) == FAILURE) {
88 			RETURN_THROWS();
89 		}
90 
91 		if (object && instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry)) {
92 			mysqli_resource = (Z_MYSQLI_P(object))->ptr;
93 			if (mysqli_resource && mysqli_resource->ptr) {
94 				mysql = (MY_MYSQL*) mysqli_resource->ptr;
95 			}
96 		}
97 		if (!mysql) {
98 			mysql = (MY_MYSQL *) ecalloc(1, sizeof(MY_MYSQL));
99 			self_alloced = 1;
100 		}
101 		flags |= CLIENT_MULTI_RESULTS; /* needed for mysql_multi_query() */
102 	} else {
103 		/* We have flags too */
104 		if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|s!s!s!s!l!s!l", &object, mysqli_link_class_entry,
105 				&hostname, &hostname_len, &username, &username_len, &passwd, &passwd_len, &dbname, &dbname_len, &port, &port_is_null, &socket, &socket_len, &flags) == FAILURE) {
106 			RETURN_THROWS();
107 		}
108 
109 		mysqli_resource = (Z_MYSQLI_P(object))->ptr;
110 		MYSQLI_FETCH_RESOURCE_CONN(mysql, object, MYSQLI_STATUS_INITIALIZED);
111 
112 		/* set some required options */
113 		flags |= CLIENT_MULTI_RESULTS; /* needed for mysql_multi_query() */
114 		/* remove some insecure options */
115 		flags &= ~CLIENT_MULTI_STATEMENTS;   /* don't allow multi_queries via connect parameter */
116 #ifndef MYSQLI_USE_MYSQLND
117 		if (PG(open_basedir) && PG(open_basedir)[0] != '\0') {
118 			flags &= ~CLIENT_LOCAL_FILES;
119 		}
120 #endif
121 	}
122 
123 	if (!socket_len || !socket) {
124 		socket = MyG(default_socket);
125 	}
126 	if (port_is_null || !port) {
127 		port = MyG(default_port);
128 	}
129 	if (!passwd) {
130 		passwd = MyG(default_pw);
131 		passwd_len = strlen(SAFE_STR(passwd));
132 	}
133 	if (!username){
134 		username = MyG(default_user);
135 	}
136 	if (!hostname || !hostname_len) {
137 		hostname = MyG(default_host);
138 	}
139 
140 	if (mysql->mysql && mysqli_resource &&
141 		(mysqli_resource->status > MYSQLI_STATUS_INITIALIZED))
142 	{
143 		/* already connected, we should close the connection */
144 		php_mysqli_close(mysql, MYSQLI_CLOSE_IMPLICIT, mysqli_resource->status);
145 	}
146 
147 	if (strlen(SAFE_STR(hostname)) > 2 && !strncasecmp(hostname, "p:", 2)) {
148 		hostname += 2;
149 		if (!MyG(allow_persistent)) {
150 			php_error_docref(NULL, E_WARNING, "Persistent connections are disabled. Downgrading to normal");
151 		} else {
152 			mysql->persistent = persistent = TRUE;
153 
154 			hash_key = strpprintf(0, "mysqli_%s_%s" ZEND_LONG_FMT "%s%s%s", SAFE_STR(hostname), SAFE_STR(socket),
155 								port, SAFE_STR(username), SAFE_STR(dbname),
156 								SAFE_STR(passwd));
157 
158 			mysql->hash_key = hash_key;
159 
160 			/* check if we can reuse existing connection ... */
161 			if ((le = zend_hash_find_ptr(&EG(persistent_list), hash_key)) != NULL) {
162 				if (le->type == php_le_pmysqli()) {
163 					plist = (mysqli_plist_entry *) le->ptr;
164 
165 					do {
166 						if (zend_ptr_stack_num_elements(&plist->free_links)) {
167 							/* If we have an initialized (but unconnected) mysql resource,
168 							 * close it before we reuse an existing persistent resource. */
169 							if (mysql->mysql) {
170 								mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT);
171 							}
172 
173 							mysql->mysql = zend_ptr_stack_pop(&plist->free_links);
174 
175 							MyG(num_inactive_persistent)--;
176 							/* reset variables */
177 
178 #ifndef MYSQLI_NO_CHANGE_USER_ON_PCONNECT
179 							if (!mysqli_change_user_silent(mysql->mysql, username, passwd, dbname, passwd_len)) {
180 #else
181 							if (!mysql_ping(mysql->mysql)) {
182 #endif
183 #ifdef MYSQLI_USE_MYSQLND
184 								mysqlnd_restart_psession(mysql->mysql);
185 #endif
186 								MyG(num_active_persistent)++;
187 
188 								/* clear error */
189 								php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql));
190 
191 								goto end;
192 							} else {
193 #ifdef MYSQLI_USE_MYSQLND
194 								if (mysql->mysql->data->vio->data->ssl) {
195 									/* copy over pre-existing ssl settings so we can reuse them when reconnecting */
196 									ssl = TRUE;
197 
198 									ssl_key = mysql->mysql->data->vio->data->options.ssl_key ? estrdup(mysql->mysql->data->vio->data->options.ssl_key) : NULL;
199 									ssl_cert = mysql->mysql->data->vio->data->options.ssl_cert ? estrdup(mysql->mysql->data->vio->data->options.ssl_cert) : NULL;
200 									ssl_ca = mysql->mysql->data->vio->data->options.ssl_ca ? estrdup(mysql->mysql->data->vio->data->options.ssl_ca) : NULL;
201 									ssl_capath = mysql->mysql->data->vio->data->options.ssl_capath ? estrdup(mysql->mysql->data->vio->data->options.ssl_capath) : NULL;
202 									ssl_cipher = mysql->mysql->data->vio->data->options.ssl_cipher ? estrdup(mysql->mysql->data->vio->data->options.ssl_cipher) : NULL;
203 								}
204 #else
205 								if (mysql->mysql->options.ssl_key
206 										|| mysql->mysql->options.ssl_cert
207 										|| mysql->mysql->options.ssl_ca
208 										|| mysql->mysql->options.ssl_capath
209 										|| mysql->mysql->options.ssl_cipher) {
210 									/* copy over pre-existing ssl settings so we can reuse them when reconnecting */
211 									ssl = TRUE;
212 
213 									ssl_key = mysql->mysql->options.ssl_key ? estrdup(mysql->mysql->options.ssl_key) : NULL;
214 									ssl_cert = mysql->mysql->options.ssl_cert ? estrdup(mysql->mysql->options.ssl_cert) : NULL;
215 									ssl_ca = mysql->mysql->options.ssl_ca ? estrdup(mysql->mysql->options.ssl_ca) : NULL;
216 									ssl_capath = mysql->mysql->options.ssl_capath ? estrdup(mysql->mysql->options.ssl_capath) : NULL;
217 									ssl_cipher = mysql->mysql->options.ssl_cipher ? estrdup(mysql->mysql->options.ssl_cipher) : NULL;
218 								}
219 #endif
220 								mysqli_close(mysql->mysql, MYSQLI_CLOSE_IMPLICIT);
221 								mysql->mysql = NULL;
222 							}
223 						}
224 					} while (0);
225 				}
226 			} else {
227 				plist = calloc(1, sizeof(mysqli_plist_entry));
228 
229 				zend_ptr_stack_init_ex(&plist->free_links, 1);
230 				zend_register_persistent_resource(ZSTR_VAL(hash_key), ZSTR_LEN(hash_key), plist, php_le_pmysqli());
231 			}
232 		}
233 	}
234 	if (MyG(max_links) != -1 && MyG(num_links) >= MyG(max_links)) {
235 		php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", MyG(num_links));
236 		goto err;
237 	}
238 
239 	if (persistent && MyG(max_persistent) != -1 &&
240 		(MyG(num_active_persistent) + MyG(num_inactive_persistent))>= MyG(max_persistent))
241 	{
242 		php_error_docref(NULL, E_WARNING, "Too many open persistent links (" ZEND_LONG_FMT ")",
243 								MyG(num_active_persistent) + MyG(num_inactive_persistent));
244 		goto err;
245 	}
246 	if (!mysql->mysql) {
247 #ifndef MYSQLI_USE_MYSQLND
248 		if (!(mysql->mysql = mysql_init(NULL))) {
249 #else
250 		if (!(mysql->mysql = mysqlnd_init(MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA, persistent))) {
251 #endif
252 			goto err;
253 		}
254 		new_connection = TRUE;
255 	}
256 
257 #ifndef MYSQLI_USE_MYSQLND
258 	/* BC for prior to bug fix #53425 */
259 	flags |= CLIENT_MULTI_RESULTS;
260 
261 	if (ssl) {
262 		/* if we're here, this means previous conn was ssl, repopulate settings */
263 		mysql_ssl_set(mysql->mysql, ssl_key, ssl_cert, ssl_ca, ssl_capath, ssl_cipher);
264 
265 		if (ssl_key) {
266 		    efree(ssl_key);
267 		}
268 
269 		if (ssl_cert) {
270 		    efree(ssl_cert);
271 		}
272 
273 		if (ssl_ca) {
274 		    efree(ssl_ca);
275 		}
276 
277 		if (ssl_capath) {
278 		    efree(ssl_capath);
279 		}
280 
281 		if (ssl_cipher) {
282 		    efree(ssl_cipher);
283 		}
284 	}
285 	if (mysql_real_connect(mysql->mysql, hostname, username, passwd, dbname, port, socket, flags) == NULL)
286 #else
287 	if (ssl) {
288 		/* if we're here, this means previous conn was ssl, repopulate settings */
289 		mysql_ssl_set(mysql->mysql, ssl_key, ssl_cert, ssl_ca, ssl_capath, ssl_cipher);
290 
291 		if (ssl_key) {
292 		    efree(ssl_key);
293 		}
294 
295 		if (ssl_cert) {
296 		    efree(ssl_cert);
297 		}
298 
299 		if (ssl_ca) {
300 		    efree(ssl_ca);
301 		}
302 
303 		if (ssl_capath) {
304 		    efree(ssl_capath);
305 		}
306 
307 		if (ssl_cipher) {
308 		    efree(ssl_cipher);
309 		}
310 	}
311 	if (mysqlnd_connect(mysql->mysql, hostname, username, passwd, passwd_len, dbname, dbname_len,
312 						port, socket, flags, MYSQLND_CLIENT_KNOWS_RSET_COPY_DATA) == NULL)
313 #endif
314 	{
315 		/* Save error messages - for mysqli_connect_error() & mysqli_connect_errno() */
316 		php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql));
317 		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql),
318 										"%s", mysql_error(mysql->mysql));
319 		if (!is_real_connect) {
320 			/* free mysql structure */
321 			mysqli_close(mysql->mysql, MYSQLI_CLOSE_DISCONNECTED);
322 			mysql->mysql = NULL;
323 		}
324 		goto err;
325 	}
326 
327 	/* clear error */
328 	php_mysqli_set_error(mysql_errno(mysql->mysql), (char *) mysql_error(mysql->mysql));
329 
330 #ifndef MYSQLI_USE_MYSQLND
331 	char reconnect = MyG(reconnect);
332 	mysql_options(mysql->mysql, MYSQL_OPT_RECONNECT, (char *)&reconnect);
333 #endif
334 	unsigned int allow_local_infile = MyG(allow_local_infile);
335 	mysql_options(mysql->mysql, MYSQL_OPT_LOCAL_INFILE, (char *)&allow_local_infile);
336 
337 end:
338 	if (!mysqli_resource) {
339 		mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
340 		mysqli_resource->ptr = (void *)mysql;
341 	}
342 	mysqli_resource->status = MYSQLI_STATUS_VALID;
343 
344 	/* store persistent connection */
345 	if (persistent && (new_connection || is_real_connect)) {
346 		MyG(num_active_persistent)++;
347 	}
348 
349 	MyG(num_links)++;
350 
351 	mysql->multi_query = 0;
352 
353 	if (!object || !instanceof_function(Z_OBJCE_P(object), mysqli_link_class_entry)) {
354 		MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_link_class_entry);
355 	} else {
356 		(Z_MYSQLI_P(object))->ptr = mysqli_resource;
357 	}
358 	if (!is_real_connect) {
359 		return;
360 	} else {
361 		RETURN_TRUE;
362 	}
363 
364 err:
365 	if (mysql->hash_key) {
366 		zend_string_release_ex(mysql->hash_key, 0);
367 		mysql->hash_key = NULL;
368 		mysql->persistent = FALSE;
369 	}
370 	if (!is_real_connect && self_alloced) {
371 		efree(mysql);
372 	}
373 	RETVAL_FALSE;
374 } /* }}} */
375 
376 /* {{{ Open a connection to a mysql server */
377 PHP_FUNCTION(mysqli_connect)
378 {
379 	mysqli_common_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, FALSE, FALSE);
380 }
381 /* }}} */
382 
383 PHP_METHOD(mysqli, __construct)
384 {
385 	mysqli_common_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, FALSE, TRUE);
386 }
387 
388 /* {{{ Initialize mysqli and return a resource for use with mysql_real_connect */
389 PHP_METHOD(mysqli, init)
390 {
391 	php_mysqli_init(INTERNAL_FUNCTION_PARAM_PASSTHRU, TRUE);
392 }
393 /* }}} */
394 
395 /* {{{ Returns the numerical value of the error message from last connect command */
396 PHP_FUNCTION(mysqli_connect_errno)
397 {
398 	if (zend_parse_parameters_none() == FAILURE) {
399 		RETURN_THROWS();
400 	}
401 
402 	RETURN_LONG(MyG(error_no));
403 }
404 /* }}} */
405 
406 /* {{{ Returns the text of the error message from previous MySQL operation */
407 PHP_FUNCTION(mysqli_connect_error)
408 {
409 	if (zend_parse_parameters_none() == FAILURE) {
410 		RETURN_THROWS();
411 	}
412 
413 	if (MyG(error_msg)) {
414 		RETURN_STRING(MyG(error_msg));
415 	} else {
416 		RETURN_NULL();
417 	}
418 }
419 /* }}} */
420 
421 /* {{{ Fetch a result row as an associative array, a numeric array, or both */
422 PHP_FUNCTION(mysqli_fetch_array)
423 {
424 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
425 }
426 /* }}} */
427 
428 /* {{{ Fetch a result row as an associative array */
429 PHP_FUNCTION(mysqli_fetch_assoc)
430 {
431 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0);
432 }
433 /* }}} */
434 
435 /* {{{ Fetches all result rows as an associative array, a numeric array, or both */
436 #ifdef MYSQLI_USE_MYSQLND
437 PHP_FUNCTION(mysqli_fetch_all)
438 {
439 	MYSQL_RES	*result;
440 	zval		*mysql_result;
441 	zend_long		mode = MYSQLND_FETCH_NUM;
442 
443 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) {
444 		RETURN_THROWS();
445 	}
446 	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
447 
448 	if (!mode || (mode & ~MYSQLND_FETCH_BOTH)) {
449 		zend_argument_value_error(ERROR_ARG_POS(2), "must be one of MYSQLI_FETCH_NUM, "
450 		                 "MYSQLI_FETCH_ASSOC, or MYSQLI_FETCH_BOTH");
451 		RETURN_THROWS();
452 	}
453 
454 	mysqlnd_fetch_all(result, mode, return_value);
455 }
456 /* }}} */
457 
458 /* {{{ Returns statistics about the zval cache */
459 PHP_FUNCTION(mysqli_get_client_stats)
460 {
461 	if (zend_parse_parameters_none() == FAILURE) {
462 		RETURN_THROWS();
463 	}
464 	mysqlnd_get_client_stats(return_value);
465 }
466 /* }}} */
467 
468 /* {{{ Returns statistics about the zval cache */
469 PHP_FUNCTION(mysqli_get_connection_stats)
470 {
471 	MY_MYSQL	*mysql;
472 	zval		*mysql_link;
473 
474 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
475 									 &mysql_link, mysqli_link_class_entry) == FAILURE) {
476 		RETURN_THROWS();
477 	}
478 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
479 
480 	mysqlnd_get_connection_stats(mysql->mysql, return_value);
481 }
482 #endif
483 /* }}} */
484 
485 /* {{{ Fetches all client errors */
486 PHP_FUNCTION(mysqli_error_list)
487 {
488 	MY_MYSQL	*mysql;
489 	zval		*mysql_link;
490 
491 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
492 		RETURN_THROWS();
493 	}
494 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
495 #ifdef MYSQLI_USE_MYSQLND
496 	if (1) {
497 		MYSQLND_ERROR_LIST_ELEMENT * message;
498 		zend_llist_position pos;
499 		array_init(return_value);
500 		for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&mysql->mysql->data->error_info->error_list, &pos);
501 			 message;
502 			 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&mysql->mysql->data->error_info->error_list, &pos))
503 		{
504 			zval single_error;
505 			array_init(&single_error);
506 			add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
507 			add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
508 			add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
509 			add_next_index_zval(return_value, &single_error);
510 		}
511 	} else {
512 		RETURN_EMPTY_ARRAY();
513 	}
514 #else
515 	if (mysql_errno(mysql->mysql)) {
516 		zval single_error;
517 		array_init(return_value);
518 		array_init(&single_error);
519 		add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_errno(mysql->mysql));
520 		add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_sqlstate(mysql->mysql));
521 		add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_error(mysql->mysql));
522 		add_next_index_zval(return_value, &single_error);
523 	} else {
524 		RETURN_EMPTY_ARRAY();
525 	}
526 #endif
527 }
528 /* }}} */
529 
530 /* {{{ */
531 PHP_FUNCTION(mysqli_stmt_error_list)
532 {
533 	MY_STMT	*stmt;
534 	zval 	*mysql_stmt;
535 
536 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
537 		RETURN_THROWS();
538 	}
539 	MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_INITIALIZED);
540 #ifdef MYSQLI_USE_MYSQLND
541 	if (stmt->stmt && stmt->stmt->data && stmt->stmt->data->error_info) {
542 		MYSQLND_ERROR_LIST_ELEMENT * message;
543 		zend_llist_position pos;
544 		array_init(return_value);
545 		for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&stmt->stmt->data->error_info->error_list, &pos);
546 			 message;
547 			 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&stmt->stmt->data->error_info->error_list, &pos))
548 		{
549 			zval single_error;
550 			array_init(&single_error);
551 			add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
552 			add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
553 			add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
554 			add_next_index_zval(return_value, &single_error);
555 		}
556 	} else {
557 		RETURN_EMPTY_ARRAY();
558 	}
559 #else
560 	if (mysql_stmt_errno(stmt->stmt)) {
561 		zval single_error;
562 		array_init(return_value);
563 		array_init(&single_error);
564 		add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_stmt_errno(stmt->stmt));
565 		add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_stmt_sqlstate(stmt->stmt));
566 		add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_stmt_error(stmt->stmt));
567 		add_next_index_zval(return_value, &single_error);
568 	} else {
569 		RETURN_EMPTY_ARRAY();
570 	}
571 #endif
572 }
573 /* }}} */
574 
575 /* {{{ Fetch a result row as an object */
576 PHP_FUNCTION(mysqli_fetch_object)
577 {
578 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1);
579 }
580 /* }}} */
581 
582 /* {{{ allows to execute multiple queries  */
583 PHP_FUNCTION(mysqli_multi_query)
584 {
585 	MY_MYSQL	*mysql;
586 	zval		*mysql_link;
587 	char		*query = NULL;
588 	size_t 		query_len;
589 
590 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &query, &query_len) == FAILURE) {
591 		RETURN_THROWS();
592 	}
593 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
594 
595 	MYSQLI_ENABLE_MQ;
596 	if (mysql_real_query(mysql->mysql, query, query_len)) {
597 #ifndef MYSQLI_USE_MYSQLND
598 		char s_error[MYSQL_ERRMSG_SIZE], s_sqlstate[SQLSTATE_LENGTH+1];
599 		unsigned int s_errno;
600 		/* we have to save error information, cause
601 		MYSQLI_DISABLE_MQ will reset error information */
602 		strcpy(s_error, mysql_error(mysql->mysql));
603 		strcpy(s_sqlstate, mysql_sqlstate(mysql->mysql));
604 		s_errno = mysql_errno(mysql->mysql);
605 #else
606 		MYSQLND_ERROR_INFO error_info = *mysql->mysql->data->error_info;
607 		mysql->mysql->data->error_info->error_list.head = NULL;
608 		mysql->mysql->data->error_info->error_list.tail = NULL;
609 		mysql->mysql->data->error_info->error_list.count = 0;
610 #endif
611 		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
612 		MYSQLI_DISABLE_MQ;
613 
614 #ifndef MYSQLI_USE_MYSQLND
615 		/* restore error information */
616 		strcpy(mysql->mysql->net.last_error, s_error);
617 		strcpy(mysql->mysql->net.sqlstate, s_sqlstate);
618 		mysql->mysql->net.last_errno = s_errno;
619 #else
620 		zend_llist_clean(&mysql->mysql->data->error_info->error_list);
621 		*mysql->mysql->data->error_info = error_info;
622 #endif
623 		RETURN_FALSE;
624 	}
625 	RETURN_TRUE;
626 }
627 /* }}} */
628 
629 /* {{{ */
630 PHP_FUNCTION(mysqli_query)
631 {
632 	MY_MYSQL			*mysql;
633 	zval				*mysql_link;
634 	MYSQLI_RESOURCE		*mysqli_resource;
635 	MYSQL_RES 			*result = NULL;
636 	char				*query = NULL;
637 	size_t 				query_len;
638 	zend_long 				resultmode = MYSQLI_STORE_RESULT;
639 
640 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os|l", &mysql_link, mysqli_link_class_entry, &query, &query_len, &resultmode) == FAILURE) {
641 		RETURN_THROWS();
642 	}
643 
644 	if (!query_len) {
645 		zend_argument_value_error(ERROR_ARG_POS(2), "cannot be empty");
646 		RETURN_THROWS();
647 	}
648 	if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT &&
649 		MYSQLI_STORE_RESULT != (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA))
650 	) {
651 		zend_argument_value_error(ERROR_ARG_POS(3), "must be either MYSQLI_USE_RESULT or MYSQLI_STORE_RESULT"
652 			#ifdef MYSQLI_USE_MYSQLND
653 				" with MYSQLI_ASYNC as an optional bitmask flag"
654 			#endif
655 		);
656 		RETURN_THROWS();
657 	}
658 
659 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
660 
661 	MYSQLI_DISABLE_MQ;
662 
663 
664 #ifdef MYSQLI_USE_MYSQLND
665 	if (resultmode & MYSQLI_ASYNC) {
666 		if (mysqli_async_query(mysql->mysql, query, query_len)) {
667 			MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
668 			RETURN_FALSE;
669 		}
670 		mysql->async_result_fetch_type = resultmode & ~MYSQLI_ASYNC;
671 		RETURN_TRUE;
672 	}
673 #endif
674 
675 	if (mysql_real_query(mysql->mysql, query, query_len)) {
676 		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
677 		RETURN_FALSE;
678 	}
679 
680 	if (!mysql_field_count(mysql->mysql)) {
681 		/* no result set - not a SELECT */
682 		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
683 			php_mysqli_report_index(query, mysqli_server_status(mysql->mysql));
684 		}
685 		RETURN_TRUE;
686 	}
687 
688 #ifdef MYSQLI_USE_MYSQLND
689 	switch (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) {
690 #else
691 	switch (resultmode & ~MYSQLI_ASYNC) {
692 #endif
693 		case MYSQLI_STORE_RESULT:
694 #ifdef MYSQLI_USE_MYSQLND
695 			if (resultmode & MYSQLI_STORE_RESULT_COPY_DATA) {
696 				result = mysqlnd_store_result_ofs(mysql->mysql);
697 			} else
698 #endif
699 				result = mysql_store_result(mysql->mysql);
700 			break;
701 		case MYSQLI_USE_RESULT:
702 			result = mysql_use_result(mysql->mysql);
703 			break;
704 	}
705 	if (!result) {
706 		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql),
707 										"%s", mysql_error(mysql->mysql));
708 		RETURN_FALSE;
709 	}
710 
711 	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
712 		php_mysqli_report_index(query, mysqli_server_status(mysql->mysql));
713 	}
714 
715 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
716 	mysqli_resource->ptr = (void *)result;
717 	mysqli_resource->status = MYSQLI_STATUS_VALID;
718 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
719 }
720 /* }}} */
721 
722 #ifdef MYSQLI_USE_MYSQLND
723 #include "php_network.h"
724 /* {{{ mysqlnd_zval_array_to_mysqlnd_array functions */
725 static int mysqlnd_zval_array_to_mysqlnd_array(zval *in_array, MYSQLND ***out_array)
726 {
727 	zval *elem;
728 	int i = 0, current = 0;
729 
730 	if (Z_TYPE_P(in_array) != IS_ARRAY) {
731 		return SUCCESS;
732 	}
733 	*out_array = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(in_array)) + 1, sizeof(MYSQLND *));
734 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(in_array), elem) {
735 		i++;
736 		if (Z_TYPE_P(elem) != IS_OBJECT ||
737 			!instanceof_function(Z_OBJCE_P(elem), mysqli_link_class_entry)) {
738 			zend_argument_type_error(i, "must be an instance of mysqli, %s given", zend_zval_type_name(elem));
739 			return FAILURE;
740 		} else {
741 			MY_MYSQL *mysql;
742 			MYSQLI_RESOURCE *my_res;
743 			mysqli_object *intern = Z_MYSQLI_P(elem);
744 			if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
745 				zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name));
746 				return FAILURE;
747 		  	}
748 			mysql = (MY_MYSQL*) my_res->ptr;
749 			if (my_res->status < MYSQLI_STATUS_VALID) {
750 				zend_throw_error(NULL, "%s object is not fully initialized", ZSTR_VAL(intern->zo.ce->name));
751 				return FAILURE;
752 			}
753 			(*out_array)[current++] = mysql->mysql;
754 		}
755 	} ZEND_HASH_FOREACH_END();
756 	return SUCCESS;
757 }
758 /* }}} */
759 
760 /* {{{ mysqlnd_zval_array_from_mysqlnd_array */
761 static int mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *out_array)
762 {
763 	MYSQLND **p = in_array;
764 	zval dest_array;
765 	zval *elem, *dest_elem;
766 	int ret = 0;
767 
768 	array_init_size(&dest_array, zend_hash_num_elements(Z_ARRVAL_P(out_array)));
769 
770 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(out_array), elem) {
771 		if (Z_TYPE_P(elem) != IS_OBJECT ||
772 				!instanceof_function(Z_OBJCE_P(elem), mysqli_link_class_entry)) {
773 			continue;
774 		}
775 		{
776 			MY_MYSQL *mysql;
777 			MYSQLI_RESOURCE *my_res;
778 			mysqli_object *intern = Z_MYSQLI_P(elem);
779 			if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
780 				zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name));
781 				return FAILURE;
782 		  	}
783 			mysql = (MY_MYSQL *) my_res->ptr;
784 			if (mysql->mysql == *p) {
785 				dest_elem = zend_hash_next_index_insert(Z_ARRVAL(dest_array), elem);
786 				if (dest_elem) {
787 					zval_add_ref(dest_elem);
788 				}
789 				ret++;
790 				p++;
791 			}
792 		}
793 	} ZEND_HASH_FOREACH_END();
794 
795 	/* destroy old array and add new one */
796 	zval_ptr_dtor(out_array);
797 	ZVAL_COPY_VALUE(out_array, &dest_array);
798 
799 	return 0;
800 }
801 /* }}} */
802 
803 /* {{{ mysqlnd_dont_poll_zval_array_from_mysqlnd_array */
804 static int mysqlnd_dont_poll_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *in_zval_array, zval *out_array)
805 {
806 	MYSQLND **p = in_array;
807 	zval proxy, *elem, *dest_elem;
808 	int ret = 0;
809 
810 	array_init(&proxy);
811 	if (in_array) {
812 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(in_zval_array), elem) {
813 			MY_MYSQL *mysql;
814 			mysqli_object *intern = Z_MYSQLI_P(elem);
815 			mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)intern->ptr)->ptr;
816 			if (mysql->mysql == *p) {
817 				dest_elem = zend_hash_next_index_insert(Z_ARRVAL(proxy), elem);
818 				if (dest_elem) {
819 					zval_add_ref(dest_elem);
820 				}
821 				ret++;
822 				p++;
823 			}
824 		} ZEND_HASH_FOREACH_END();
825 	}
826 
827 	/* destroy old array and add new one */
828 	zval_ptr_dtor(out_array);
829 	ZVAL_COPY_VALUE(out_array, &proxy);
830 
831 	return 0;
832 }
833 /* }}} */
834 
835 /* {{{ Poll connections */
836 PHP_FUNCTION(mysqli_poll)
837 {
838 	zval			*r_array, *e_array, *dont_poll_array;
839 	MYSQLND			**new_r_array = NULL, **new_e_array = NULL, **new_dont_poll_array = NULL;
840 	zend_long			sec = 0, usec = 0;
841 	enum_func_status ret;
842 	int 			desc_num;
843 
844 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!al|l", &r_array, &e_array, &dont_poll_array, &sec, &usec) == FAILURE) {
845 		RETURN_THROWS();
846 	}
847 	if (sec < 0) {
848 		zend_argument_value_error(4, "must be greater than or equal to 0");
849 		RETURN_THROWS();
850 	}
851 	if (usec < 0) {
852 		zend_argument_value_error(5, "must be greater than or equal to 0");
853 		RETURN_THROWS();
854 	}
855 
856 	// TODO Error promotion
857 	if (!r_array && !e_array) {
858 		php_error_docref(NULL, E_WARNING, "No stream arrays were passed");
859 		RETURN_FALSE;
860 	}
861 
862 	if (r_array != NULL) {
863 		if (mysqlnd_zval_array_to_mysqlnd_array(r_array, &new_r_array) == FAILURE) {
864 			efree(new_r_array);
865 			RETURN_THROWS();
866 		}
867 	}
868 	if (e_array != NULL) {
869 		if (mysqlnd_zval_array_to_mysqlnd_array(e_array, &new_e_array) == FAILURE) {
870 			efree(new_e_array);
871 			efree(new_r_array);
872 			RETURN_THROWS();
873 		}
874 	}
875 
876 	ret = mysqlnd_poll(new_r_array, new_e_array, &new_dont_poll_array, sec, usec, &desc_num);
877 
878 	mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array);
879 
880 	if (r_array != NULL) {
881 		mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array);
882 	}
883 	if (e_array != NULL) {
884 		mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array);
885 	}
886 
887 	if (new_dont_poll_array) {
888 		efree(new_dont_poll_array);
889 	}
890 	if (new_r_array) {
891 		efree(new_r_array);
892 	}
893 	if (new_e_array) {
894 		efree(new_e_array);
895 	}
896 	if (ret == PASS) {
897 		RETURN_LONG(desc_num);
898 	} else {
899 		RETURN_FALSE;
900 	}
901 }
902 /* }}} */
903 
904 /* {{{ Poll connections */
905 PHP_FUNCTION(mysqli_reap_async_query)
906 {
907 	MY_MYSQL		*mysql;
908 	zval			*mysql_link;
909 	MYSQLI_RESOURCE		*mysqli_resource;
910 	MYSQL_RES 			*result = NULL;
911 
912 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
913 		RETURN_THROWS();
914 	}
915 
916 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
917 
918 	if (FAIL == mysqlnd_reap_async_query(mysql->mysql)) {
919 		RETURN_FALSE;
920 	}
921 
922 	if (!mysql_field_count(mysql->mysql)) {
923 		/* no result set - not a SELECT */
924 		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
925 /*			php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
926 		}
927 		RETURN_TRUE;
928 	}
929 
930 	switch (mysql->async_result_fetch_type) {
931 		case MYSQLI_STORE_RESULT:
932 			result = mysql_store_result(mysql->mysql);
933 			break;
934 		case MYSQLI_USE_RESULT:
935 			result = mysql_use_result(mysql->mysql);
936 			break;
937 	}
938 
939 	if (!result) {
940 		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql),
941 										"%s", mysql_error(mysql->mysql));
942 		RETURN_FALSE;
943 	}
944 
945 	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
946 /*		php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
947 	}
948 
949 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
950 	mysqli_resource->ptr = (void *)result;
951 	mysqli_resource->status = MYSQLI_STATUS_VALID;
952 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
953 }
954 /* }}} */
955 
956 /* {{{ Buffer result set on client */
957 PHP_FUNCTION(mysqli_stmt_get_result)
958 {
959 	MYSQL_RES 		*result;
960 	MYSQLI_RESOURCE	*mysqli_resource;
961 	MY_STMT			*stmt;
962 	zval 			*mysql_stmt;
963 
964 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
965 		RETURN_THROWS();
966 	}
967 	MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
968 
969 	if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) {
970 		MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
971 		RETURN_FALSE;
972 	}
973 
974 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
975 	mysqli_resource->ptr = (void *)result;
976 	mysqli_resource->status = MYSQLI_STATUS_VALID;
977 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
978 }
979 /* }}} */
980 #endif
981 
982 /* {{{ */
983 PHP_FUNCTION(mysqli_get_warnings)
984 {
985 	MY_MYSQL			*mysql;
986 	zval				*mysql_link;
987 	MYSQLI_RESOURCE		*mysqli_resource;
988 	MYSQLI_WARNING		*w = NULL;
989 
990 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
991 		RETURN_THROWS();
992 	}
993 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
994 
995 	if (mysql_warning_count(mysql->mysql)) {
996 #ifdef MYSQLI_USE_MYSQLND
997 		w = php_get_warnings(mysql->mysql->data);
998 #else
999 		w = php_get_warnings(mysql->mysql);
1000 #endif
1001 	}
1002 	if (!w) {
1003 		RETURN_FALSE;
1004 	}
1005 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
1006 	mysqli_resource->ptr = mysqli_resource->info = (void *)w;
1007 	mysqli_resource->status = MYSQLI_STATUS_VALID;
1008 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
1009 }
1010 /* }}} */
1011 
1012 /* {{{ */
1013 PHP_FUNCTION(mysqli_stmt_get_warnings)
1014 {
1015 	MY_STMT				*stmt;
1016 	zval				*stmt_link;
1017 	MYSQLI_RESOURCE		*mysqli_resource;
1018 	MYSQLI_WARNING		*w = NULL;
1019 
1020 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) {
1021 		RETURN_THROWS();
1022 	}
1023 	MYSQLI_FETCH_RESOURCE_STMT(stmt, stmt_link, MYSQLI_STATUS_VALID);
1024 
1025 	if (mysqli_stmt_warning_count(stmt->stmt)) {
1026 		w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt));
1027 	}
1028 	if (!w) {
1029 		RETURN_FALSE;
1030 	}
1031 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
1032 	mysqli_resource->ptr = mysqli_resource->info = (void *)w;
1033 	mysqli_resource->status = MYSQLI_STATUS_VALID;
1034 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
1035 }
1036 /* }}} */
1037 
1038 /* {{{ sets client character set */
1039 PHP_FUNCTION(mysqli_set_charset)
1040 {
1041 	MY_MYSQL	*mysql;
1042 	zval		*mysql_link;
1043 	char		*cs_name;
1044 	size_t		csname_len;
1045 
1046 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &csname_len) == FAILURE) {
1047 		RETURN_THROWS();
1048 	}
1049 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1050 
1051 	if (mysql_set_character_set(mysql->mysql, cs_name)) {
1052 		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
1053 		RETURN_FALSE;
1054 	}
1055 	RETURN_TRUE;
1056 }
1057 /* }}} */
1058 
1059 /* {{{ returns a character set object */
1060 PHP_FUNCTION(mysqli_get_charset)
1061 {
1062 	MY_MYSQL				*mysql;
1063 	zval					*mysql_link;
1064 	const char 				*name = NULL, *collation = NULL, *dir = NULL, *comment = NULL;
1065 	uint32_t				minlength, maxlength, number, state;
1066 #ifndef MYSQLI_USE_MYSQLND
1067 	MY_CHARSET_INFO			cs;
1068 #else
1069 	const MYSQLND_CHARSET	*cs;
1070 #endif
1071 
1072 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
1073 		RETURN_THROWS();
1074 	}
1075 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1076 
1077 
1078 #ifndef MYSQLI_USE_MYSQLND
1079 	mysql_get_character_set_info(mysql->mysql, &cs);
1080 	name = (char *)cs.csname;
1081 	collation = (char *)cs.name;
1082 	dir = (char *)cs.dir;
1083 	minlength = cs.mbminlen;
1084 	maxlength = cs.mbmaxlen;
1085 	number = cs.number;
1086 	state = cs.state;
1087 	comment = cs.comment;
1088 #else
1089 	cs = mysql->mysql->data->charset;
1090 	if (!cs) {
1091 		php_error_docref(NULL, E_WARNING, "The connection has no charset associated");
1092 		RETURN_NULL();
1093 	}
1094 	name = cs->name;
1095 	collation = cs->collation;
1096 	minlength = cs->char_minlen;
1097 	maxlength = cs->char_maxlen;
1098 	number = cs->nr;
1099 	comment = cs->comment;
1100 	state = 1;	/* all charsets are compiled in */
1101 #endif
1102 	object_init(return_value);
1103 
1104 	add_property_string(return_value, "charset", (name) ? (char *)name : "");
1105 	add_property_string(return_value, "collation",(collation) ? (char *)collation : "");
1106 	add_property_string(return_value, "dir", (dir) ? (char *)dir : "");
1107 	add_property_long(return_value, "min_length", minlength);
1108 	add_property_long(return_value, "max_length", maxlength);
1109 	add_property_long(return_value, "number", number);
1110 	add_property_long(return_value, "state", state);
1111 	add_property_string(return_value, "comment", (comment) ? (char *)comment : "");
1112 }
1113 /* }}} */
1114 
1115 #ifndef MYSQLI_USE_MYSQLND
1116 extern char * mysqli_escape_string_for_tx_name_in_comment(const char * const name);
1117 
1118 /* {{{ */
1119 static int mysqli_begin_transaction_libmysql(MYSQL * conn, const unsigned int mode, const char * const name)
1120 {
1121 	int ret;
1122 	smart_str tmp_str = {0};
1123 	char * name_esc;
1124 	char * query;
1125 	unsigned int query_len;
1126 	if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
1127 		if (tmp_str.s) {
1128 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1129 		}
1130 		smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
1131 	}
1132 	if (mode & TRANS_START_READ_WRITE) {
1133 		if (tmp_str.s) {
1134 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1135 		}
1136 		smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
1137 	} else if (mode & TRANS_START_READ_ONLY) {
1138 		if (tmp_str.s) {
1139 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1140 		}
1141 		smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
1142 	}
1143 	smart_str_0(&tmp_str);
1144 
1145 	name_esc = mysqli_escape_string_for_tx_name_in_comment(name);
1146 	query_len = spprintf(&query, 0, "START TRANSACTION%s %s",
1147 						 name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
1148 
1149 	smart_str_free(&tmp_str);
1150 	if (name_esc) {
1151 		efree(name_esc);
1152 	}
1153 
1154 	ret = mysql_real_query(conn, query, query_len);
1155 	efree(query);
1156 
1157 	if (ret && mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY) && mysql_errno(conn) == 1064) {
1158 		php_error_docref(NULL, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
1159 	}
1160 	return ret;
1161 }
1162 /* }}} */
1163 #endif
1164 
1165 /* {{{ Starts a transaction */
1166 PHP_FUNCTION(mysqli_begin_transaction)
1167 {
1168 	MY_MYSQL	*mysql;
1169 	zval		*mysql_link;
1170 	zend_long		flags = TRANS_START_NO_OPT;
1171 	char *		name = NULL;
1172 	size_t			name_len = 0;
1173 
1174 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ls!", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) {
1175 		RETURN_THROWS();
1176 	}
1177 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1178 	if (flags < 0) {
1179 		zend_argument_value_error(ERROR_ARG_POS(2), "must be one of the MYSQLI_TRANS_* constants");
1180 		RETURN_THROWS();
1181 	}
1182 	if (name && !name_len) {
1183 		zend_argument_value_error(ERROR_ARG_POS(3), "cannot be empty");
1184 		RETURN_THROWS();
1185 	}
1186 
1187 #ifndef MYSQLI_USE_MYSQLND
1188 	if (mysqli_begin_transaction_libmysql(mysql->mysql, flags, name)) {
1189 		RETURN_FALSE;
1190 	}
1191 #else
1192 	if (FAIL == mysqlnd_begin_transaction(mysql->mysql, flags, name)) {
1193 		RETURN_FALSE;
1194 	}
1195 #endif
1196 	RETURN_TRUE;
1197 }
1198 /* }}} */
1199 
1200 #ifndef MYSQLI_USE_MYSQLND
1201 /* {{{ */
1202 static int mysqli_savepoint_libmysql(MYSQL * conn, const char * const name, zend_bool release)
1203 {
1204 	int ret;
1205 	char * query;
1206 	unsigned int query_len = spprintf(&query, 0, release? "RELEASE SAVEPOINT `%s`":"SAVEPOINT `%s`", name);
1207 	ret = mysql_real_query(conn, query, query_len);
1208 	efree(query);
1209 	return ret;
1210 }
1211 /* }}} */
1212 #endif
1213 
1214 /* {{{ Starts a transaction */
1215 PHP_FUNCTION(mysqli_savepoint)
1216 {
1217 	MY_MYSQL	*mysql;
1218 	zval		*mysql_link;
1219 	char *		name = NULL;
1220 	size_t			name_len;
1221 
1222 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1223 		RETURN_THROWS();
1224 	}
1225 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1226 	if (name_len == 0) {
1227 		zend_argument_value_error(ERROR_ARG_POS(2), "cannot be empty");
1228 		RETURN_THROWS();
1229 	}
1230 
1231 #ifndef MYSQLI_USE_MYSQLND
1232 	if (mysqli_savepoint_libmysql(mysql->mysql, name, FALSE)) {
1233 #else
1234 	if (FAIL == mysqlnd_savepoint(mysql->mysql, name)) {
1235 #endif
1236 		RETURN_FALSE;
1237 	}
1238 	RETURN_TRUE;
1239 }
1240 /* }}} */
1241 
1242 /* {{{ Starts a transaction */
1243 PHP_FUNCTION(mysqli_release_savepoint)
1244 {
1245 	MY_MYSQL	*mysql;
1246 	zval		*mysql_link;
1247 	char *		name = NULL;
1248 	size_t			name_len;
1249 
1250 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1251 		RETURN_THROWS();
1252 	}
1253 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1254 	if (name_len == 0) {
1255 		zend_argument_value_error(ERROR_ARG_POS(2), "cannot be empty");
1256 		RETURN_THROWS();
1257 	}
1258 #ifndef MYSQLI_USE_MYSQLND
1259 	if (mysqli_savepoint_libmysql(mysql->mysql, name, TRUE)) {
1260 #else
1261 	if (FAIL == mysqlnd_release_savepoint(mysql->mysql, name)) {
1262 #endif
1263 		RETURN_FALSE;
1264 	}
1265 	RETURN_TRUE;
1266 }
1267 /* }}} */
1268 
1269 /* {{{ Returns information about open and cached links */
1270 PHP_FUNCTION(mysqli_get_links_stats)
1271 {
1272 	if (zend_parse_parameters_none() == FAILURE) {
1273 		RETURN_THROWS();
1274 	}
1275 
1276 	array_init(return_value);
1277 	add_assoc_long_ex(return_value, "total", sizeof("total") - 1, MyG(num_links));
1278 	add_assoc_long_ex(return_value, "active_plinks", sizeof("active_plinks") - 1, MyG(num_active_persistent));
1279 	add_assoc_long_ex(return_value, "cached_plinks", sizeof("cached_plinks") - 1, MyG(num_inactive_persistent));
1280 }
1281 /* }}} */
1282