xref: /PHP-7.0/ext/standard/var.c (revision 478f119a)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2017 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Jani Lehtimäki <jkl@njet.net>                               |
16    |          Thies C. Arntzen <thies@thieso.net>                         |
17    |          Sascha Schumann <sascha@schumann.cx>                        |
18    +----------------------------------------------------------------------+
19 */
20 
21 /* $Id$ */
22 
23 /* {{{ includes
24 */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include "php.h"
29 #include "php_string.h"
30 #include "php_var.h"
31 #include "zend_smart_str.h"
32 #include "basic_functions.h"
33 #include "php_incomplete_class.h"
34 
35 #define COMMON (is_ref ? "&" : "")
36 /* }}} */
37 
php_array_element_dump(zval * zv,zend_ulong index,zend_string * key,int level)38 static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
39 {
40 	if (key == NULL) { /* numeric key */
41 		php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
42 	} else { /* string key */
43 		php_printf("%*c[\"", level + 1, ' ');
44 		PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
45 		php_printf("\"]=>\n");
46 	}
47 	php_var_dump(zv, level + 2);
48 }
49 /* }}} */
50 
php_object_property_dump(zval * zv,zend_ulong index,zend_string * key,int level)51 static void php_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
52 {
53 	const char *prop_name, *class_name;
54 
55 	if (key == NULL) { /* numeric key */
56 		php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
57 	} else { /* string key */
58 		int unmangle = zend_unmangle_property_name(key, &class_name, &prop_name);
59 		php_printf("%*c[", level + 1, ' ');
60 
61 		if (class_name && unmangle == SUCCESS) {
62 			if (class_name[0] == '*') {
63 				php_printf("\"%s\":protected", prop_name);
64 			} else {
65 				php_printf("\"%s\":\"%s\":private", prop_name, class_name);
66 			}
67 		} else {
68 			php_printf("\"");
69 			PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
70 			php_printf("\"");
71 		}
72 		ZEND_PUTS("]=>\n");
73 	}
74 	php_var_dump(zv, level + 2);
75 }
76 /* }}} */
77 
php_var_dump(zval * struc,int level)78 PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
79 {
80 	HashTable *myht;
81 	zend_string *class_name;
82 	int is_temp;
83 	int is_ref = 0;
84 	zend_ulong num;
85 	zend_string *key;
86 	zval *val;
87 	uint32_t count;
88 
89 	if (level > 1) {
90 		php_printf("%*c", level - 1, ' ');
91 	}
92 
93 again:
94 	switch (Z_TYPE_P(struc)) {
95 		case IS_FALSE:
96 			php_printf("%sbool(false)\n", COMMON);
97 			break;
98 		case IS_TRUE:
99 			php_printf("%sbool(true)\n", COMMON);
100 			break;
101 		case IS_NULL:
102 			php_printf("%sNULL\n", COMMON);
103 			break;
104 		case IS_LONG:
105 			php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
106 			break;
107 		case IS_DOUBLE:
108 			php_printf("%sfloat(%.*G)\n", COMMON, (int) EG(precision), Z_DVAL_P(struc));
109 			break;
110 		case IS_STRING:
111 			php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
112 			PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
113 			PUTS("\"\n");
114 			break;
115 		case IS_ARRAY:
116 			myht = Z_ARRVAL_P(struc);
117 			if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && ++myht->u.v.nApplyCount > 1) {
118 				PUTS("*RECURSION*\n");
119 				--myht->u.v.nApplyCount;
120 				return;
121 			}
122 			count = zend_array_count(myht);
123 			php_printf("%sarray(%d) {\n", COMMON, count);
124 			is_temp = 0;
125 
126 			ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) {
127 				php_array_element_dump(val, num, key, level);
128 			} ZEND_HASH_FOREACH_END();
129 			if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
130 				--myht->u.v.nApplyCount;
131 			}
132 			if (is_temp) {
133 				zend_hash_destroy(myht);
134 				efree(myht);
135 			}
136 			if (level > 1) {
137 				php_printf("%*c", level-1, ' ');
138 			}
139 			PUTS("}\n");
140 			break;
141 		case IS_OBJECT:
142 			if (Z_OBJ_APPLY_COUNT_P(struc) > 0) {
143 				PUTS("*RECURSION*\n");
144 				return;
145 			}
146 			Z_OBJ_INC_APPLY_COUNT_P(struc);
147 
148 			myht = Z_OBJDEBUG_P(struc, is_temp);
149 			class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
150 			php_printf("%sobject(%s)#%d (%d) {\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
151 			zend_string_release(class_name);
152 
153 			if (myht) {
154 				zend_ulong num;
155 				zend_string *key;
156 				zval *val;
157 
158 				ZEND_HASH_FOREACH_KEY_VAL_IND(myht, num, key, val) {
159 					php_object_property_dump(val, num, key, level);
160 				} ZEND_HASH_FOREACH_END();
161 				if (is_temp) {
162 					zend_hash_destroy(myht);
163 					efree(myht);
164 				}
165 			}
166 			if (level > 1) {
167 				php_printf("%*c", level-1, ' ');
168 			}
169 			PUTS("}\n");
170 			Z_OBJ_DEC_APPLY_COUNT_P(struc);
171 			break;
172 		case IS_RESOURCE: {
173 			const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
174 			php_printf("%sresource(%pd) of type (%s)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown");
175 			break;
176 		}
177 		case IS_REFERENCE:
178 			//??? hide references with refcount==1 (for compatibility)
179 			if (Z_REFCOUNT_P(struc) > 1) {
180 				is_ref = 1;
181 			}
182 			struc = Z_REFVAL_P(struc);
183 			goto again;
184 			break;
185 		default:
186 			php_printf("%sUNKNOWN:0\n", COMMON);
187 			break;
188 	}
189 }
190 /* }}} */
191 
192 /* {{{ proto void var_dump(mixed var)
193    Dumps a string representation of variable to output */
PHP_FUNCTION(var_dump)194 PHP_FUNCTION(var_dump)
195 {
196 	zval *args;
197 	int argc;
198 	int	i;
199 
200 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
201 		return;
202 	}
203 
204 	for (i = 0; i < argc; i++) {
205 		php_var_dump(&args[i], 1);
206 	}
207 }
208 /* }}} */
209 
zval_array_element_dump(zval * zv,zend_ulong index,zend_string * key,int level)210 static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
211 {
212 	if (key == NULL) { /* numeric key */
213 		php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
214 	} else { /* string key */
215 		php_printf("%*c[\"", level + 1, ' ');
216 		PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
217 		php_printf("\"]=>\n");
218 	}
219 	php_debug_zval_dump(zv, level + 2);
220 }
221 /* }}} */
222 
zval_object_property_dump(zval * zv,zend_ulong index,zend_string * key,int level)223 static void zval_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
224 {
225 	const char *prop_name, *class_name;
226 
227 	if (key == NULL) { /* numeric key */
228 		php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
229 	} else { /* string key */
230 		zend_unmangle_property_name(key, &class_name, &prop_name);
231 		php_printf("%*c[", level + 1, ' ');
232 
233 		if (class_name) {
234 			if (class_name[0] == '*') {
235 				php_printf("\"%s\":protected", prop_name);
236 			} else {
237 				php_printf("\"%s\":\"%s\":private", prop_name, class_name);
238 			}
239 		} else {
240 			php_printf("\"%s\"", prop_name);
241 		}
242 		ZEND_PUTS("]=>\n");
243 	}
244 	php_debug_zval_dump(zv, level + 2);
245 }
246 /* }}} */
247 
php_debug_zval_dump(zval * struc,int level)248 PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
249 {
250 	HashTable *myht = NULL;
251 	zend_string *class_name;
252 	int is_temp = 0;
253 	int is_ref = 0;
254 	zend_ulong index;
255 	zend_string *key;
256 	zval *val;
257 	uint32_t count;
258 
259 	if (level > 1) {
260 		php_printf("%*c", level - 1, ' ');
261 	}
262 
263 again:
264 	switch (Z_TYPE_P(struc)) {
265 	case IS_FALSE:
266 		php_printf("%sbool(false)\n", COMMON);
267 		break;
268 	case IS_TRUE:
269 		php_printf("%sbool(true)\n", COMMON);
270 		break;
271 	case IS_NULL:
272 		php_printf("%sNULL\n", COMMON);
273 		break;
274 	case IS_LONG:
275 		php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
276 		break;
277 	case IS_DOUBLE:
278 		php_printf("%sfloat(%.*G)\n", COMMON, (int) EG(precision), Z_DVAL_P(struc));
279 		break;
280 	case IS_STRING:
281 		php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
282 		PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
283 		php_printf("\" refcount(%u)\n", Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1);
284 		break;
285 	case IS_ARRAY:
286 		myht = Z_ARRVAL_P(struc);
287 		if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 1) {
288 			myht->u.v.nApplyCount--;
289 			PUTS("*RECURSION*\n");
290 			return;
291 		}
292 		count = zend_array_count(myht);
293 		php_printf("%sarray(%d) refcount(%u){\n", COMMON, count, Z_REFCOUNTED_P(struc) ? Z_REFCOUNT_P(struc) : 1);
294 		ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
295 			zval_array_element_dump(val, index, key, level);
296 		} ZEND_HASH_FOREACH_END();
297 		if (level > 1 && ZEND_HASH_APPLY_PROTECTION(myht)) {
298 			myht->u.v.nApplyCount--;
299 		}
300 		if (is_temp) {
301 			zend_hash_destroy(myht);
302 			efree(myht);
303 		}
304 		if (level > 1) {
305 			php_printf("%*c", level - 1, ' ');
306 		}
307 		PUTS("}\n");
308 		break;
309 	case IS_OBJECT:
310 		myht = Z_OBJDEBUG_P(struc, is_temp);
311 		if (myht) {
312 			if (myht->u.v.nApplyCount > 1) {
313 				PUTS("*RECURSION*\n");
314 				return;
315 			} else {
316 				myht->u.v.nApplyCount++;
317 			}
318 		}
319 		class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
320 		php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc));
321 		zend_string_release(class_name);
322 		if (myht) {
323 			ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
324 				zval_object_property_dump(val, index, key, level);
325 			} ZEND_HASH_FOREACH_END();
326 			myht->u.v.nApplyCount--;
327 			if (is_temp) {
328 				zend_hash_destroy(myht);
329 				efree(myht);
330 			}
331 		}
332 		if (level > 1) {
333 			php_printf("%*c", level - 1, ' ');
334 		}
335 		PUTS("}\n");
336 		break;
337 	case IS_RESOURCE: {
338 		const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
339 		php_printf("%sresource(%d) of type (%s) refcount(%u)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown", Z_REFCOUNT_P(struc));
340 		break;
341 	}
342 	case IS_REFERENCE:
343 		//??? hide references with refcount==1 (for compatibility)
344 		if (Z_REFCOUNT_P(struc) > 1) {
345 			is_ref = 1;
346 		}
347 		struc = Z_REFVAL_P(struc);
348 		goto again;
349 	default:
350 		php_printf("%sUNKNOWN:0\n", COMMON);
351 		break;
352 	}
353 }
354 /* }}} */
355 
356 /* {{{ proto void debug_zval_dump(mixed var)
357    Dumps a string representation of an internal zend value to output. */
PHP_FUNCTION(debug_zval_dump)358 PHP_FUNCTION(debug_zval_dump)
359 {
360 	zval *args;
361 	int argc;
362 	int	i;
363 
364 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
365 		return;
366 	}
367 
368 	for (i = 0; i < argc; i++) {
369 		php_debug_zval_dump(&args[i], 1);
370 	}
371 }
372 /* }}} */
373 
374 #define buffer_append_spaces(buf, num_spaces) \
375 	do { \
376 		char *tmp_spaces; \
377 		size_t tmp_spaces_len; \
378 		tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \
379 		smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \
380 		efree(tmp_spaces); \
381 	} while(0);
382 
php_array_element_export(zval * zv,zend_ulong index,zend_string * key,int level,smart_str * buf)383 static void php_array_element_export(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf) /* {{{ */
384 {
385 	if (key == NULL) { /* numeric key */
386 		buffer_append_spaces(buf, level+1);
387 		smart_str_append_long(buf, (zend_long) index);
388 		smart_str_appendl(buf, " => ", 4);
389 
390 	} else { /* string key */
391 		zend_string *tmp_str;
392 		zend_string *ckey = php_addcslashes(key, 0, "'\\", 2);
393 		tmp_str = php_str_to_str(ZSTR_VAL(ckey), ZSTR_LEN(ckey), "\0", 1, "' . \"\\0\" . '", 12);
394 
395 		buffer_append_spaces(buf, level + 1);
396 
397 		smart_str_appendc(buf, '\'');
398 		smart_str_append(buf, tmp_str);
399 		smart_str_appendl(buf, "' => ", 5);
400 
401 		zend_string_free(ckey);
402 		zend_string_free(tmp_str);
403 	}
404 	php_var_export_ex(zv, level + 2, buf);
405 
406 	smart_str_appendc(buf, ',');
407 	smart_str_appendc(buf, '\n');
408 }
409 /* }}} */
410 
php_object_element_export(zval * zv,zend_ulong index,zend_string * key,int level,smart_str * buf)411 static void php_object_element_export(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf) /* {{{ */
412 {
413 	buffer_append_spaces(buf, level + 2);
414 	if (key != NULL) {
415 		const char *class_name, *prop_name;
416 		size_t prop_name_len;
417 		zend_string *pname_esc;
418 
419 		zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len);
420 		pname_esc = php_addcslashes(zend_string_init(prop_name, prop_name_len, 0), 1, "'\\", 2);
421 
422 		smart_str_appendc(buf, '\'');
423 		smart_str_append(buf, pname_esc);
424 		smart_str_appendc(buf, '\'');
425 		zend_string_release(pname_esc);
426 	} else {
427 		smart_str_append_long(buf, (zend_long) index);
428 	}
429 	smart_str_appendl(buf, " => ", 4);
430 	php_var_export_ex(zv, level + 2, buf);
431 	smart_str_appendc(buf, ',');
432 	smart_str_appendc(buf, '\n');
433 }
434 /* }}} */
435 
php_var_export_ex(zval * struc,int level,smart_str * buf)436 PHPAPI void php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */
437 {
438 	HashTable *myht;
439 	char *tmp_str;
440 	size_t tmp_len;
441 	zend_string *ztmp, *ztmp2;
442 	zend_ulong index;
443 	zend_string *key;
444 	zval *val;
445 
446 again:
447 	switch (Z_TYPE_P(struc)) {
448 		case IS_FALSE:
449 			smart_str_appendl(buf, "false", 5);
450 			break;
451 		case IS_TRUE:
452 			smart_str_appendl(buf, "true", 4);
453 			break;
454 		case IS_NULL:
455 			smart_str_appendl(buf, "NULL", 4);
456 			break;
457 		case IS_LONG:
458 			smart_str_append_long(buf, Z_LVAL_P(struc));
459 			break;
460 		case IS_DOUBLE:
461 			tmp_len = spprintf(&tmp_str, 0,"%.*H", PG(serialize_precision), Z_DVAL_P(struc));
462 			smart_str_appendl(buf, tmp_str, tmp_len);
463 			/* Without a decimal point, PHP treats a number literal as an int.
464 			 * This check even works for scientific notation, because the
465 			 * mantissa always contains a decimal point.
466 			 * We need to check for finiteness, because INF, -INF and NAN
467 			 * must not have a decimal point added.
468 			 */
469 			if (zend_finite(Z_DVAL_P(struc)) && NULL == strchr(tmp_str, '.')) {
470 				smart_str_appendl(buf, ".0", 2);
471 			}
472 			efree(tmp_str);
473 			break;
474 		case IS_STRING:
475 			ztmp = php_addcslashes(Z_STR_P(struc), 0, "'\\", 2);
476 			ztmp2 = php_str_to_str(ZSTR_VAL(ztmp), ZSTR_LEN(ztmp), "\0", 1, "' . \"\\0\" . '", 12);
477 
478 			smart_str_appendc(buf, '\'');
479 			smart_str_append(buf, ztmp2);
480 			smart_str_appendc(buf, '\'');
481 
482 			zend_string_free(ztmp);
483 			zend_string_free(ztmp2);
484 			break;
485 		case IS_ARRAY:
486 			myht = Z_ARRVAL_P(struc);
487 			if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) {
488 				myht->u.v.nApplyCount--;
489 				smart_str_appendl(buf, "NULL", 4);
490 				zend_error(E_WARNING, "var_export does not handle circular references");
491 				return;
492 			}
493 			if (level > 1) {
494 				smart_str_appendc(buf, '\n');
495 				buffer_append_spaces(buf, level - 1);
496 			}
497 			smart_str_appendl(buf, "array (\n", 8);
498 			ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
499 				php_array_element_export(val, index, key, level, buf);
500 			} ZEND_HASH_FOREACH_END();
501 			if (ZEND_HASH_APPLY_PROTECTION(myht)) {
502 				myht->u.v.nApplyCount--;
503 			}
504 			if (level > 1) {
505 				buffer_append_spaces(buf, level - 1);
506 			}
507 			smart_str_appendc(buf, ')');
508 
509 			break;
510 
511 		case IS_OBJECT:
512 			myht = Z_OBJPROP_P(struc);
513 			if (myht) {
514 				if (myht->u.v.nApplyCount > 0) {
515 					smart_str_appendl(buf, "NULL", 4);
516 					zend_error(E_WARNING, "var_export does not handle circular references");
517 					return;
518 				} else {
519 					myht->u.v.nApplyCount++;
520 				}
521 			}
522 			if (level > 1) {
523 				smart_str_appendc(buf, '\n');
524 				buffer_append_spaces(buf, level - 1);
525 			}
526 
527 			smart_str_append(buf, Z_OBJCE_P(struc)->name);
528 			smart_str_appendl(buf, "::__set_state(array(\n", 21);
529 
530 			if (myht) {
531 				ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
532 					php_object_element_export(val, index, key, level, buf);
533 				} ZEND_HASH_FOREACH_END();
534 				myht->u.v.nApplyCount--;
535 			}
536 			if (level > 1) {
537 				buffer_append_spaces(buf, level - 1);
538 			}
539 			smart_str_appendl(buf, "))", 2);
540 
541 			break;
542 		case IS_REFERENCE:
543 			struc = Z_REFVAL_P(struc);
544 			goto again;
545 			break;
546 		default:
547 			smart_str_appendl(buf, "NULL", 4);
548 			break;
549 	}
550 }
551 /* }}} */
552 
553 /* FOR BC reasons, this will always perform and then print */
php_var_export(zval * struc,int level)554 PHPAPI void php_var_export(zval *struc, int level) /* {{{ */
555 {
556 	smart_str buf = {0};
557 	php_var_export_ex(struc, level, &buf);
558 	smart_str_0(&buf);
559 	PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
560 	smart_str_free(&buf);
561 }
562 /* }}} */
563 
564 /* {{{ proto mixed var_export(mixed var [, bool return])
565    Outputs or returns a string representation of a variable */
PHP_FUNCTION(var_export)566 PHP_FUNCTION(var_export)
567 {
568 	zval *var;
569 	zend_bool return_output = 0;
570 	smart_str buf = {0};
571 
572 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &var, &return_output) == FAILURE) {
573 		return;
574 	}
575 
576 	php_var_export_ex(var, 1, &buf);
577 	smart_str_0 (&buf);
578 
579 	if (return_output) {
580 		RETURN_NEW_STR(buf.s);
581 	} else {
582 		PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
583 		smart_str_free(&buf);
584 	}
585 }
586 /* }}} */
587 
588 static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash);
589 
php_add_var_hash(php_serialize_data_t data,zval * var)590 static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */
591 {
592 	zval *zv;
593 	zend_ulong key;
594 	zend_bool is_ref = Z_ISREF_P(var);
595 
596 	data->n += 1;
597 
598 	if (!is_ref && Z_TYPE_P(var) != IS_OBJECT) {
599 		return 0;
600 	}
601 
602 	/* References to objects are treated as if the reference didn't exist */
603 	if (is_ref && Z_TYPE_P(Z_REFVAL_P(var)) == IS_OBJECT) {
604 		var = Z_REFVAL_P(var);
605 	}
606 
607 	/* Index for the variable is stored using the numeric value of the pointer to
608 	 * the zend_refcounted struct */
609 	key = (zend_ulong) (zend_uintptr_t) Z_COUNTED_P(var);
610 	zv = zend_hash_index_find(&data->ht, key);
611 
612 	if (zv) {
613 		/* References are only counted once, undo the data->n increment above */
614 		if (is_ref) {
615 			data->n -= 1;
616 		}
617 
618 		return Z_LVAL_P(zv);
619 	} else {
620 		zval zv_n;
621 		ZVAL_LONG(&zv_n, data->n);
622 		zend_hash_index_add_new(&data->ht, key, &zv_n);
623 
624 		/* Additionally to the index, we also store the variable, to ensure that it is
625 		 * not destroyed during serialization and its pointer reused. The variable is
626 		 * stored at the numeric value of the pointer + 1, which cannot be the location
627 		 * of another zend_refcounted structure. */
628 		zend_hash_index_add_new(&data->ht, key + 1, var);
629 		Z_ADDREF_P(var);
630 
631 		return 0;
632 	}
633 }
634 /* }}} */
635 
php_var_serialize_long(smart_str * buf,zend_long val)636 static inline void php_var_serialize_long(smart_str *buf, zend_long val) /* {{{ */
637 {
638 	smart_str_appendl(buf, "i:", 2);
639 	smart_str_append_long(buf, val);
640 	smart_str_appendc(buf, ';');
641 }
642 /* }}} */
643 
php_var_serialize_string(smart_str * buf,char * str,size_t len)644 static inline void php_var_serialize_string(smart_str *buf, char *str, size_t len) /* {{{ */
645 {
646 	smart_str_appendl(buf, "s:", 2);
647 	smart_str_append_unsigned(buf, len);
648 	smart_str_appendl(buf, ":\"", 2);
649 	smart_str_appendl(buf, str, len);
650 	smart_str_appendl(buf, "\";", 2);
651 }
652 /* }}} */
653 
php_var_serialize_class_name(smart_str * buf,zval * struc)654 static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc) /* {{{ */
655 {
656 	PHP_CLASS_ATTRIBUTES;
657 
658 	PHP_SET_CLASS_ATTRIBUTES(struc);
659 	smart_str_appendl(buf, "O:", 2);
660 	smart_str_append_unsigned(buf, ZSTR_LEN(class_name));
661 	smart_str_appendl(buf, ":\"", 2);
662 	smart_str_append(buf, class_name);
663 	smart_str_appendl(buf, "\":", 2);
664 	PHP_CLEANUP_CLASS_ATTRIBUTES();
665 	return incomplete_class;
666 }
667 /* }}} */
668 
php_var_serialize_collect_names(HashTable * src,uint32_t count,zend_bool incomplete)669 static HashTable *php_var_serialize_collect_names(HashTable *src, uint32_t count, zend_bool incomplete) /* {{{ */ {
670 	zval *val;
671 	HashTable *ht;
672 	zend_string *key, *name;
673 
674 	ALLOC_HASHTABLE(ht);
675 	zend_hash_init(ht, count, NULL, NULL, 0);
676 	ZEND_HASH_FOREACH_STR_KEY_VAL(src, key, val) {
677 		if (incomplete && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) {
678 			continue;
679 		}
680 		if (Z_TYPE_P(val) != IS_STRING) {
681 			php_error_docref(NULL, E_NOTICE,
682 					"__sleep should return an array only containing the names of instance-variables to serialize.");
683 		}
684 		name = zval_get_string(val);
685 		if (zend_hash_exists(ht, name)) {
686 			php_error_docref(NULL, E_NOTICE,
687 					"\"%s\" is returned from __sleep multiple times", ZSTR_VAL(name));
688 			zend_string_release(name);
689 			continue;
690 		}
691 		zend_hash_add_empty_element(ht, name);
692 		zend_string_release(name);
693 	} ZEND_HASH_FOREACH_END();
694 
695 	return ht;
696 }
697 /* }}} */
698 
php_var_serialize_class(smart_str * buf,zval * struc,zval * retval_ptr,php_serialize_data_t var_hash)699 static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */
700 {
701 	uint32_t count;
702 	zend_bool incomplete_class;
703 	HashTable *ht;
704 
705 	incomplete_class = php_var_serialize_class_name(buf, struc);
706 	/* count after serializing name, since php_var_serialize_class_name
707 	 * changes the count if the variable is incomplete class */
708 	if (Z_TYPE_P(retval_ptr) == IS_ARRAY) {
709 		ht = Z_ARRVAL_P(retval_ptr);
710 		count = zend_array_count(ht);
711 	} else if (Z_TYPE_P(retval_ptr) == IS_OBJECT) {
712 		ht = Z_OBJPROP_P(retval_ptr);
713 		count = zend_array_count(ht);
714 		if (incomplete_class) {
715 			--count;
716 		}
717 	} else {
718 		count = 0;
719 		ht = NULL;
720 	}
721 
722 	if (count > 0) {
723 		zval *d;
724 		zval nval, *nvalp;
725 		zend_string *name;
726 		HashTable *names, *propers;
727 
728 		names = php_var_serialize_collect_names(ht, count, incomplete_class);
729 
730 		smart_str_append_unsigned(buf, zend_hash_num_elements(names));
731 		smart_str_appendl(buf, ":{", 2);
732 
733 		ZVAL_NULL(&nval);
734 		nvalp = &nval;
735 		propers = Z_OBJPROP_P(struc);
736 
737 		ZEND_HASH_FOREACH_STR_KEY(names, name) {
738 			if ((d = zend_hash_find(propers, name)) != NULL) {
739 				if (Z_TYPE_P(d) == IS_INDIRECT) {
740 					d = Z_INDIRECT_P(d);
741 					if (Z_TYPE_P(d) == IS_UNDEF) {
742 						continue;
743 					}
744 				}
745 				php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
746 				php_var_serialize_intern(buf, d, var_hash);
747 			} else {
748 				zend_class_entry *ce = Z_OBJ_P(struc)->ce;
749 				if (ce) {
750 					zend_string *prot_name, *priv_name;
751 
752 					do {
753 						priv_name = zend_mangle_property_name(
754 								ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
755 						if ((d = zend_hash_find(propers, priv_name)) != NULL) {
756 							if (Z_TYPE_P(d) == IS_INDIRECT) {
757 								d = Z_INDIRECT_P(d);
758 								if (Z_ISUNDEF_P(d)) {
759 									break;
760 								}
761 							}
762 							php_var_serialize_string(buf, ZSTR_VAL(priv_name), ZSTR_LEN(priv_name));
763 							zend_string_free(priv_name);
764 							php_var_serialize_intern(buf, d, var_hash);
765 							break;
766 						}
767 						zend_string_free(priv_name);
768 						prot_name = zend_mangle_property_name(
769 								"*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
770 						if ((d = zend_hash_find(propers, prot_name)) != NULL) {
771 							if (Z_TYPE_P(d) == IS_INDIRECT) {
772 								d = Z_INDIRECT_P(d);
773 								if (Z_TYPE_P(d) == IS_UNDEF) {
774 									zend_string_free(prot_name);
775 									break;
776 								}
777 							}
778 							php_var_serialize_string(buf, ZSTR_VAL(prot_name), ZSTR_LEN(prot_name));
779 							zend_string_free(prot_name);
780 							php_var_serialize_intern(buf, d, var_hash);
781 							break;
782 						}
783 						zend_string_free(prot_name);
784 						php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
785 						php_var_serialize_intern(buf, nvalp, var_hash);
786 						php_error_docref(NULL, E_NOTICE,
787 								"\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
788 					} while (0);
789 				} else {
790 					php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
791 					php_var_serialize_intern(buf, nvalp, var_hash);
792 				}
793 			}
794 		} ZEND_HASH_FOREACH_END();
795 		smart_str_appendc(buf, '}');
796 
797 		zend_hash_destroy(names);
798 		FREE_HASHTABLE(names);
799 	} else {
800 		smart_str_appendl(buf, "0:{}", 4);
801 	}
802 }
803 /* }}} */
804 
php_var_serialize_intern(smart_str * buf,zval * struc,php_serialize_data_t var_hash)805 static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
806 {
807 	zend_long var_already;
808 	HashTable *myht;
809 
810 	if (EG(exception)) {
811 		return;
812 	}
813 
814 	if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
815 		if (Z_ISREF_P(struc)) {
816 			smart_str_appendl(buf, "R:", 2);
817 			smart_str_append_long(buf, var_already);
818 			smart_str_appendc(buf, ';');
819 			return;
820 		} else if (Z_TYPE_P(struc) == IS_OBJECT) {
821 			smart_str_appendl(buf, "r:", 2);
822 			smart_str_append_long(buf, var_already);
823 			smart_str_appendc(buf, ';');
824 			return;
825 		}
826 	}
827 
828 again:
829 	switch (Z_TYPE_P(struc)) {
830 		case IS_FALSE:
831 			smart_str_appendl(buf, "b:0;", 4);
832 			return;
833 
834 		case IS_TRUE:
835 			smart_str_appendl(buf, "b:1;", 4);
836 			return;
837 
838 		case IS_NULL:
839 			smart_str_appendl(buf, "N;", 2);
840 			return;
841 
842 		case IS_LONG:
843 			php_var_serialize_long(buf, Z_LVAL_P(struc));
844 			return;
845 
846 		case IS_DOUBLE: {
847 				char *s;
848 
849 				smart_str_appendl(buf, "d:", 2);
850 				s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1);
851 				php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', s);
852 				smart_str_appends(buf, s);
853 				smart_str_appendc(buf, ';');
854 				efree(s);
855 				return;
856 			}
857 
858 		case IS_STRING:
859 			php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
860 			return;
861 
862 		case IS_OBJECT: {
863 				zend_class_entry *ce = Z_OBJCE_P(struc);
864 
865 				if (ce->serialize != NULL) {
866 					/* has custom handler */
867 					unsigned char *serialized_data = NULL;
868 					size_t serialized_length;
869 
870 					if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) {
871 						smart_str_appendl(buf, "C:", 2);
872 						smart_str_append_unsigned(buf, ZSTR_LEN(Z_OBJCE_P(struc)->name));
873 						smart_str_appendl(buf, ":\"", 2);
874 						smart_str_append(buf, Z_OBJCE_P(struc)->name);
875 						smart_str_appendl(buf, "\":", 2);
876 
877 						smart_str_append_unsigned(buf, serialized_length);
878 						smart_str_appendl(buf, ":{", 2);
879 						smart_str_appendl(buf, (char *) serialized_data, serialized_length);
880 						smart_str_appendc(buf, '}');
881 					} else {
882 						smart_str_appendl(buf, "N;", 2);
883 					}
884 					if (serialized_data) {
885 						efree(serialized_data);
886 					}
887 					return;
888 				}
889 
890 				if (ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) {
891 					zval fname, tmp, retval;
892 					int res;
893 
894 					ZVAL_COPY(&tmp, struc);
895 					ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1);
896 					BG(serialize_lock)++;
897 					res = call_user_function_ex(CG(function_table), &tmp, &fname, &retval, 0, 0, 1, NULL);
898 					BG(serialize_lock)--;
899 					zval_dtor(&fname);
900 
901 					if (EG(exception)) {
902 						zval_ptr_dtor(&retval);
903 						zval_ptr_dtor(&tmp);
904 						return;
905 					}
906 
907 					if (res == SUCCESS) {
908 						if (Z_TYPE(retval) != IS_UNDEF) {
909 							if (HASH_OF(&retval)) {
910 								php_var_serialize_class(buf, &tmp, &retval, var_hash);
911 							} else {
912 								php_error_docref(NULL, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize");
913 								/* we should still add element even if it's not OK,
914 								 * since we already wrote the length of the array before */
915 								smart_str_appendl(buf,"N;", 2);
916 							}
917 						}
918 						zval_ptr_dtor(&retval);
919 						zval_ptr_dtor(&tmp);
920 						return;
921 					}
922 					zval_ptr_dtor(&retval);
923 					zval_ptr_dtor(&tmp);
924 				}
925 
926 				/* fall-through */
927 			}
928 		case IS_ARRAY: {
929 			uint32_t i;
930 			zend_bool incomplete_class = 0;
931 			if (Z_TYPE_P(struc) == IS_ARRAY) {
932 				smart_str_appendl(buf, "a:", 2);
933 				myht = Z_ARRVAL_P(struc);
934 				i = zend_array_count(myht);
935 			} else {
936 				incomplete_class = php_var_serialize_class_name(buf, struc);
937 				myht = Z_OBJPROP_P(struc);
938 				/* count after serializing name, since php_var_serialize_class_name
939 				 * changes the count if the variable is incomplete class */
940 				i = zend_array_count(myht);
941 				if (i > 0 && incomplete_class) {
942 					--i;
943 				}
944 			}
945 			smart_str_append_unsigned(buf, i);
946 			smart_str_appendl(buf, ":{", 2);
947 			if (i > 0) {
948 				zend_string *key;
949 				zval *data;
950 				zend_ulong index;
951 
952 				ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
953 
954 					if (incomplete_class && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) {
955 						continue;
956 					}
957 
958 					if (!key) {
959 						php_var_serialize_long(buf, index);
960 					} else {
961 						php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
962 					}
963 
964 					if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
965 						data = Z_REFVAL_P(data);
966 					}
967 
968 					/* we should still add element even if it's not OK,
969 					 * since we already wrote the length of the array before */
970 					if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))
971 						|| (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1)
972 					) {
973 						smart_str_appendl(buf, "N;", 2);
974 					} else {
975 						if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
976 							Z_ARRVAL_P(data)->u.v.nApplyCount++;
977 						}
978 						php_var_serialize_intern(buf, data, var_hash);
979 						if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
980 							Z_ARRVAL_P(data)->u.v.nApplyCount--;
981 						}
982 					}
983 				} ZEND_HASH_FOREACH_END();
984 			}
985 			smart_str_appendc(buf, '}');
986 			return;
987 		}
988 		case IS_REFERENCE:
989 			struc = Z_REFVAL_P(struc);
990 			goto again;
991 		default:
992 			smart_str_appendl(buf, "i:0;", 4);
993 			return;
994 	}
995 }
996 /* }}} */
997 
php_var_serialize(smart_str * buf,zval * struc,php_serialize_data_t * data)998 PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
999 {
1000 	php_var_serialize_intern(buf, struc, *data);
1001 	smart_str_0(buf);
1002 }
1003 /* }}} */
1004 
1005 /* {{{ proto string serialize(mixed variable)
1006    Returns a string representation of variable (which can later be unserialized) */
PHP_FUNCTION(serialize)1007 PHP_FUNCTION(serialize)
1008 {
1009 	zval *struc;
1010 	php_serialize_data_t var_hash;
1011 	smart_str buf = {0};
1012 
1013 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &struc) == FAILURE) {
1014 		return;
1015 	}
1016 
1017 	PHP_VAR_SERIALIZE_INIT(var_hash);
1018 	php_var_serialize(&buf, struc, &var_hash);
1019 	PHP_VAR_SERIALIZE_DESTROY(var_hash);
1020 
1021 	if (EG(exception)) {
1022 		smart_str_free(&buf);
1023 		RETURN_FALSE;
1024 	}
1025 
1026 	if (buf.s) {
1027 		RETURN_NEW_STR(buf.s);
1028 	} else {
1029 		RETURN_NULL();
1030 	}
1031 }
1032 /* }}} */
1033 
1034 /* {{{ proto mixed unserialize(string variable_representation[, array allowed_classes])
1035    Takes a string representation of variable and recreates it */
PHP_FUNCTION(unserialize)1036 PHP_FUNCTION(unserialize)
1037 {
1038 	char *buf = NULL;
1039 	size_t buf_len;
1040 	const unsigned char *p;
1041 	php_unserialize_data_t var_hash;
1042 	zval *options = NULL, *classes = NULL;
1043 	zval *retval;
1044 	HashTable *class_hash = NULL;
1045 
1046 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &options) == FAILURE) {
1047 		RETURN_FALSE;
1048 	}
1049 
1050 	if (buf_len == 0) {
1051 		RETURN_FALSE;
1052 	}
1053 
1054 	p = (const unsigned char*) buf;
1055 	PHP_VAR_UNSERIALIZE_INIT(var_hash);
1056 	if(options != NULL) {
1057 		classes = zend_hash_str_find(Z_ARRVAL_P(options), "allowed_classes", sizeof("allowed_classes")-1);
1058 		if(classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
1059 			ALLOC_HASHTABLE(class_hash);
1060 			zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0);
1061 		}
1062 		if(class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
1063 			zval *entry;
1064 			zend_string *lcname;
1065 
1066 			ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) {
1067 				convert_to_string_ex(entry);
1068 				lcname = zend_string_tolower(Z_STR_P(entry));
1069 				zend_hash_add_empty_element(class_hash, lcname);
1070 		        zend_string_release(lcname);
1071 			} ZEND_HASH_FOREACH_END();
1072 		}
1073 	}
1074 
1075 	retval = var_tmp_var(&var_hash);
1076 	if (!php_var_unserialize_ex(retval, &p, p + buf_len, &var_hash, class_hash)) {
1077 		PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1078 		if (class_hash) {
1079 			zend_hash_destroy(class_hash);
1080 			FREE_HASHTABLE(class_hash);
1081 		}
1082 		if (!EG(exception)) {
1083 			php_error_docref(NULL, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
1084 				(zend_long)((char*)p - buf), buf_len);
1085 		}
1086 		RETURN_FALSE;
1087 	}
1088 
1089 	ZVAL_COPY(return_value, retval);
1090 
1091 	PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1092 	if (class_hash) {
1093 		zend_hash_destroy(class_hash);
1094 		FREE_HASHTABLE(class_hash);
1095 	}
1096 }
1097 /* }}} */
1098 
1099 /* {{{ proto int memory_get_usage([bool real_usage])
1100    Returns the allocated by PHP memory */
PHP_FUNCTION(memory_get_usage)1101 PHP_FUNCTION(memory_get_usage) {
1102 	zend_bool real_usage = 0;
1103 
1104 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &real_usage) == FAILURE) {
1105 		RETURN_FALSE;
1106 	}
1107 
1108 	RETURN_LONG(zend_memory_usage(real_usage));
1109 }
1110 /* }}} */
1111 
1112 /* {{{ proto int memory_get_peak_usage([bool real_usage])
1113    Returns the peak allocated by PHP memory */
PHP_FUNCTION(memory_get_peak_usage)1114 PHP_FUNCTION(memory_get_peak_usage) {
1115 	zend_bool real_usage = 0;
1116 
1117 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &real_usage) == FAILURE) {
1118 		RETURN_FALSE;
1119 	}
1120 
1121 	RETURN_LONG(zend_memory_peak_usage(real_usage));
1122 }
1123 /* }}} */
1124 
1125 /*
1126  * Local variables:
1127  * tab-width: 4
1128  * c-basic-offset: 4
1129  * End:
1130  * vim600: sw=4 ts=4 fdm=marker
1131  * vim<600: sw=4 ts=4
1132  */
1133