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