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