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_type_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 int 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 int ret = 0;
682
683 array_init_size(&dest_array, zend_hash_num_elements(Z_ARRVAL_P(out_array)));
684
685 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(out_array), elem) {
686 if (Z_TYPE_P(elem) != IS_OBJECT ||
687 !instanceof_function(Z_OBJCE_P(elem), mysqli_link_class_entry)) {
688 continue;
689 }
690 {
691 MY_MYSQL *mysql;
692 MYSQLI_RESOURCE *my_res;
693 mysqli_object *intern = Z_MYSQLI_P(elem);
694 if (!(my_res = (MYSQLI_RESOURCE *)intern->ptr)) {
695 zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(intern->zo.ce->name));
696 return FAILURE;
697 }
698 mysql = (MY_MYSQL *) my_res->ptr;
699 if (mysql->mysql == *p) {
700 dest_elem = zend_hash_next_index_insert(Z_ARRVAL(dest_array), elem);
701 if (dest_elem) {
702 zval_add_ref(dest_elem);
703 }
704 ret++;
705 p++;
706 }
707 }
708 } ZEND_HASH_FOREACH_END();
709
710 /* destroy old array and add new one */
711 zval_ptr_dtor(out_array);
712 ZVAL_COPY_VALUE(out_array, &dest_array);
713
714 return 0;
715 }
716 /* }}} */
717
718 /* {{{ mysqlnd_dont_poll_zval_array_from_mysqlnd_array */
719 static int mysqlnd_dont_poll_zval_array_from_mysqlnd_array(MYSQLND **in_array, zval *in_zval_array, zval *out_array)
720 {
721 MYSQLND **p = in_array;
722 zval proxy, *elem, *dest_elem;
723 int ret = 0;
724
725 array_init(&proxy);
726 if (in_array) {
727 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(in_zval_array), elem) {
728 MY_MYSQL *mysql;
729 mysqli_object *intern = Z_MYSQLI_P(elem);
730 mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)intern->ptr)->ptr;
731 if (mysql->mysql == *p) {
732 dest_elem = zend_hash_next_index_insert(Z_ARRVAL(proxy), elem);
733 if (dest_elem) {
734 zval_add_ref(dest_elem);
735 }
736 ret++;
737 p++;
738 }
739 } ZEND_HASH_FOREACH_END();
740 }
741
742 /* destroy old array and add new one */
743 zval_ptr_dtor(out_array);
744 ZVAL_COPY_VALUE(out_array, &proxy);
745
746 return 0;
747 }
748 /* }}} */
749
750 /* {{{ Poll connections */
751 PHP_FUNCTION(mysqli_poll)
752 {
753 zval *r_array, *e_array, *dont_poll_array;
754 MYSQLND **new_r_array = NULL, **new_e_array = NULL, **new_dont_poll_array = NULL;
755 zend_long sec = 0, usec = 0;
756 enum_func_status ret;
757 int desc_num;
758
759 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a!a!al|l", &r_array, &e_array, &dont_poll_array, &sec, &usec) == FAILURE) {
760 RETURN_THROWS();
761 }
762 if (sec < 0) {
763 zend_argument_value_error(4, "must be greater than or equal to 0");
764 RETURN_THROWS();
765 }
766 if (usec < 0) {
767 zend_argument_value_error(5, "must be greater than or equal to 0");
768 RETURN_THROWS();
769 }
770
771 // TODO Error promotion
772 if (!r_array && !e_array) {
773 php_error_docref(NULL, E_WARNING, "No stream arrays were passed");
774 RETURN_FALSE;
775 }
776
777 if (r_array != NULL) {
778 if (mysqlnd_zval_array_to_mysqlnd_array(r_array, &new_r_array) == FAILURE) {
779 efree(new_r_array);
780 RETURN_THROWS();
781 }
782 }
783 if (e_array != NULL) {
784 if (mysqlnd_zval_array_to_mysqlnd_array(e_array, &new_e_array) == FAILURE) {
785 efree(new_e_array);
786 efree(new_r_array);
787 RETURN_THROWS();
788 }
789 }
790
791 ret = mysqlnd_poll(new_r_array, new_e_array, &new_dont_poll_array, sec, usec, &desc_num);
792
793 mysqlnd_dont_poll_zval_array_from_mysqlnd_array(r_array != NULL ? new_dont_poll_array:NULL, r_array, dont_poll_array);
794
795 if (r_array != NULL) {
796 mysqlnd_zval_array_from_mysqlnd_array(new_r_array, r_array);
797 }
798 if (e_array != NULL) {
799 mysqlnd_zval_array_from_mysqlnd_array(new_e_array, e_array);
800 }
801
802 if (new_dont_poll_array) {
803 efree(new_dont_poll_array);
804 }
805 if (new_r_array) {
806 efree(new_r_array);
807 }
808 if (new_e_array) {
809 efree(new_e_array);
810 }
811 if (ret == PASS) {
812 RETURN_LONG(desc_num);
813 } else {
814 RETURN_FALSE;
815 }
816 }
817 /* }}} */
818
819 /* {{{ Poll connections */
820 PHP_FUNCTION(mysqli_reap_async_query)
821 {
822 MY_MYSQL *mysql;
823 zval *mysql_link;
824 MYSQLI_RESOURCE *mysqli_resource;
825 MYSQL_RES *result = NULL;
826
827 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
828 RETURN_THROWS();
829 }
830
831 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
832
833 if (FAIL == mysqlnd_reap_async_query(mysql->mysql)) {
834 MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
835 RETURN_FALSE;
836 }
837
838 if (!mysql_field_count(mysql->mysql)) {
839 /* no result set - not a SELECT */
840 if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
841 /* php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
842 }
843 RETURN_TRUE;
844 }
845
846 switch (mysql->async_result_fetch_type) {
847 case MYSQLI_STORE_RESULT:
848 result = mysql_store_result(mysql->mysql);
849 break;
850 case MYSQLI_USE_RESULT:
851 result = mysql_use_result(mysql->mysql);
852 break;
853 }
854
855 if (!result) {
856 MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
857 RETURN_FALSE;
858 }
859
860 if (MyG(report_mode) & MYSQLI_REPORT_INDEX) {
861 /* php_mysqli_report_index("n/a", mysqli_server_status(mysql->mysql)); */
862 }
863
864 mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
865 mysqli_resource->ptr = (void *)result;
866 mysqli_resource->status = MYSQLI_STATUS_VALID;
867 MYSQLI_RETVAL_RESOURCE(mysqli_resource, mysqli_result_class_entry);
868 }
869 /* }}} */
870
871 /* {{{ Buffer result set on client */
872 PHP_FUNCTION(mysqli_stmt_get_result)
873 {
874 MYSQL_RES *result;
875 MYSQLI_RESOURCE *mysqli_resource;
876 MY_STMT *stmt;
877 zval *mysql_stmt;
878
879 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_stmt, mysqli_stmt_class_entry) == FAILURE) {
880 RETURN_THROWS();
881 }
882 MYSQLI_FETCH_RESOURCE_STMT(stmt, mysql_stmt, MYSQLI_STATUS_VALID);
883
884 if (!(result = mysqlnd_stmt_get_result(stmt->stmt))) {
885 MYSQLI_REPORT_STMT_ERROR(stmt->stmt);
886 RETURN_FALSE;
887 }
888
889 mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
890 mysqli_resource->ptr = (void *)result;
891 mysqli_resource->status = MYSQLI_STATUS_VALID;
892 MYSQLI_RETVAL_RESOURCE(mysqli_resource, mysqli_result_class_entry);
893 }
894 /* }}} */
895
896 /* {{{ */
897 PHP_FUNCTION(mysqli_get_warnings)
898 {
899 MY_MYSQL *mysql;
900 zval *mysql_link;
901 MYSQLI_RESOURCE *mysqli_resource;
902 MYSQLI_WARNING *w = NULL;
903
904 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
905 RETURN_THROWS();
906 }
907 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
908
909 if (mysql_warning_count(mysql->mysql)) {
910 w = php_get_warnings(mysql->mysql->data);
911 }
912 if (!w) {
913 RETURN_FALSE;
914 }
915 mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
916 mysqli_resource->ptr = mysqli_resource->info = (void *)w;
917 mysqli_resource->status = MYSQLI_STATUS_VALID;
918 MYSQLI_RETVAL_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
919 }
920 /* }}} */
921
922 /* {{{ */
923 PHP_FUNCTION(mysqli_stmt_get_warnings)
924 {
925 MY_STMT *stmt;
926 zval *stmt_link;
927 MYSQLI_RESOURCE *mysqli_resource;
928 MYSQLI_WARNING *w = NULL;
929
930 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &stmt_link, mysqli_stmt_class_entry) == FAILURE) {
931 RETURN_THROWS();
932 }
933 MYSQLI_FETCH_RESOURCE_STMT(stmt, stmt_link, MYSQLI_STATUS_VALID);
934
935 if (mysqli_stmt_warning_count(stmt->stmt)) {
936 w = php_get_warnings(mysqli_stmt_get_connection(stmt->stmt));
937 }
938 if (!w) {
939 RETURN_FALSE;
940 }
941 mysqli_resource = (MYSQLI_RESOURCE *)ecalloc (1, sizeof(MYSQLI_RESOURCE));
942 mysqli_resource->ptr = mysqli_resource->info = (void *)w;
943 mysqli_resource->status = MYSQLI_STATUS_VALID;
944 MYSQLI_RETVAL_RESOURCE(mysqli_resource, mysqli_warning_class_entry);
945 }
946 /* }}} */
947
948 /* {{{ sets client character set */
949 PHP_FUNCTION(mysqli_set_charset)
950 {
951 MY_MYSQL *mysql;
952 zval *mysql_link;
953 char *cs_name;
954 size_t csname_len;
955
956 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &csname_len) == FAILURE) {
957 RETURN_THROWS();
958 }
959 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
960
961 if (mysql_set_character_set(mysql->mysql, cs_name)) {
962 MYSQLI_REPORT_MYSQL_ERROR(mysql->mysql);
963 RETURN_FALSE;
964 }
965 RETURN_TRUE;
966 }
967 /* }}} */
968
969 /* {{{ returns a character set object */
970 PHP_FUNCTION(mysqli_get_charset)
971 {
972 MY_MYSQL *mysql;
973 zval *mysql_link;
974 const char *name = NULL, *collation = NULL, *dir = NULL, *comment = NULL;
975 uint32_t minlength, maxlength, number, state;
976 const MYSQLND_CHARSET *cs;
977
978 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O", &mysql_link, mysqli_link_class_entry) == FAILURE) {
979 RETURN_THROWS();
980 }
981 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
982
983
984 cs = mysql->mysql->data->charset;
985 if (!cs) {
986 php_error_docref(NULL, E_WARNING, "The connection has no charset associated");
987 RETURN_NULL();
988 }
989 name = cs->name;
990 collation = cs->collation;
991 minlength = cs->char_minlen;
992 maxlength = cs->char_maxlen;
993 number = cs->nr;
994 comment = cs->comment;
995 state = 1; /* all charsets are compiled in */
996 object_init(return_value);
997
998 add_property_string(return_value, "charset", (name) ? (char *)name : "");
999 add_property_string(return_value, "collation",(collation) ? (char *)collation : "");
1000 add_property_string(return_value, "dir", (dir) ? (char *)dir : "");
1001 add_property_long(return_value, "min_length", minlength);
1002 add_property_long(return_value, "max_length", maxlength);
1003 add_property_long(return_value, "number", number);
1004 add_property_long(return_value, "state", state);
1005 add_property_string(return_value, "comment", (comment) ? (char *)comment : "");
1006 }
1007 /* }}} */
1008
1009 /* {{{ Starts a transaction */
1010 PHP_FUNCTION(mysqli_begin_transaction)
1011 {
1012 MY_MYSQL *mysql;
1013 zval *mysql_link;
1014 zend_long flags = TRANS_START_NO_OPT;
1015 char * name = NULL;
1016 size_t name_len = 0;
1017
1018 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "O|ls!", &mysql_link, mysqli_link_class_entry, &flags, &name, &name_len) == FAILURE) {
1019 RETURN_THROWS();
1020 }
1021 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1022 if (flags < 0) {
1023 zend_argument_value_error(ERROR_ARG_POS(2), "must be one of the MYSQLI_TRANS_* constants");
1024 RETURN_THROWS();
1025 }
1026 if (name && !name_len) {
1027 zend_argument_value_error(ERROR_ARG_POS(3), "cannot be empty");
1028 RETURN_THROWS();
1029 }
1030
1031 if (FAIL == mysqlnd_begin_transaction(mysql->mysql, flags, name)) {
1032 RETURN_FALSE;
1033 }
1034 RETURN_TRUE;
1035 }
1036 /* }}} */
1037
1038 /* {{{ Starts a transaction */
1039 PHP_FUNCTION(mysqli_savepoint)
1040 {
1041 MY_MYSQL *mysql;
1042 zval *mysql_link;
1043 char * name = NULL;
1044 size_t name_len;
1045
1046 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1047 RETURN_THROWS();
1048 }
1049 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1050 if (name_len == 0) {
1051 zend_argument_value_error(ERROR_ARG_POS(2), "cannot be empty");
1052 RETURN_THROWS();
1053 }
1054
1055 if (FAIL == mysqlnd_savepoint(mysql->mysql, name)) {
1056 RETURN_FALSE;
1057 }
1058 RETURN_TRUE;
1059 }
1060 /* }}} */
1061
1062 /* {{{ Starts a transaction */
1063 PHP_FUNCTION(mysqli_release_savepoint)
1064 {
1065 MY_MYSQL *mysql;
1066 zval *mysql_link;
1067 char * name = NULL;
1068 size_t name_len;
1069
1070 if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Os", &mysql_link, mysqli_link_class_entry, &name, &name_len) == FAILURE) {
1071 RETURN_THROWS();
1072 }
1073 MYSQLI_FETCH_RESOURCE_CONN(mysql, mysql_link, MYSQLI_STATUS_VALID);
1074 if (name_len == 0) {
1075 zend_argument_value_error(ERROR_ARG_POS(2), "cannot be empty");
1076 RETURN_THROWS();
1077 }
1078 if (FAIL == mysqlnd_release_savepoint(mysql->mysql, name)) {
1079 RETURN_FALSE;
1080 }
1081 RETURN_TRUE;
1082 }
1083 /* }}} */
1084
1085 /* {{{ Returns information about open and cached links */
1086 PHP_FUNCTION(mysqli_get_links_stats)
1087 {
1088 if (zend_parse_parameters_none() == FAILURE) {
1089 RETURN_THROWS();
1090 }
1091
1092 array_init(return_value);
1093 add_assoc_long_ex(return_value, "total", sizeof("total") - 1, MyG(num_links));
1094 add_assoc_long_ex(return_value, "active_plinks", sizeof("active_plinks") - 1, MyG(num_active_persistent));
1095 add_assoc_long_ex(return_value, "cached_plinks", sizeof("cached_plinks") - 1, MyG(num_inactive_persistent));
1096 }
1097 /* }}} */
1098