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 | Author: Georg Richter <georg@php.net> |
14 | Andrey Hristov <andrey@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include <signal.h>
23
24 #include "php.h"
25 #include "php_ini.h"
26 #include "ext/standard/info.h"
27 #include "php_mysqli_structs.h"
28 #include "mysqli_priv.h"
29
30 #define CHECK_STATUS(value, quiet) \
31 if (!obj->ptr || ((MYSQLI_RESOURCE *)obj->ptr)->status < value ) { \
32 if (!quiet) { \
33 zend_throw_error(NULL, "Property access is not allowed yet"); \
34 } \
35 return FAILURE; \
36 } \
37
38 #define MYSQLI_GET_MYSQL(statusval) \
39 MYSQL *p; \
40 if (!obj->ptr || !(MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr) { \
41 if (!quiet) { \
42 zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(obj->zo.ce->name)); \
43 } \
44 return FAILURE; \
45 } else { \
46 CHECK_STATUS(statusval, quiet);\
47 p = (MYSQL *)((MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr)->mysql;\
48 }
49
50 #define MYSQLI_GET_RESULT(statusval) \
51 MYSQL_RES *p; \
52 if (!obj->ptr) { \
53 if (!quiet) { \
54 zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(obj->zo.ce->name)); \
55 } \
56 return FAILURE; \
57 } else { \
58 CHECK_STATUS(statusval, quiet);\
59 p = (MYSQL_RES *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr; \
60 }
61
62 #define MYSQLI_GET_STMT(statusval) \
63 MYSQL_STMT *p; \
64 if (!obj->ptr) { \
65 if (!quiet) { \
66 zend_throw_error(NULL, "%s object is already closed", ZSTR_VAL(obj->zo.ce->name)); \
67 } \
68 return FAILURE; \
69 } else { \
70 CHECK_STATUS(statusval, quiet); \
71 p = (MYSQL_STMT *)((MY_STMT *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr)->stmt; \
72 }
73
74 #define MYSQLI_MAP_PROPERTY_FUNC_LONG_OR_STR( __func, __int_func, __get_type, __ret_type, __ret_type_sprint_mod)\
75 static int __func(mysqli_object *obj, zval *retval, bool quiet) \
76 {\
77 __ret_type l;\
78 __get_type;\
79 ZEND_ASSERT(p);\
80 l = (__ret_type)__int_func(p);\
81 if (l < ZEND_LONG_MAX) {\
82 ZVAL_LONG(retval, (zend_long) l);\
83 } else { \
84 ZVAL_NEW_STR(retval, strpprintf(0, __ret_type_sprint_mod, l)); \
85 } \
86 return SUCCESS; \
87 }
88
89 #define MYSQLI_MAP_PROPERTY_FUNC_LONG( __func, __int_func, __get_type, __ret_type, __ret_type_sprint_mod)\
90 static int __func(mysqli_object *obj, zval *retval, bool quiet) \
91 {\
92 __ret_type l;\
93 __get_type;\
94 ZEND_ASSERT(p);\
95 l = (__ret_type)__int_func(p);\
96 ZEND_ASSERT(l < ZEND_LONG_MAX);\
97 ZVAL_LONG(retval, (zend_long) l);\
98 return SUCCESS; \
99 }
100
101 #define MYSQLI_MAP_PROPERTY_FUNC_STR_OR_NULL(__func, __int_func, __get_type)\
102 static int __func(mysqli_object *obj, zval *retval, bool quiet)\
103 {\
104 char *c;\
105 __get_type;\
106 ZEND_ASSERT(p);\
107 c = (char *)__int_func(p);\
108 if (c) {\
109 ZVAL_STRING(retval, c);\
110 } else {\
111 ZVAL_NULL(retval);\
112 }\
113 return SUCCESS; \
114 }
115
116 #define MYSQLI_MAP_PROPERTY_FUNC_STR(__func, __int_func, __get_type)\
117 static int __func(mysqli_object *obj, zval *retval, bool quiet)\
118 {\
119 char *c;\
120 __get_type;\
121 ZEND_ASSERT(p);\
122 c = (char *)__int_func(p);\
123 ZEND_ASSERT(c);\
124 ZVAL_STRING(retval, c);\
125 return SUCCESS; \
126 }
127
128 /* {{{ property link_client_version_read */
link_client_version_read(mysqli_object * obj,zval * retval,bool quiet)129 static int link_client_version_read(mysqli_object *obj, zval *retval, bool quiet)
130 {
131 ZVAL_LONG(retval, MYSQL_VERSION_ID);
132
133 return SUCCESS;
134 }
135 /* }}} */
136
137 /* {{{ property link_client_info_read */
link_client_info_read(mysqli_object * obj,zval * retval,bool quiet)138 static int link_client_info_read(mysqli_object *obj, zval *retval, bool quiet)
139 {
140 ZVAL_STRING(retval, MYSQL_SERVER_VERSION);
141
142 return SUCCESS;
143 }
144 /* }}} */
145
146 /* {{{ property link_connect_errno_read */
link_connect_errno_read(mysqli_object * obj,zval * retval,bool quiet)147 static int link_connect_errno_read(mysqli_object *obj, zval *retval, bool quiet)
148 {
149 ZVAL_LONG(retval, (zend_long)MyG(error_no));
150
151 return SUCCESS;
152 }
153 /* }}} */
154
155 /* {{{ property link_connect_error_read */
link_connect_error_read(mysqli_object * obj,zval * retval,bool quiet)156 static int link_connect_error_read(mysqli_object *obj, zval *retval, bool quiet)
157 {
158 if (MyG(error_msg)) {
159 ZVAL_STRING(retval, MyG(error_msg));
160 } else {
161 ZVAL_NULL(retval);
162 }
163
164 return SUCCESS;
165 }
166 /* }}} */
167
168 /* {{{ property link_affected_rows_read */
link_affected_rows_read(mysqli_object * obj,zval * retval,bool quiet)169 static int link_affected_rows_read(mysqli_object *obj, zval *retval, bool quiet)
170 {
171 MY_MYSQL *mysql;
172 my_ulonglong rc;
173
174 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
175
176 mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
177 ZEND_ASSERT(mysql);
178
179 rc = mysql_affected_rows(mysql->mysql);
180
181 if (rc == (my_ulonglong) -1) {
182 ZVAL_LONG(retval, -1);
183 return SUCCESS;
184 }
185
186 if (rc < ZEND_LONG_MAX) {
187 ZVAL_LONG(retval, (zend_long) rc);
188 } else {
189 ZVAL_NEW_STR(retval, strpprintf(0, MYSQLI_LLU_SPEC, rc));
190 }
191
192 return SUCCESS;
193 }
194 /* }}} */
195
196 /* {{{ property link_error_list_read */
link_error_list_read(mysqli_object * obj,zval * retval,bool quiet)197 static int link_error_list_read(mysqli_object *obj, zval *retval, bool quiet)
198 {
199 MY_MYSQL *mysql;
200
201 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
202
203 mysql = (MY_MYSQL *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
204
205 if (mysql) {
206 array_init(retval);
207 #ifdef MYSQLI_USE_MYSQLND
208 if (1) {
209 MYSQLND_ERROR_LIST_ELEMENT * message;
210 zend_llist_position pos;
211 for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&mysql->mysql->data->error_info->error_list, &pos);
212 message;
213 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&mysql->mysql->data->error_info->error_list, &pos))
214 {
215 zval single_error;
216 array_init(&single_error);
217 add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
218 add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
219 add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
220 add_next_index_zval(retval, &single_error);
221 }
222 }
223 #else
224 if (mysql_errno(mysql->mysql)) {
225 zval single_error;
226 array_init(&single_error);
227 add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_errno(mysql->mysql));
228 add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_sqlstate(mysql->mysql));
229 add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_error(mysql->mysql));
230 add_next_index_zval(retval, &single_error);
231 }
232 #endif
233 } else {
234 ZVAL_EMPTY_ARRAY(retval);
235 }
236
237 return SUCCESS;
238 }
239 /* }}} */
240
241 /* link properties */
MYSQLI_MAP_PROPERTY_FUNC_LONG(link_errno_read,mysql_errno,MYSQLI_GET_MYSQL (MYSQLI_STATUS_INITIALIZED),zend_ulong,ZEND_ULONG_FMT)242 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_errno_read, mysql_errno, MYSQLI_GET_MYSQL(MYSQLI_STATUS_INITIALIZED), zend_ulong, ZEND_ULONG_FMT)
243 MYSQLI_MAP_PROPERTY_FUNC_STR(link_error_read, mysql_error, MYSQLI_GET_MYSQL(MYSQLI_STATUS_INITIALIZED))
244 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_field_count_read, mysql_field_count, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
245 MYSQLI_MAP_PROPERTY_FUNC_STR(link_host_info_read, mysql_get_host_info, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID))
246 MYSQLI_MAP_PROPERTY_FUNC_STR_OR_NULL(link_info_read, mysql_info, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID))
247 MYSQLI_MAP_PROPERTY_FUNC_LONG_OR_STR(link_insert_id_read, mysql_insert_id, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC)
248 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_protocol_version_read, mysql_get_proto_info, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
249 MYSQLI_MAP_PROPERTY_FUNC_STR(link_server_info_read, mysql_get_server_info, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID))
250 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_server_version_read, mysql_get_server_version, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
251 MYSQLI_MAP_PROPERTY_FUNC_STR(link_sqlstate_read, mysql_sqlstate, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID))
252 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_thread_id_read, mysql_thread_id, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
253 MYSQLI_MAP_PROPERTY_FUNC_LONG(link_warning_count_read, mysql_warning_count, MYSQLI_GET_MYSQL(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
254
255 /* result properties */
256
257 /* {{{ property result_type_read */
258 static int result_type_read(mysqli_object *obj, zval *retval, bool quiet)
259 {
260 MYSQL_RES *p;
261
262 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
263 p = (MYSQL_RES *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
264
265 ZEND_ASSERT(p);
266 ZVAL_LONG(retval, mysqli_result_is_unbuffered(p) ? MYSQLI_USE_RESULT:MYSQLI_STORE_RESULT);
267
268 return SUCCESS;
269 }
270 /* }}} */
271
272 /* {{{ property result_lengths_read */
result_lengths_read(mysqli_object * obj,zval * retval,bool quiet)273 static int result_lengths_read(mysqli_object *obj, zval *retval, bool quiet)
274 {
275 MYSQL_RES *p;
276 #ifdef MYSQLI_USE_MYSQLND
277 const size_t *ret;
278 #else
279 const unsigned long *ret;
280 #endif
281 uint32_t field_count;
282
283 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
284 p = (MYSQL_RES *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
285 field_count = mysql_num_fields(p);
286 if (!p || !field_count || !(ret = mysql_fetch_lengths(p))) {
287 ZVAL_NULL(retval);
288 } else {
289 zend_ulong i;
290
291 array_init(retval);
292
293 for (i = 0; i < field_count; i++) {
294 add_index_long(retval, i, ret[i]);
295 }
296 }
297
298 return SUCCESS;
299 }
300 /* }}} */
301
MYSQLI_MAP_PROPERTY_FUNC_LONG(result_current_field_read,mysql_field_tell,MYSQLI_GET_RESULT (MYSQLI_STATUS_VALID),zend_ulong,ZEND_ULONG_FMT)302 MYSQLI_MAP_PROPERTY_FUNC_LONG(result_current_field_read, mysql_field_tell, MYSQLI_GET_RESULT(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
303 MYSQLI_MAP_PROPERTY_FUNC_LONG(result_field_count_read, mysql_num_fields, MYSQLI_GET_RESULT(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
304 MYSQLI_MAP_PROPERTY_FUNC_LONG_OR_STR(result_num_rows_read, mysql_num_rows, MYSQLI_GET_RESULT(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC)
305
306 /* statement properties */
307
308 /* {{{ property stmt_id_read */
309 static int stmt_id_read(mysqli_object *obj, zval *retval, bool quiet)
310 {
311 MY_STMT *p;
312
313 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
314
315 p = (MY_STMT*)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
316
317 ZEND_ASSERT(p);
318 ZVAL_LONG(retval, mysqli_stmt_get_id(p->stmt));
319
320 return SUCCESS;
321 }
322 /* }}} */
323
324 /* {{{ property stmt_affected_rows_read */
stmt_affected_rows_read(mysqli_object * obj,zval * retval,bool quiet)325 static int stmt_affected_rows_read(mysqli_object *obj, zval *retval, bool quiet)
326 {
327 MY_STMT *p;
328 my_ulonglong rc;
329
330 CHECK_STATUS(MYSQLI_STATUS_VALID, quiet);
331
332 p = (MY_STMT *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
333
334 ZEND_ASSERT(p);
335 rc = mysql_stmt_affected_rows(p->stmt);
336
337 if (rc == (my_ulonglong) -1) {
338 ZVAL_LONG(retval, -1);
339 return SUCCESS;
340 }
341
342 if (rc < ZEND_LONG_MAX) {
343 ZVAL_LONG(retval, (zend_long) rc);
344 } else {
345 ZVAL_NEW_STR(retval, strpprintf(0, MYSQLI_LLU_SPEC, rc));
346 }
347
348 return SUCCESS;
349 }
350 /* }}} */
351
352 /* {{{ property stmt_error_list_read */
stmt_error_list_read(mysqli_object * obj,zval * retval,bool quiet)353 static int stmt_error_list_read(mysqli_object *obj, zval *retval, bool quiet)
354 {
355 MY_STMT * stmt;
356
357 CHECK_STATUS(MYSQLI_STATUS_INITIALIZED, quiet);
358
359 stmt = (MY_STMT *)((MYSQLI_RESOURCE *)(obj->ptr))->ptr;
360 if (stmt && stmt->stmt) {
361 array_init(retval);
362 #ifdef MYSQLI_USE_MYSQLND
363 if (stmt->stmt->data && stmt->stmt->data->error_info) {
364 MYSQLND_ERROR_LIST_ELEMENT * message;
365 zend_llist_position pos;
366 for (message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_first_ex(&stmt->stmt->data->error_info->error_list, &pos);
367 message;
368 message = (MYSQLND_ERROR_LIST_ELEMENT *) zend_llist_get_next_ex(&stmt->stmt->data->error_info->error_list, &pos))
369 {
370 zval single_error;
371 array_init(&single_error);
372 add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, message->error_no);
373 add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, message->sqlstate);
374 add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, message->error);
375 add_next_index_zval(retval, &single_error);
376 }
377 }
378 #else
379 if (mysql_stmt_errno(stmt->stmt)) {
380 zval single_error;
381 array_init(&single_error);
382 add_assoc_long_ex(&single_error, "errno", sizeof("errno") - 1, mysql_stmt_errno(stmt->stmt));
383 add_assoc_string_ex(&single_error, "sqlstate", sizeof("sqlstate") - 1, mysql_stmt_sqlstate(stmt->stmt));
384 add_assoc_string_ex(&single_error, "error", sizeof("error") - 1, mysql_stmt_error(stmt->stmt));
385 add_next_index_zval(retval, &single_error);
386 }
387 #endif
388 } else {
389 ZVAL_EMPTY_ARRAY(retval);
390 }
391
392 return SUCCESS;
393 }
394 /* }}} */
395
396 MYSQLI_MAP_PROPERTY_FUNC_LONG_OR_STR(stmt_insert_id_read, mysql_stmt_insert_id, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC)
397 MYSQLI_MAP_PROPERTY_FUNC_LONG_OR_STR(stmt_num_rows_read, mysql_stmt_num_rows, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), my_ulonglong, MYSQLI_LLU_SPEC)
398 MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_param_count_read, mysql_stmt_param_count, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
399 MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_field_count_read, mysql_stmt_field_count, MYSQLI_GET_STMT(MYSQLI_STATUS_VALID), zend_ulong, ZEND_ULONG_FMT)
400 MYSQLI_MAP_PROPERTY_FUNC_LONG(stmt_errno_read, mysql_stmt_errno, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED), zend_ulong, ZEND_ULONG_FMT)
401 MYSQLI_MAP_PROPERTY_FUNC_STR(stmt_error_read, mysql_stmt_error, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED))
402 MYSQLI_MAP_PROPERTY_FUNC_STR(stmt_sqlstate_read, mysql_stmt_sqlstate, MYSQLI_GET_STMT(MYSQLI_STATUS_INITIALIZED))
403
404 /* }}} */
405 const mysqli_property_entry mysqli_link_property_entries[] = {
406 {"affected_rows", sizeof("affected_rows") - 1, link_affected_rows_read, NULL},
407 {"client_info", sizeof("client_info") - 1, link_client_info_read, NULL},
408 {"client_version", sizeof("client_version") - 1, link_client_version_read, NULL},
409 {"connect_errno", sizeof("connect_errno") - 1, link_connect_errno_read, NULL},
410 {"connect_error", sizeof("connect_error") - 1, link_connect_error_read, NULL},
411 {"errno", sizeof("errno") - 1, link_errno_read, NULL},
412 {"error", sizeof("error") - 1, link_error_read, NULL},
413 {"error_list", sizeof("error_list") - 1, link_error_list_read, NULL},
414 {"field_count", sizeof("field_count") - 1, link_field_count_read, NULL},
415 {"host_info", sizeof("host_info") - 1, link_host_info_read, NULL},
416 {"info", sizeof("info") - 1, link_info_read, NULL},
417 {"insert_id", sizeof("insert_id") - 1, link_insert_id_read, NULL},
418 {"server_info", sizeof("server_info") - 1, link_server_info_read, NULL},
419 {"server_version", sizeof("server_version") - 1, link_server_version_read, NULL},
420 {"sqlstate", sizeof("sqlstate") - 1, link_sqlstate_read, NULL},
421 {"protocol_version",sizeof("protocol_version") - 1, link_protocol_version_read, NULL},
422 {"thread_id", sizeof("thread_id") - 1, link_thread_id_read, NULL},
423 {"warning_count", sizeof("warning_count") - 1, link_warning_count_read, NULL},
424 {NULL, 0, NULL, NULL}
425 };
426
427
428 const mysqli_property_entry mysqli_result_property_entries[] = {
429 {"current_field",sizeof("current_field")-1, result_current_field_read, NULL},
430 {"field_count", sizeof("field_count") - 1, result_field_count_read, NULL},
431 {"lengths", sizeof("lengths") - 1, result_lengths_read, NULL},
432 {"num_rows", sizeof("num_rows") - 1, result_num_rows_read, NULL},
433 {"type", sizeof("type") - 1, result_type_read, NULL},
434 {NULL, 0, NULL, NULL}
435 };
436
437 const mysqli_property_entry mysqli_stmt_property_entries[] = {
438 {"affected_rows", sizeof("affected_rows")-1,stmt_affected_rows_read, NULL},
439 {"insert_id", sizeof("insert_id") - 1, stmt_insert_id_read, NULL},
440 {"num_rows", sizeof("num_rows") - 1, stmt_num_rows_read, NULL},
441 {"param_count", sizeof("param_count") - 1, stmt_param_count_read, NULL},
442 {"field_count", sizeof("field_count") - 1, stmt_field_count_read, NULL},
443 {"errno", sizeof("errno") - 1, stmt_errno_read, NULL},
444 {"error", sizeof("error") - 1, stmt_error_read, NULL},
445 {"error_list", sizeof("error_list") - 1, stmt_error_list_read, NULL},
446 {"sqlstate", sizeof("sqlstate") - 1, stmt_sqlstate_read, NULL},
447 {"id", sizeof("id") - 1, stmt_id_read, NULL},
448 {NULL, 0, NULL, NULL}
449 };
450