xref: /PHP-7.4/ext/mysqli/mysqli_nonapi.c (revision 740f0f61)
1 /*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) The PHP Group                                          |
6   +----------------------------------------------------------------------+
7   | This source file is subject to version 3.01 of the PHP license,      |
8   | that is bundled with this package in the file LICENSE, and is        |
9   | available through the world-wide-web at the following url:           |
10   | http://www.php.net/license/3_01.txt                                  |
11   | If you did not receive a copy of the PHP license and are unable to   |
12   | obtain it through the world-wide-web, please send a note to          |
13   | license@php.net so we can mail you a copy immediately.               |
14   +----------------------------------------------------------------------+
15   | Authors: Georg Richter <georg@php.net>                               |
16   |          Andrey Hristov <andrey@php.net>                             |
17   |          Ulf Wendel <uw@php.net>                                     |
18   +----------------------------------------------------------------------+
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include <signal.h>
26 
27 #include "php.h"
28 #include "php_ini.h"
29 #include "ext/standard/info.h"
30 #include "zend_smart_str.h"
31 #include "php_mysqli_structs.h"
32 #include "mysqli_priv.h"
33 
34 #define SAFE_STR(a) ((a)?a:"")
35 
36 /* {{{ php_mysqli_set_error
37  */
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;
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;
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 #if !defined(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 #if !defined(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 #if !defined(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 #if !defined(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 /* {{{ proto object mysqli_connect([string hostname [,string username [,string passwd [,string dbname [,int port [,string socket]]]]]])
377    Open a connection to a mysql server */
378 PHP_FUNCTION(mysqli_connect)
379 {
380 	mysqli_common_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, FALSE, FALSE);
381 }
382 /* }}} */
383 
384 /* {{{ proto object mysqli_link_construct()
385   */
386 PHP_FUNCTION(mysqli_link_construct)
387 {
388 	mysqli_common_connect(INTERNAL_FUNCTION_PARAM_PASSTHRU, FALSE, TRUE);
389 }
390 /* }}} */
391 
392 /* {{{ proto int mysqli_connect_errno(void)
393    Returns the numerical value of the error message from last connect command */
394 PHP_FUNCTION(mysqli_connect_errno)
395 {
396 	RETURN_LONG(MyG(error_no));
397 }
398 /* }}} */
399 
400 /* {{{ proto string mysqli_connect_error(void)
401    Returns the text of the error message from previous MySQL operation */
402 PHP_FUNCTION(mysqli_connect_error)
403 {
404 	if (MyG(error_msg)) {
405 		RETURN_STRING(MyG(error_msg));
406 	} else {
407 		RETURN_NULL();
408 	}
409 }
410 /* }}} */
411 
412 /* {{{ proto mixed mysqli_fetch_array(object result [,int resulttype])
413    Fetch a result row as an associative array, a numeric array, or both */
414 PHP_FUNCTION(mysqli_fetch_array)
415 {
416 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0, 0);
417 }
418 /* }}} */
419 
420 /* {{{ proto mixed mysqli_fetch_assoc(object result)
421    Fetch a result row as an associative array */
422 PHP_FUNCTION(mysqli_fetch_assoc)
423 {
424 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 0);
425 }
426 /* }}} */
427 
428 /* {{{ proto mixed mysqli_fetch_all(object result [,int resulttype])
429    Fetches all result rows as an associative array, a numeric array, or both */
430 #if defined(MYSQLI_USE_MYSQLND)
431 PHP_FUNCTION(mysqli_fetch_all)
432 {
433 	MYSQL_RES	*result;
434 	zval		*mysql_result;
435 	zend_long		mode = MYSQLND_FETCH_NUM;
436 
437 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|l", &mysql_result, mysqli_result_class_entry, &mode) == FAILURE) {
438 		return;
439 	}
440 	MYSQLI_FETCH_RESOURCE(result, MYSQL_RES *, mysql_result, "mysqli_result", MYSQLI_STATUS_VALID);
441 
442 	if (!mode || (mode & ~MYSQLND_FETCH_BOTH)) {
443 		php_error_docref(NULL, E_WARNING, "Mode can be only MYSQLI_FETCH_NUM, "
444 		                 "MYSQLI_FETCH_ASSOC or MYSQLI_FETCH_BOTH");
445 		RETURN_FALSE;
446 	}
447 
448 	mysqlnd_fetch_all(result, mode, return_value);
449 }
450 /* }}} */
451 
452 /* {{{ proto array mysqli_get_client_stats(void)
453    Returns statistics about the zval cache */
454 PHP_FUNCTION(mysqli_get_client_stats)
455 {
456 	if (zend_parse_parameters_none() == FAILURE) {
457 		return;
458 	}
459 	mysqlnd_get_client_stats(return_value);
460 }
461 /* }}} */
462 
463 /* {{{ proto array mysqli_get_connection_stats(void)
464    Returns statistics about the zval cache */
465 PHP_FUNCTION(mysqli_get_connection_stats)
466 {
467 	MY_MYSQL	*mysql;
468 	zval		*mysql_link;
469 
470 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O",
471 									 &mysql_link, mysqli_link_class_entry) == FAILURE) {
472 		return;
473 	}
474 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
475 
476 	mysqlnd_get_connection_stats(mysql->mysql, return_value);
477 }
478 #endif
479 /* }}} */
480 
481 /* {{{ proto mixed mysqli_error_list(object connection)
482    Fetches all client errors */
483 PHP_FUNCTION(mysqli_error_list)
484 {
485 	MY_MYSQL	*mysql;
486 	zval		*mysql_link;
487 
488 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
489 		return;
490 	}
491 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
492 #if defined(MYSQLI_USE_MYSQLND)
493 	if (1) {
494 		MYSQLND_ERROR_LIST_ELEMENT * message;
495 		zend_llist_position pos;
496 		array_init(return_value);
497 		for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&mysql->mysql->data->error_info->error_list, &pos);
498 			 message;
499 			 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&mysql->mysql->data->error_info->error_list, &pos))
500 		{
501 			zval single_error;
502 			array_init(&single_error);
503 			add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
504 			add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
505 			add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
506 			add_next_index_zval(return_value, &single_error);
507 		}
508 	} else {
509 		RETURN_EMPTY_ARRAY();
510 	}
511 #else
512 	if (mysql_errno(mysql->mysql)) {
513 		zval single_error;
514 		array_init(return_value);
515 		array_init(&single_error);
516 		add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_errno(mysql->mysql));
517 		add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_sqlstate(mysql->mysql));
518 		add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_error(mysql->mysql));
519 		add_next_index_zval(return_value, &single_error);
520 	} else {
521 		RETURN_EMPTY_ARRAY();
522 	}
523 #endif
524 }
525 /* }}} */
526 
527 /* {{{ proto string mysqli_stmt_error_list(object stmt)
528 */
529 PHP_FUNCTION(mysqli_stmt_error_list)
530 {
531 	MY_STMT	*stmt;
532 	zval 	*mysql_stmt;
533 
534 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
535 		return;
536 	}
537 	MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_INITIALIZED);
538 #if defined(MYSQLI_USE_MYSQLND)
539 	if (stmt->stmt && stmt->stmt->data && stmt->stmt->data->error_info) {
540 		MYSQLND_ERROR_LIST_ELEMENT * message;
541 		zend_llist_position pos;
542 		array_init(return_value);
543 		for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&stmt->stmt->data->error_info->error_list, &pos);
544 			 message;
545 			 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&stmt->stmt->data->error_info->error_list, &pos))
546 		{
547 			zval single_error;
548 			array_init(&single_error);
549 			add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
550 			add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
551 			add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
552 			add_next_index_zval(return_value, &single_error);
553 		}
554 	} else {
555 		RETURN_EMPTY_ARRAY();
556 	}
557 #else
558 	if (mysql_stmt_errno(stmt->stmt)) {
559 		zval single_error;
560 		array_init(return_value);
561 		array_init(&single_error);
562 		add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_stmt_errno(stmt->stmt));
563 		add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_stmt_sqlstate(stmt->stmt));
564 		add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_stmt_error(stmt->stmt));
565 		add_next_index_zval(return_value, &single_error);
566 	} else {
567 		RETURN_EMPTY_ARRAY();
568 	}
569 #endif
570 }
571 /* }}} */
572 
573 /* {{{ proto mixed mysqli_fetch_object(object result [, string class_name [, NULL|array ctor_params]])
574    Fetch a result row as an object */
575 PHP_FUNCTION(mysqli_fetch_object)
576 {
577 	php_mysqli_fetch_into_hash(INTERNAL_FUNCTION_PARAM_PASSTHRU, MYSQLI_ASSOC, 1);
578 }
579 /* }}} */
580 
581 /* {{{ proto bool mysqli_multi_query(object link, string query)
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;
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 /* {{{ proto mixed mysqli_query(object link, string query [,int resultmode]) */
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;
642 	}
643 
644 	if (!query_len) {
645 		php_error_docref(NULL, E_WARNING, "Empty query");
646 		RETURN_FALSE;
647 	}
648 #ifdef MYSQLI_USE_MYSQLND
649 	if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) != MYSQLI_STORE_RESULT) {
650 #else
651 	if ((resultmode & ~MYSQLI_ASYNC) != MYSQLI_USE_RESULT && (resultmode & ~MYSQLI_ASYNC) != MYSQLI_STORE_RESULT) {
652 #endif
653 		php_error_docref(NULL, E_WARNING, "Invalid value for resultmode");
654 		RETURN_FALSE;
655 	}
656 
657 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
658 
659 	MYSQLI_DISABLE_MQ;
660 
661 
662 #ifdef MYSQLI_USE_MYSQLND
663 	if (resultmode & MYSQLI_ASYNC) {
664 		if (mysqli_async_query(mysql->mysql, query, query_len)) {
665 			MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
666 			RETURN_FALSE;
667 		}
668 		mysql->async_result_fetch_type = resultmode & ~MYSQLI_ASYNC;
669 		RETURN_TRUE;
670 	}
671 #endif
672 
673 	if (mysql_real_query(mysql->mysql, query, query_len)) {
674 		MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
675 		RETURN_FALSE;
676 	}
677 
678 	if (!mysql_field_count(mysql->mysql)) {
679 		/* no result set - not a SELECT */
680 		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
681 			php_mysqli_report_index(query, mysqli_server_status(mysql->mysql));
682 		}
683 		RETURN_TRUE;
684 	}
685 
686 #ifdef MYSQLI_USE_MYSQLND
687 	switch (resultmode & ~(MYSQLI_ASYNC | MYSQLI_STORE_RESULT_COPY_DATA)) {
688 #else
689 	switch (resultmode & ~MYSQLI_ASYNC) {
690 #endif
691 		case MYSQLI_STORE_RESULT:
692 #ifdef MYSQLI_USE_MYSQLND
693 			if (resultmode & MYSQLI_STORE_RESULT_COPY_DATA) {
694 				result = mysqlnd_store_result_ofs(mysql->mysql);
695 			} else
696 #endif
697 				result = mysql_store_result(mysql->mysql);
698 			break;
699 		case MYSQLI_USE_RESULT:
700 			result = mysql_use_result(mysql->mysql);
701 			break;
702 	}
703 	if (!result) {
704 		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql),
705 										"%s", mysql_error(mysql->mysql));
706 		RETURN_FALSE;
707 	}
708 
709 	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
710 		php_mysqli_report_index(query, mysqli_server_status(mysql->mysql));
711 	}
712 
713 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
714 	mysqli_resource->ptr = (void *)result;
715 	mysqli_resource->status = MYSQLI_STATUS_VALID;
716 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
717 }
718 /* }}} */
719 
720 #if defined(MYSQLI_USE_MYSQLND)
721 #include "php_network.h"
722 /* {{{ mysqlnd_zval_array_to_mysqlnd_array functions */
723 static int mysqlnd_zval_array_to_mysqlnd_array(zval *in_array, MYSQLND ***out_array)
724 {
725 	zval *elem;
726 	int i = 0, current = 0;
727 
728 	if (Z_TYPE_P(in_array) != IS_ARRAY) {
729 		return 0;
730 	}
731 	*out_array = ecalloc(zend_hash_num_elements(Z_ARRVAL_P(in_array)) + 1, sizeof(MYSQLND *));
732 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(in_array), elem) {
733 		i++;
734 		if (Z_TYPE_P(elem) != IS_OBJECT ||
735 			!instanceof_function(Z_OBJCE_P(elem), mysqli_link_class_entry)) {
736 			php_error_docref(NULL, E_WARNING, "Parameter %d not a mysqli object", i);
737 		} else {
738 			MY_MYSQL *mysql;
739 			MYSQLI_RESOURCE *my_res;
740 			mysqli_object *intern = Z_MYSQLI_P(elem);
741 			if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
742 		  		php_error_docref(NULL, E_WARNING, "[%d] Couldn't fetch %s", i, ZSTR_VAL(intern->zo.ce->name));
743 				continue;
744 		  	}
745 			mysql = (MY_MYSQL*) my_res->ptr;
746 			if (MYSQLI_STATUS_VALID && my_res->status < MYSQLI_STATUS_VALID) {
747 				php_error_docref(NULL, E_WARNING, "Invalid object %d or resource %s", i, ZSTR_VAL(intern->zo.ce->name));
748 				continue;
749 			}
750 			(*out_array)[current++] = mysql->mysql;
751 		}
752 	} ZEND_HASH_FOREACH_END();
753 	return 0;
754 }
755 /* }}} */
756 
757 /* {{{ mysqlnd_zval_array_from_mysqlnd_array */
758 static int mysqlnd_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *out_array)
759 {
760 	MYSQLND **p = in_array;
761 	zval dest_array;
762 	zval *elem, *dest_elem;
763 	int ret = 0, i = 0;
764 
765 	array_init_size(&dest_array, zend_hash_num_elements(Z_ARRVAL_P(out_array)));
766 
767 	ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(out_array), elem) {
768 		i++;
769 		if (Z_TYPE_P(elem) != IS_OBJECT ||
770 				!instanceof_function(Z_OBJCE_P(elem), mysqli_link_class_entry)) {
771 			continue;
772 		}
773 		{
774 			MY_MYSQL *mysql;
775 			MYSQLI_RESOURCE *my_res;
776 			mysqli_object *intern = Z_MYSQLI_P(elem);
777 			if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
778 		  		php_error_docref(NULL, E_WARNING, "[%d] Couldn't fetch %s", i, ZSTR_VAL(intern->zo.ce->name));
779 				continue;
780 		  	}
781 			mysql = (MY_MYSQL *) my_res->ptr;
782 			if (mysql->mysql == *p) {
783 				dest_elem = zend_hash_next_index_insert(Z_ARRVAL(dest_array), elem);
784 				if (dest_elem) {
785 					zval_add_ref(dest_elem);
786 				}
787 				ret++;
788 				p++;
789 			}
790 		}
791 	} ZEND_HASH_FOREACH_END();
792 
793 	/* destroy old array and add new one */
794 	zval_ptr_dtor(out_array);
795 	ZVAL_COPY_VALUE(out_array, &dest_array);
796 
797 	return 0;
798 }
799 /* }}} */
800 
801 /* {{{ mysqlnd_dont_poll_zval_array_from_mysqlnd_array */
802 static int mysqlnd_dont_poll_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *in_zval_array, zval *out_array)
803 {
804 	MYSQLND **p = in_array;
805 	zval proxy, *elem, *dest_elem;
806 	int ret = 0;
807 
808 	array_init(&proxy);
809 	if (in_array) {
810 		ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(in_zval_array), elem) {
811 			MY_MYSQL *mysql;
812 			mysqli_object *intern = Z_MYSQLI_P(elem);
813 			mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)intern->ptr)->ptr;
814 			if (mysql->mysql == *p) {
815 				dest_elem = zend_hash_next_index_insert(Z_ARRVAL(proxy), elem);
816 				if (dest_elem) {
817 					zval_add_ref(dest_elem);
818 				}
819 				ret++;
820 				p++;
821 			}
822 		} ZEND_HASH_FOREACH_END();
823 	}
824 
825 	/* destroy old array and add new one */
826 	zval_ptr_dtor(out_array);
827 	ZVAL_COPY_VALUE(out_array, &proxy);
828 
829 	return 0;
830 }
831 /* }}} */
832 
833 /* {{{ proto int mysqli_poll(array read, array write, array error, int sec [, int usec]) U
834    Poll connections */
835 PHP_FUNCTION(mysqli_poll)
836 {
837 	zval			*r_array, *e_array, *dont_poll_array;
838 	MYSQLND			**new_r_array = NULL, **new_e_array = NULL, **new_dont_poll_array = NULL;
839 	zend_long			sec = 0, usec = 0;
840 	enum_func_status ret;
841 	int 			desc_num;
842 
843 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!al|l", &r_array, &e_array, &dont_poll_array, &sec, &usec) == FAILURE) {
844 		return;
845 	}
846 	if (sec < 0 || usec < 0) {
847 		php_error_docref(NULL, E_WARNING, "Negative values passed for sec and/or usec");
848 		RETURN_FALSE;
849 	}
850 
851 	if (!r_array && !e_array) {
852 		php_error_docref(NULL, E_WARNING, "No stream arrays were passed");
853 		RETURN_FALSE;
854 	}
855 
856 	if (r_array != NULL) {
857 		mysqlnd_zval_array_to_mysqlnd_array(r_array, &new_r_array);
858 	}
859 	if (e_array != NULL) {
860 		mysqlnd_zval_array_to_mysqlnd_array(e_array, &new_e_array);
861 	}
862 
863 	ret = mysqlnd_poll(new_r_array, new_e_array, &new_dont_poll_array, sec, usec, &desc_num);
864 
865 	mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array);
866 
867 	if (r_array != NULL) {
868 		mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array);
869 	}
870 	if (e_array != NULL) {
871 		mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array);
872 	}
873 
874 	if (new_dont_poll_array) {
875 		efree(new_dont_poll_array);
876 	}
877 	if (new_r_array) {
878 		efree(new_r_array);
879 	}
880 	if (new_e_array) {
881 		efree(new_e_array);
882 	}
883 	if (ret == PASS) {
884 		RETURN_LONG(desc_num);
885 	} else {
886 		RETURN_FALSE;
887 	}
888 }
889 /* }}} */
890 
891 /* {{{ proto int mysqli_reap_async_query(object link) U
892    Poll connections */
893 PHP_FUNCTION(mysqli_reap_async_query)
894 {
895 	MY_MYSQL		*mysql;
896 	zval			*mysql_link;
897 	MYSQLI_RESOURCE		*mysqli_resource;
898 	MYSQL_RES 			*result = NULL;
899 
900 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
901 		return;
902 	}
903 
904 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
905 
906 	if (FAIL == mysqlnd_reap_async_query(mysql->mysql)) {
907 		RETURN_FALSE;
908 	}
909 
910 	if (!mysql_field_count(mysql->mysql)) {
911 		/* no result set - not a SELECT */
912 		if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
913 /*			php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
914 		}
915 		RETURN_TRUE;
916 	}
917 
918 	switch (mysql->async_result_fetch_type) {
919 		case MYSQLI_STORE_RESULT:
920 			result = mysql_store_result(mysql->mysql);
921 			break;
922 		case MYSQLI_USE_RESULT:
923 			result = mysql_use_result(mysql->mysql);
924 			break;
925 	}
926 
927 	if (!result) {
928 		php_mysqli_throw_sql_exception((char *)mysql_sqlstate(mysql->mysql), mysql_errno(mysql->mysql),
929 										"%s", mysql_error(mysql->mysql));
930 		RETURN_FALSE;
931 	}
932 
933 	if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
934 /*		php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
935 	}
936 
937 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
938 	mysqli_resource->ptr = (void *)result;
939 	mysqli_resource->status = MYSQLI_STATUS_VALID;
940 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
941 }
942 /* }}} */
943 
944 /* {{{ proto object mysqli_stmt_get_result(object link) U
945    Buffer result set on client */
946 PHP_FUNCTION(mysqli_stmt_get_result)
947 {
948 	MYSQL_RES 		*result;
949 	MYSQLI_RESOURCE	*mysqli_resource;
950 	MY_STMT			*stmt;
951 	zval 			*mysql_stmt;
952 
953 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
954 		return;
955 	}
956 	MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
957 
958 	if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) {
959 		MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
960 		RETURN_FALSE;
961 	}
962 
963 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
964 	mysqli_resource->ptr = (void *)result;
965 	mysqli_resource->status = MYSQLI_STATUS_VALID;
966 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_result_class_entry);
967 }
968 /* }}} */
969 #endif
970 
971 /* {{{ proto object mysqli_get_warnings(object link) */
972 PHP_FUNCTION(mysqli_get_warnings)
973 {
974 	MY_MYSQL			*mysql;
975 	zval				*mysql_link;
976 	MYSQLI_RESOURCE		*mysqli_resource;
977 	MYSQLI_WARNING		*w = NULL;
978 
979 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
980 		return;
981 	}
982 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
983 
984 	if (mysql_warning_count(mysql->mysql)) {
985 #ifdef MYSQLI_USE_MYSQLND
986 		w = php_get_warnings(mysql->mysql->data);
987 #else
988 		w = php_get_warnings(mysql->mysql);
989 #endif
990 	}
991 	if (!w) {
992 		RETURN_FALSE;
993 	}
994 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
995 	mysqli_resource->ptr = mysqli_resource->info = (void *)w;
996 	mysqli_resource->status = MYSQLI_STATUS_VALID;
997 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
998 }
999 /* }}} */
1000 
1001 /* {{{ proto object mysqli_stmt_get_warnings(object link) */
1002 PHP_FUNCTION(mysqli_stmt_get_warnings)
1003 {
1004 	MY_STMT				*stmt;
1005 	zval				*stmt_link;
1006 	MYSQLI_RESOURCE		*mysqli_resource;
1007 	MYSQLI_WARNING		*w = NULL;
1008 
1009 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) {
1010 		return;
1011 	}
1012 	MYSQLI_FETCH_RESOURCE_STMT(stmt, stmt_link, MYSQLI_STATUS_VALID);
1013 
1014 	if (mysqli_stmt_warning_count(stmt->stmt)) {
1015 		w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt));
1016 	}
1017 	if (!w) {
1018 		RETURN_FALSE;
1019 	}
1020 	mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
1021 	mysqli_resource->ptr = mysqli_resource->info = (void *)w;
1022 	mysqli_resource->status = MYSQLI_STATUS_VALID;
1023 	MYSQLI_RETURN_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
1024 }
1025 /* }}} */
1026 
1027 #ifdef HAVE_MYSQLI_SET_CHARSET
1028 /* {{{ proto bool mysqli_set_charset(object link, string csname)
1029    sets client character set */
1030 PHP_FUNCTION(mysqli_set_charset)
1031 {
1032 	MY_MYSQL	*mysql;
1033 	zval		*mysql_link;
1034 	char		*cs_name;
1035 	size_t		csname_len;
1036 
1037 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &csname_len) == FAILURE) {
1038 		return;
1039 	}
1040 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1041 
1042 	if (mysql_set_character_set(mysql->mysql, cs_name)) {
1043 		RETURN_FALSE;
1044 	}
1045 	RETURN_TRUE;
1046 }
1047 /* }}} */
1048 #endif
1049 
1050 #ifdef HAVE_MYSQLI_GET_CHARSET
1051 /* {{{ proto object mysqli_get_charset(object link) U
1052    returns a character set object */
1053 PHP_FUNCTION(mysqli_get_charset)
1054 {
1055 	MY_MYSQL				*mysql;
1056 	zval					*mysql_link;
1057 	const char 				*name = NULL, *collation = NULL, *dir = NULL, *comment = NULL;
1058 	uint32_t				minlength, maxlength, number, state;
1059 #if !defined(MYSQLI_USE_MYSQLND)
1060 	MY_CHARSET_INFO			cs;
1061 #else
1062 	const MYSQLND_CHARSET	*cs;
1063 #endif
1064 
1065 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
1066 		return;
1067 	}
1068 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1069 
1070 
1071 #if !defined(MYSQLI_USE_MYSQLND)
1072 	mysql_get_character_set_info(mysql->mysql, &cs);
1073 	name = (char *)cs.csname;
1074 	collation = (char *)cs.name;
1075 	dir = (char *)cs.dir;
1076 	minlength = cs.mbminlen;
1077 	maxlength = cs.mbmaxlen;
1078 	number = cs.number;
1079 	state = cs.state;
1080 	comment = cs.comment;
1081 #else
1082 	cs = mysql->mysql->data->charset;
1083 	if (!cs) {
1084 		php_error_docref(NULL, E_WARNING, "The connection has no charset associated");
1085 		RETURN_NULL();
1086 	}
1087 	name = cs->name;
1088 	collation = cs->collation;
1089 	minlength = cs->char_minlen;
1090 	maxlength = cs->char_maxlen;
1091 	number = cs->nr;
1092 	comment = cs->comment;
1093 	state = 1;	/* all charsets are compiled in */
1094 #endif
1095 	object_init(return_value);
1096 
1097 	add_property_string(return_value, "charset", (name) ? (char *)name : "");
1098 	add_property_string(return_value, "collation",(collation) ? (char *)collation : "");
1099 	add_property_string(return_value, "dir", (dir) ? (char *)dir : "");
1100 	add_property_long(return_value, "min_length", minlength);
1101 	add_property_long(return_value, "max_length", maxlength);
1102 	add_property_long(return_value, "number", number);
1103 	add_property_long(return_value, "state", state);
1104 	add_property_string(return_value, "comment", (comment) ? (char *)comment : "");
1105 }
1106 /* }}} */
1107 #endif
1108 
1109 #if !defined(MYSQLI_USE_MYSQLND)
1110 extern char * mysqli_escape_string_for_tx_name_in_comment(const char * const name);
1111 
1112 /* {{{ proto bool mysqli_begin_transaction_libmysql */
1113 static int mysqli_begin_transaction_libmysql(MYSQL * conn, const unsigned int mode, const char * const name)
1114 {
1115 	int ret;
1116 	smart_str tmp_str = {0};
1117 	char * name_esc;
1118 	char * query;
1119 	unsigned int query_len;
1120 	if (mode & TRANS_START_WITH_CONSISTENT_SNAPSHOT) {
1121 		if (tmp_str.s) {
1122 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1123 		}
1124 		smart_str_appendl(&tmp_str, "WITH CONSISTENT SNAPSHOT", sizeof("WITH CONSISTENT SNAPSHOT") - 1);
1125 	}
1126 	if (mode & TRANS_START_READ_WRITE) {
1127 		if (tmp_str.s) {
1128 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1129 		}
1130 		smart_str_appendl(&tmp_str, "READ WRITE", sizeof("READ WRITE") - 1);
1131 	} else if (mode & TRANS_START_READ_ONLY) {
1132 		if (tmp_str.s) {
1133 			smart_str_appendl(&tmp_str, ", ", sizeof(", ") - 1);
1134 		}
1135 		smart_str_appendl(&tmp_str, "READ ONLY", sizeof("READ ONLY") - 1);
1136 	}
1137 	smart_str_0(&tmp_str);
1138 
1139 	name_esc = mysqli_escape_string_for_tx_name_in_comment(name);
1140 	query_len = spprintf(&query, 0, "START TRANSACTION%s %s",
1141 						 name_esc? name_esc:"", tmp_str.s? ZSTR_VAL(tmp_str.s):"");
1142 
1143 	smart_str_free(&tmp_str);
1144 	if (name_esc) {
1145 		efree(name_esc);
1146 	}
1147 
1148 	ret = mysql_real_query(conn, query, query_len);
1149 	efree(query);
1150 
1151 	if (ret && mode & (TRANS_START_READ_WRITE | TRANS_START_READ_ONLY) && mysql_errno(conn) == 1064) {
1152 		php_error_docref(NULL, E_WARNING, "This server version doesn't support 'READ WRITE' and 'READ ONLY'. Minimum 5.6.5 is required");
1153 	}
1154 	return ret;
1155 }
1156 /* }}} */
1157 #endif
1158 
1159 /* {{{ proto bool mysqli_begin_transaction(object link, [int flags [, string name]])
1160    Starts a transaction */
1161 PHP_FUNCTION(mysqli_begin_transaction)
1162 {
1163 	MY_MYSQL	*mysql;
1164 	zval		*mysql_link;
1165 	zend_long		flags = TRANS_START_NO_OPT;
1166 	char *		name = NULL;
1167 	size_t			name_len = -1;
1168 	zend_bool	err = FALSE;
1169 
1170 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ls", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) {
1171 		return;
1172 	}
1173 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1174 	if (flags < 0) {
1175 		php_error_docref(NULL, E_WARNING, "Invalid value for parameter flags (" ZEND_LONG_FMT ")", flags);
1176 		err = TRUE;
1177 	}
1178 	if (!name_len) {
1179 		php_error_docref(NULL, E_WARNING, "Savepoint name cannot be empty");
1180 		err = TRUE;
1181 	}
1182 	if (TRUE == err) {
1183 		RETURN_FALSE;
1184 	}
1185 
1186 #if !defined(MYSQLI_USE_MYSQLND)
1187 	if (mysqli_begin_transaction_libmysql(mysql->mysql, flags, name)) {
1188 		RETURN_FALSE;
1189 	}
1190 #else
1191 	if (FAIL == mysqlnd_begin_transaction(mysql->mysql, flags, name)) {
1192 		RETURN_FALSE;
1193 	}
1194 #endif
1195 	RETURN_TRUE;
1196 }
1197 /* }}} */
1198 
1199 #if !defined(MYSQLI_USE_MYSQLND)
1200 /* {{{ proto bool mysqli_savepoint_libmysql */
1201 static int mysqli_savepoint_libmysql(MYSQL * conn, const char * const name, zend_bool release)
1202 {
1203 	int ret;
1204 	char * query;
1205 	unsigned int query_len = spprintf(&query, 0, release? "RELEASE SAVEPOINT `%s`":"SAVEPOINT `%s`", name);
1206 	ret = mysql_real_query(conn, query, query_len);
1207 	efree(query);
1208 	return ret;
1209 }
1210 /* }}} */
1211 #endif
1212 
1213 /* {{{ proto bool mysqli_savepoint(object link, string name)
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 = -1;
1221 
1222 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1223 		return;
1224 	}
1225 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1226 	if (!name || !name_len) {
1227 		php_error_docref(NULL, E_WARNING, "Savepoint name cannot be empty");
1228 		RETURN_FALSE;
1229 	}
1230 
1231 #if !defined(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 /* {{{ proto bool mysqli_release_savepoint(object link, string name)
1243    Starts a transaction */
1244 PHP_FUNCTION(mysqli_release_savepoint)
1245 {
1246 	MY_MYSQL	*mysql;
1247 	zval		*mysql_link;
1248 	char *		name = NULL;
1249 	size_t			name_len = -1;
1250 
1251 	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1252 		return;
1253 	}
1254 	MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1255 	if (!name || !name_len) {
1256 		php_error_docref(NULL, E_WARNING, "Savepoint name cannot be empty");
1257 		RETURN_FALSE;
1258 	}
1259 #if !defined(MYSQLI_USE_MYSQLND)
1260 	if (mysqli_savepoint_libmysql(mysql->mysql, name, TRUE)) {
1261 #else
1262 	if (FAIL == mysqlnd_release_savepoint(mysql->mysql, name)) {
1263 #endif
1264 		RETURN_FALSE;
1265 	}
1266 	RETURN_TRUE;
1267 }
1268 /* }}} */
1269 
1270 /* {{{ proto bool mysqli_get_links_stats()
1271    Returns information about open and cached links */
1272 PHP_FUNCTION(mysqli_get_links_stats)
1273 {
1274 	if (ZEND_NUM_ARGS()) {
1275 		php_error_docref(NULL, E_WARNING, "no parameters expected");
1276 		return;
1277 	}
1278 	array_init(return_value);
1279 	add_assoc_long_ex(return_value, "total", sizeof("total") - 1, MyG(num_links));
1280 	add_assoc_long_ex(return_value, "active_plinks", sizeof("active_plinks") - 1, MyG(num_active_persistent));
1281 	add_assoc_long_ex(return_value, "cached_plinks", sizeof("cached_plinks") - 1, MyG(num_inactive_persistent));
1282 }
1283 /* }}} */
1284