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 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
206 return;
207 }
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 if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
370 return;
371 }
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 smart_str_append_long(buf, Z_LVAL_P(struc));
463 break;
464 case IS_DOUBLE:
465 php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
466 smart_str_appends(buf, tmp_str);
467 /* Without a decimal point, PHP treats a number literal as an int.
468 * This check even works for scientific notation, because the
469 * mantissa always contains a decimal point.
470 * We need to check for finiteness, because INF, -INF and NAN
471 * must not have a decimal point added.
472 */
473 if (zend_finite(Z_DVAL_P(struc)) && NULL == strchr(tmp_str, '.')) {
474 smart_str_appendl(buf, ".0", 2);
475 }
476 break;
477 case IS_STRING:
478 ztmp = php_addcslashes(Z_STR_P(struc), 0, "'\\", 2);
479 ztmp2 = php_str_to_str(ZSTR_VAL(ztmp), ZSTR_LEN(ztmp), "\0", 1, "' . \"\\0\" . '", 12);
480
481 smart_str_appendc(buf, '\'');
482 smart_str_append(buf, ztmp2);
483 smart_str_appendc(buf, '\'');
484
485 zend_string_free(ztmp);
486 zend_string_free(ztmp2);
487 break;
488 case IS_ARRAY:
489 myht = Z_ARRVAL_P(struc);
490 if (ZEND_HASH_APPLY_PROTECTION(myht) && myht->u.v.nApplyCount++ > 0) {
491 myht->u.v.nApplyCount--;
492 smart_str_appendl(buf, "NULL", 4);
493 zend_error(E_WARNING, "var_export does not handle circular references");
494 return;
495 }
496 if (level > 1) {
497 smart_str_appendc(buf, '\n');
498 buffer_append_spaces(buf, level - 1);
499 }
500 smart_str_appendl(buf, "array (\n", 8);
501 ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
502 php_array_element_export(val, index, key, level, buf);
503 } ZEND_HASH_FOREACH_END();
504 if (ZEND_HASH_APPLY_PROTECTION(myht)) {
505 myht->u.v.nApplyCount--;
506 }
507 if (level > 1) {
508 buffer_append_spaces(buf, level - 1);
509 }
510 smart_str_appendc(buf, ')');
511
512 break;
513
514 case IS_OBJECT:
515 myht = Z_OBJPROP_P(struc);
516 if (myht) {
517 if (myht->u.v.nApplyCount > 0) {
518 smart_str_appendl(buf, "NULL", 4);
519 zend_error(E_WARNING, "var_export does not handle circular references");
520 return;
521 } else {
522 myht->u.v.nApplyCount++;
523 }
524 }
525 if (level > 1) {
526 smart_str_appendc(buf, '\n');
527 buffer_append_spaces(buf, level - 1);
528 }
529
530 smart_str_append(buf, Z_OBJCE_P(struc)->name);
531 smart_str_appendl(buf, "::__set_state(array(\n", 21);
532
533 if (myht) {
534 ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
535 php_object_element_export(val, index, key, level, buf);
536 } ZEND_HASH_FOREACH_END();
537 myht->u.v.nApplyCount--;
538 }
539 if (level > 1) {
540 buffer_append_spaces(buf, level - 1);
541 }
542 smart_str_appendl(buf, "))", 2);
543
544 break;
545 case IS_REFERENCE:
546 struc = Z_REFVAL_P(struc);
547 goto again;
548 break;
549 default:
550 smart_str_appendl(buf, "NULL", 4);
551 break;
552 }
553 }
554 /* }}} */
555
556 /* FOR BC reasons, this will always perform and then print */
php_var_export(zval * struc,int level)557 PHPAPI void php_var_export(zval *struc, int level) /* {{{ */
558 {
559 smart_str buf = {0};
560 php_var_export_ex(struc, level, &buf);
561 smart_str_0(&buf);
562 PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
563 smart_str_free(&buf);
564 }
565 /* }}} */
566
567 /* {{{ proto mixed var_export(mixed var [, bool return])
568 Outputs or returns a string representation of a variable */
PHP_FUNCTION(var_export)569 PHP_FUNCTION(var_export)
570 {
571 zval *var;
572 zend_bool return_output = 0;
573 smart_str buf = {0};
574
575 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|b", &var, &return_output) == FAILURE) {
576 return;
577 }
578
579 php_var_export_ex(var, 1, &buf);
580 smart_str_0 (&buf);
581
582 if (return_output) {
583 RETURN_NEW_STR(buf.s);
584 } else {
585 PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
586 smart_str_free(&buf);
587 }
588 }
589 /* }}} */
590
591 static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash);
592
php_add_var_hash(php_serialize_data_t data,zval * var)593 static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var) /* {{{ */
594 {
595 zval *zv;
596 zend_ulong key;
597 zend_bool is_ref = Z_ISREF_P(var);
598
599 data->n += 1;
600
601 if (!is_ref && Z_TYPE_P(var) != IS_OBJECT) {
602 return 0;
603 }
604
605 /* References to objects are treated as if the reference didn't exist */
606 if (is_ref && Z_TYPE_P(Z_REFVAL_P(var)) == IS_OBJECT) {
607 var = Z_REFVAL_P(var);
608 }
609
610 /* Index for the variable is stored using the numeric value of the pointer to
611 * the zend_refcounted struct */
612 key = (zend_ulong) (zend_uintptr_t) Z_COUNTED_P(var);
613 zv = zend_hash_index_find(&data->ht, key);
614
615 if (zv) {
616 /* References are only counted once, undo the data->n increment above */
617 if (is_ref) {
618 data->n -= 1;
619 }
620
621 return Z_LVAL_P(zv);
622 } else {
623 zval zv_n;
624 ZVAL_LONG(&zv_n, data->n);
625 zend_hash_index_add_new(&data->ht, key, &zv_n);
626
627 /* Additionally to the index, we also store the variable, to ensure that it is
628 * not destroyed during serialization and its pointer reused. The variable is
629 * stored at the numeric value of the pointer + 1, which cannot be the location
630 * of another zend_refcounted structure. */
631 zend_hash_index_add_new(&data->ht, key + 1, var);
632 Z_ADDREF_P(var);
633
634 return 0;
635 }
636 }
637 /* }}} */
638
php_var_serialize_long(smart_str * buf,zend_long val)639 static inline void php_var_serialize_long(smart_str *buf, zend_long val) /* {{{ */
640 {
641 smart_str_appendl(buf, "i:", 2);
642 smart_str_append_long(buf, val);
643 smart_str_appendc(buf, ';');
644 }
645 /* }}} */
646
php_var_serialize_string(smart_str * buf,char * str,size_t len)647 static inline void php_var_serialize_string(smart_str *buf, char *str, size_t len) /* {{{ */
648 {
649 smart_str_appendl(buf, "s:", 2);
650 smart_str_append_unsigned(buf, len);
651 smart_str_appendl(buf, ":\"", 2);
652 smart_str_appendl(buf, str, len);
653 smart_str_appendl(buf, "\";", 2);
654 }
655 /* }}} */
656
php_var_serialize_class_name(smart_str * buf,zval * struc)657 static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc) /* {{{ */
658 {
659 PHP_CLASS_ATTRIBUTES;
660
661 PHP_SET_CLASS_ATTRIBUTES(struc);
662 smart_str_appendl(buf, "O:", 2);
663 smart_str_append_unsigned(buf, ZSTR_LEN(class_name));
664 smart_str_appendl(buf, ":\"", 2);
665 smart_str_append(buf, class_name);
666 smart_str_appendl(buf, "\":", 2);
667 PHP_CLEANUP_CLASS_ATTRIBUTES();
668 return incomplete_class;
669 }
670 /* }}} */
671
php_var_serialize_collect_names(HashTable * src,uint32_t count,zend_bool incomplete)672 static HashTable *php_var_serialize_collect_names(HashTable *src, uint32_t count, zend_bool incomplete) /* {{{ */ {
673 zval *val;
674 HashTable *ht;
675 zend_string *key, *name;
676
677 ALLOC_HASHTABLE(ht);
678 zend_hash_init(ht, count, NULL, NULL, 0);
679 ZEND_HASH_FOREACH_STR_KEY_VAL(src, key, val) {
680 if (incomplete && strcmp(ZSTR_VAL(key), MAGIC_MEMBER) == 0) {
681 continue;
682 }
683 if (Z_TYPE_P(val) != IS_STRING) {
684 php_error_docref(NULL, E_NOTICE,
685 "__sleep should return an array only containing the names of instance-variables to serialize.");
686 }
687 name = zval_get_string(val);
688 if (zend_hash_exists(ht, name)) {
689 php_error_docref(NULL, E_NOTICE,
690 "\"%s\" is returned from __sleep multiple times", ZSTR_VAL(name));
691 zend_string_release(name);
692 continue;
693 }
694 zend_hash_add_empty_element(ht, name);
695 zend_string_release(name);
696 } ZEND_HASH_FOREACH_END();
697
698 return ht;
699 }
700 /* }}} */
701
php_var_serialize_class(smart_str * buf,zval * struc,zval * retval_ptr,php_serialize_data_t var_hash)702 static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */
703 {
704 uint32_t count;
705 zend_bool incomplete_class;
706 HashTable *ht;
707
708 incomplete_class = php_var_serialize_class_name(buf, struc);
709 /* count after serializing name, since php_var_serialize_class_name
710 * changes the count if the variable is incomplete class */
711 if (Z_TYPE_P(retval_ptr) == IS_ARRAY) {
712 ht = Z_ARRVAL_P(retval_ptr);
713 count = zend_array_count(ht);
714 } else if (Z_TYPE_P(retval_ptr) == IS_OBJECT) {
715 ht = Z_OBJPROP_P(retval_ptr);
716 count = zend_array_count(ht);
717 if (incomplete_class) {
718 --count;
719 }
720 } else {
721 count = 0;
722 ht = NULL;
723 }
724
725 if (count > 0) {
726 zval *d;
727 zval nval, *nvalp;
728 zend_string *name;
729 HashTable *names, *propers;
730
731 names = php_var_serialize_collect_names(ht, count, incomplete_class);
732
733 smart_str_append_unsigned(buf, zend_hash_num_elements(names));
734 smart_str_appendl(buf, ":{", 2);
735
736 ZVAL_NULL(&nval);
737 nvalp = &nval;
738 propers = Z_OBJPROP_P(struc);
739
740 ZEND_HASH_FOREACH_STR_KEY(names, name) {
741 if ((d = zend_hash_find(propers, name)) != NULL) {
742 if (Z_TYPE_P(d) == IS_INDIRECT) {
743 d = Z_INDIRECT_P(d);
744 if (Z_TYPE_P(d) == IS_UNDEF) {
745 continue;
746 }
747 }
748 php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
749 php_var_serialize_intern(buf, d, var_hash);
750 } else {
751 zend_class_entry *ce = Z_OBJ_P(struc)->ce;
752 if (ce) {
753 zend_string *prot_name, *priv_name;
754
755 do {
756 priv_name = zend_mangle_property_name(
757 ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
758 if ((d = zend_hash_find(propers, priv_name)) != NULL) {
759 if (Z_TYPE_P(d) == IS_INDIRECT) {
760 d = Z_INDIRECT_P(d);
761 if (Z_ISUNDEF_P(d)) {
762 break;
763 }
764 }
765 php_var_serialize_string(buf, ZSTR_VAL(priv_name), ZSTR_LEN(priv_name));
766 zend_string_free(priv_name);
767 php_var_serialize_intern(buf, d, var_hash);
768 break;
769 }
770 zend_string_free(priv_name);
771 prot_name = zend_mangle_property_name(
772 "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
773 if ((d = zend_hash_find(propers, prot_name)) != NULL) {
774 if (Z_TYPE_P(d) == IS_INDIRECT) {
775 d = Z_INDIRECT_P(d);
776 if (Z_TYPE_P(d) == IS_UNDEF) {
777 zend_string_free(prot_name);
778 break;
779 }
780 }
781 php_var_serialize_string(buf, ZSTR_VAL(prot_name), ZSTR_LEN(prot_name));
782 zend_string_free(prot_name);
783 php_var_serialize_intern(buf, d, var_hash);
784 break;
785 }
786 zend_string_free(prot_name);
787 php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
788 php_var_serialize_intern(buf, nvalp, var_hash);
789 php_error_docref(NULL, E_NOTICE,
790 "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
791 } while (0);
792 } else {
793 php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
794 php_var_serialize_intern(buf, nvalp, var_hash);
795 }
796 }
797 } ZEND_HASH_FOREACH_END();
798 smart_str_appendc(buf, '}');
799
800 zend_hash_destroy(names);
801 FREE_HASHTABLE(names);
802 } else {
803 smart_str_appendl(buf, "0:{}", 4);
804 }
805 }
806 /* }}} */
807
php_var_serialize_intern(smart_str * buf,zval * struc,php_serialize_data_t var_hash)808 static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
809 {
810 zend_long var_already;
811 HashTable *myht;
812
813 if (EG(exception)) {
814 return;
815 }
816
817 if (var_hash && (var_already = php_add_var_hash(var_hash, struc))) {
818 if (Z_ISREF_P(struc)) {
819 smart_str_appendl(buf, "R:", 2);
820 smart_str_append_long(buf, var_already);
821 smart_str_appendc(buf, ';');
822 return;
823 } else if (Z_TYPE_P(struc) == IS_OBJECT) {
824 smart_str_appendl(buf, "r:", 2);
825 smart_str_append_long(buf, var_already);
826 smart_str_appendc(buf, ';');
827 return;
828 }
829 }
830
831 again:
832 switch (Z_TYPE_P(struc)) {
833 case IS_FALSE:
834 smart_str_appendl(buf, "b:0;", 4);
835 return;
836
837 case IS_TRUE:
838 smart_str_appendl(buf, "b:1;", 4);
839 return;
840
841 case IS_NULL:
842 smart_str_appendl(buf, "N;", 2);
843 return;
844
845 case IS_LONG:
846 php_var_serialize_long(buf, Z_LVAL_P(struc));
847 return;
848
849 case IS_DOUBLE: {
850 char tmp_str[PHP_DOUBLE_MAX_LENGTH];
851 smart_str_appendl(buf, "d:", 2);
852 php_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
853 smart_str_appends(buf, tmp_str);
854 smart_str_appendc(buf, ';');
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
php_var_serialize_init()1005 PHPAPI php_serialize_data_t php_var_serialize_init() {
1006 struct php_serialize_data *d;
1007 /* fprintf(stderr, "SERIALIZE_INIT == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
1008 if (BG(serialize_lock) || !BG(serialize).level) {
1009 d = emalloc(sizeof(struct php_serialize_data));
1010 zend_hash_init(&d->ht, 16, NULL, ZVAL_PTR_DTOR, 0);
1011 d->n = 0;
1012 if (!BG(serialize_lock)) {
1013 BG(serialize).data = d;
1014 BG(serialize).level = 1;
1015 }
1016 } else {
1017 d = BG(serialize).data;
1018 ++BG(serialize).level;
1019 }
1020 return d;
1021 }
1022
php_var_serialize_destroy(php_serialize_data_t d)1023 PHPAPI void php_var_serialize_destroy(php_serialize_data_t d) {
1024 /* fprintf(stderr, "SERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
1025 if (BG(serialize_lock) || BG(serialize).level == 1) {
1026 zend_hash_destroy(&d->ht);
1027 efree(d);
1028 }
1029 if (!BG(serialize_lock) && !--BG(serialize).level) {
1030 BG(serialize).data = NULL;
1031 }
1032 }
1033
1034 /* {{{ proto string serialize(mixed variable)
1035 Returns a string representation of variable (which can later be unserialized) */
PHP_FUNCTION(serialize)1036 PHP_FUNCTION(serialize)
1037 {
1038 zval *struc;
1039 php_serialize_data_t var_hash;
1040 smart_str buf = {0};
1041
1042 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &struc) == FAILURE) {
1043 return;
1044 }
1045
1046 PHP_VAR_SERIALIZE_INIT(var_hash);
1047 php_var_serialize(&buf, struc, &var_hash);
1048 PHP_VAR_SERIALIZE_DESTROY(var_hash);
1049
1050 if (EG(exception)) {
1051 smart_str_free(&buf);
1052 RETURN_FALSE;
1053 }
1054
1055 if (buf.s) {
1056 RETURN_NEW_STR(buf.s);
1057 } else {
1058 RETURN_NULL();
1059 }
1060 }
1061 /* }}} */
1062
1063 /* {{{ proto mixed unserialize(string variable_representation[, array allowed_classes])
1064 Takes a string representation of variable and recreates it */
PHP_FUNCTION(unserialize)1065 PHP_FUNCTION(unserialize)
1066 {
1067 char *buf = NULL;
1068 size_t buf_len;
1069 const unsigned char *p;
1070 php_unserialize_data_t var_hash;
1071 zval *options = NULL, *classes = NULL;
1072 zval *retval;
1073 HashTable *class_hash = NULL, *prev_class_hash;
1074
1075 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|a", &buf, &buf_len, &options) == FAILURE) {
1076 RETURN_FALSE;
1077 }
1078
1079 if (buf_len == 0) {
1080 RETURN_FALSE;
1081 }
1082
1083 p = (const unsigned char*) buf;
1084 PHP_VAR_UNSERIALIZE_INIT(var_hash);
1085
1086 prev_class_hash = php_var_unserialize_get_allowed_classes(var_hash);
1087 if (options != NULL) {
1088 classes = zend_hash_str_find(Z_ARRVAL_P(options), "allowed_classes", sizeof("allowed_classes")-1);
1089 if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) {
1090 php_error_docref(NULL, E_WARNING, "allowed_classes option should be array or boolean");
1091 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1092 RETURN_FALSE;
1093 }
1094
1095 if(classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
1096 ALLOC_HASHTABLE(class_hash);
1097 zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0);
1098 }
1099 if(class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
1100 zval *entry;
1101 zend_string *lcname;
1102
1103 ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) {
1104 convert_to_string_ex(entry);
1105 lcname = zend_string_tolower(Z_STR_P(entry));
1106 zend_hash_add_empty_element(class_hash, lcname);
1107 zend_string_release(lcname);
1108 } ZEND_HASH_FOREACH_END();
1109 }
1110 php_var_unserialize_set_allowed_classes(var_hash, class_hash);
1111 }
1112
1113 retval = var_tmp_var(&var_hash);
1114 if (!php_var_unserialize(retval, &p, p + buf_len, &var_hash)) {
1115 if (!EG(exception)) {
1116 php_error_docref(NULL, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
1117 (zend_long)((char*)p - buf), buf_len);
1118 }
1119 RETVAL_FALSE;
1120 } else {
1121 ZVAL_COPY(return_value, retval);
1122 }
1123
1124 if (class_hash) {
1125 zend_hash_destroy(class_hash);
1126 FREE_HASHTABLE(class_hash);
1127 }
1128
1129 /* Reset to previous allowed_classes in case this is a nested call */
1130 php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash);
1131 PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1132
1133 /* Per calling convention we must not return a reference here, so unwrap. We're doing this at
1134 * the very end, because __wakeup() calls performed during UNSERIALIZE_DESTROY might affect
1135 * the value we unwrap here. This is compatible with behavior in PHP <=7.0. */
1136 if (Z_ISREF_P(return_value)) {
1137 zend_unwrap_reference(return_value);
1138 }
1139 }
1140 /* }}} */
1141
1142 /* {{{ proto int memory_get_usage([bool real_usage])
1143 Returns the allocated by PHP memory */
PHP_FUNCTION(memory_get_usage)1144 PHP_FUNCTION(memory_get_usage) {
1145 zend_bool real_usage = 0;
1146
1147 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &real_usage) == FAILURE) {
1148 RETURN_FALSE;
1149 }
1150
1151 RETURN_LONG(zend_memory_usage(real_usage));
1152 }
1153 /* }}} */
1154
1155 /* {{{ proto int memory_get_peak_usage([bool real_usage])
1156 Returns the peak allocated by PHP memory */
PHP_FUNCTION(memory_get_peak_usage)1157 PHP_FUNCTION(memory_get_peak_usage) {
1158 zend_bool real_usage = 0;
1159
1160 if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &real_usage) == FAILURE) {
1161 RETURN_FALSE;
1162 }
1163
1164 RETURN_LONG(zend_memory_peak_usage(real_usage));
1165 }
1166 /* }}} */
1167
1168 /*
1169 * Local variables:
1170 * tab-width: 4
1171 * c-basic-offset: 4
1172 * End:
1173 * vim600: sw=4 ts=4 fdm=marker
1174 * vim<600: sw=4 ts=4
1175 */
1176