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