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