Coverage Report

Created: 2026-09-14 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/php-src/ext/standard/var.c
Line
Count
Source
1
/*
2
   +----------------------------------------------------------------------+
3
   | Copyright © The PHP Group and Contributors.                          |
4
   +----------------------------------------------------------------------+
5
   | This source file is subject to the Modified BSD License that is      |
6
   | bundled with this package in the file LICENSE, and is available      |
7
   | through the World Wide Web at <https://www.php.net/license/>.        |
8
   |                                                                      |
9
   | SPDX-License-Identifier: BSD-3-Clause                                |
10
   +----------------------------------------------------------------------+
11
   | Authors: Jani Lehtimäki <jkl@njet.net>                               |
12
   |          Thies C. Arntzen <thies@thieso.net>                         |
13
   |          Sascha Schumann <sascha@schumann.cx>                        |
14
   +----------------------------------------------------------------------+
15
*/
16
17
/* {{{ includes */
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <errno.h>
21
#include "php.h"
22
#include "php_string.h"
23
#include "php_var.h"
24
#include "zend_lazy_objects.h"
25
#include "zend_smart_str.h"
26
#include "basic_functions.h"
27
#include "php_incomplete_class.h"
28
#include "zend_enum.h"
29
#include "zend_exceptions.h"
30
#include "zend_types.h"
31
/* }}} */
32
33
struct php_serialize_data {
34
  HashTable ht;
35
  uint32_t n;
36
};
37
38
4.01M
#define COMMON (is_ref ? "&" : "")
39
40
static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
41
1.68M
{
42
1.68M
#ifdef ZEND_CHECK_STACK_LIMIT
43
1.68M
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
44
0
    php_printf("%*cnesting level too deep", level + 1, ' ');
45
0
    return;
46
0
  }
47
1.68M
#endif
48
1.68M
  if (key == NULL) { /* numeric key */
49
1.41M
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
50
1.41M
  } else { /* string key */
51
272k
    php_printf("%*c[\"", level + 1, ' ');
52
272k
    PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
53
272k
    php_printf("\"]=>\n");
54
272k
  }
55
1.68M
  php_var_dump(zv, level + 2);
56
1.68M
}
57
/* }}} */
58
59
static void php_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
60
2.11M
{
61
2.11M
  const char *prop_name, *class_name;
62
63
2.11M
#ifdef ZEND_CHECK_STACK_LIMIT
64
2.11M
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
65
0
    php_printf("%*cnesting level too deep", level + 1, ' ');
66
0
    return;
67
0
  }
68
2.11M
#endif
69
2.11M
  if (key == NULL) { /* numeric key */
70
2.09M
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
71
2.09M
  } else { /* string key */
72
13.5k
    int unmangle = zend_unmangle_property_name(key, &class_name, &prop_name);
73
13.5k
    php_printf("%*c[", level + 1, ' ');
74
75
13.5k
    if (class_name && unmangle == SUCCESS) {
76
1.01k
      if (class_name[0] == '*') {
77
132
        php_printf("\"%s\":protected", prop_name);
78
882
      } else {
79
882
        php_printf("\"%s\":\"%s\":private", prop_name, class_name);
80
882
      }
81
12.5k
    } else {
82
12.5k
      php_printf("\"");
83
12.5k
      PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
84
12.5k
      php_printf("\"");
85
12.5k
    }
86
13.5k
    ZEND_PUTS("]=>\n");
87
13.5k
  }
88
89
2.11M
  if (Z_TYPE_P(zv) == IS_UNDEF) {
90
918
    ZEND_ASSERT(ZEND_TYPE_IS_SET(prop_info->type));
91
918
    zend_string *type_str = zend_type_to_string(prop_info->type);
92
918
    php_printf("%*cuninitialized(%s)\n",
93
918
      level + 1, ' ', ZSTR_VAL(type_str));
94
918
    zend_string_release(type_str);
95
2.11M
  } else {
96
2.11M
    php_var_dump(zv, level + 2);
97
2.11M
  }
98
2.11M
}
99
/* }}} */
100
101
12.3k
static const char *php_var_dump_object_prefix(zend_object *obj) {
102
12.3k
  if (EXPECTED(!zend_object_is_lazy(obj))) {
103
11.3k
    return "";
104
11.3k
  }
105
106
1.01k
  if (zend_object_is_lazy_proxy(obj)) {
107
599
    return "lazy proxy ";
108
599
  }
109
110
411
  return "lazy ghost ";
111
1.01k
}
112
113
PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
114
4.01M
{
115
4.01M
  HashTable *myht;
116
4.01M
  zend_string *class_name;
117
4.01M
  int is_ref = 0;
118
4.01M
  zend_ulong num;
119
4.01M
  zend_string *key;
120
4.01M
  zval *val;
121
4.01M
  uint32_t count;
122
123
4.01M
  if (level > 1) {
124
3.79M
    php_printf("%*c", level - 1, ' ');
125
3.79M
  }
126
127
4.01M
again:
128
4.01M
  switch (Z_TYPE_P(struc)) {
129
11.7k
    case IS_FALSE:
130
11.7k
      php_printf("%sbool(false)\n", COMMON);
131
11.7k
      break;
132
10.2k
    case IS_TRUE:
133
10.2k
      php_printf("%sbool(true)\n", COMMON);
134
10.2k
      break;
135
2.11M
    case IS_NULL:
136
2.11M
      php_printf("%sNULL\n", COMMON);
137
2.11M
      break;
138
976k
    case IS_LONG:
139
976k
      php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
140
976k
      break;
141
315k
    case IS_DOUBLE:
142
315k
      php_printf_unchecked("%sfloat(%.*H)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
143
315k
      break;
144
497k
    case IS_STRING:
145
497k
      php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
146
497k
      PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
147
497k
      PUTS("\"\n");
148
497k
      break;
149
79.6k
    case IS_ARRAY:
150
79.6k
      myht = Z_ARRVAL_P(struc);
151
79.6k
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
152
76.4k
        if (GC_IS_RECURSIVE(myht)) {
153
154
          PUTS("*RECURSION*\n");
154
154
          return;
155
154
        }
156
76.2k
        GC_ADDREF(myht);
157
76.2k
        GC_PROTECT_RECURSION(myht);
158
76.2k
      }
159
79.4k
      count = zend_hash_num_elements(myht);
160
79.4k
      php_printf("%sarray(%d) {\n", COMMON, count);
161
3.45M
      ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
162
3.45M
        php_array_element_dump(val, num, key, level);
163
3.45M
      } ZEND_HASH_FOREACH_END();
164
79.4k
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
165
76.2k
        GC_UNPROTECT_RECURSION(myht);
166
76.2k
        GC_DTOR_NO_REF(myht);
167
76.2k
      }
168
79.4k
      if (level > 1) {
169
11.3k
        php_printf("%*c", level-1, ' ');
170
11.3k
      }
171
79.4k
      PUTS("}\n");
172
79.4k
      break;
173
13.1k
    case IS_OBJECT: {
174
13.1k
      zend_class_entry *ce = Z_OBJCE_P(struc);
175
13.1k
      if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->__debugInfo == NULL) {
176
736
        zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
177
736
        php_printf("%senum(%s::%s)\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval));
178
736
        return;
179
736
      }
180
12.4k
      zend_object *zobj = Z_OBJ_P(struc);
181
12.4k
      uint32_t *guard = zend_get_recursion_guard(zobj);
182
12.4k
      if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
183
255
        PUTS("*RECURSION*\n");
184
255
        return;
185
255
      }
186
12.1k
      ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
187
188
12.1k
      myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
189
12.1k
      if (ce->ce_flags & ZEND_ACC_ENUM) {
190
16
        zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
191
16
        php_printf("%senum(%s::%s) (%d) {\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval), myht ? zend_array_count(myht) : 0);
192
12.1k
      } else {
193
12.1k
        class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
194
12.1k
        const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
195
196
12.1k
        php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
197
12.1k
        zend_string_release_ex(class_name, 0);
198
12.1k
      }
199
200
12.1k
      if (myht) {
201
11.8k
        zend_ulong num;
202
11.8k
        zend_string *key;
203
11.8k
        zval *val;
204
205
4.23M
        ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
206
4.23M
          zend_property_info *prop_info = NULL;
207
208
4.23M
          if (Z_TYPE_P(val) == IS_INDIRECT) {
209
7.64k
            val = Z_INDIRECT_P(val);
210
7.64k
            if (key) {
211
7.64k
              prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
212
7.64k
            }
213
7.64k
          }
214
215
4.23M
          if (!Z_ISUNDEF_P(val) || prop_info) {
216
2.11M
            php_object_property_dump(prop_info, val, num, key, level);
217
2.11M
          }
218
4.23M
        } ZEND_HASH_FOREACH_END();
219
11.8k
        zend_release_properties(myht);
220
11.8k
      }
221
12.1k
      if (level > 1) {
222
3.31k
        php_printf("%*c", level-1, ' ');
223
3.31k
      }
224
12.1k
      PUTS("}\n");
225
12.1k
      ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
226
12.1k
      break;
227
12.4k
    }
228
0
    case IS_RESOURCE: {
229
0
      const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
230
0
      php_printf("%sresource(" ZEND_LONG_FMT ") of type (%s)\n", COMMON, Z_RES_P(struc)->handle, type_name ? type_name : "Unknown");
231
0
      break;
232
12.4k
    }
233
2.66k
    case IS_REFERENCE:
234
      //??? hide references with refcount==1 (for compatibility)
235
2.66k
      if (Z_REFCOUNT_P(struc) > 1) {
236
1.59k
        is_ref = 1;
237
1.59k
      }
238
2.66k
      struc = Z_REFVAL_P(struc);
239
2.66k
      goto again;
240
846
    default:
241
846
      php_printf("%sUNKNOWN:0\n", COMMON);
242
846
      break;
243
4.01M
  }
244
4.01M
}
245
/* }}} */
246
247
/* {{{ Dumps a string representation of variable to output */
248
PHP_FUNCTION(var_dump)
249
199k
{
250
199k
  zval *args;
251
199k
  int argc;
252
199k
  int i;
253
254
599k
  ZEND_PARSE_PARAMETERS_START(1, -1)
255
599k
    Z_PARAM_VARIADIC('+', args, argc)
256
599k
  ZEND_PARSE_PARAMETERS_END();
257
258
407k
  for (i = 0; i < argc; i++) {
259
207k
    php_var_dump(&args[i], 1);
260
207k
  }
261
199k
}
262
/* }}} */
263
264
static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
265
0
{
266
0
#ifdef ZEND_CHECK_STACK_LIMIT
267
0
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
268
0
    php_printf("%*cnesting level too deep", level + 1, ' ');
269
0
    return;
270
0
  }
271
0
#endif
272
0
  if (key == NULL) { /* numeric key */
273
0
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
274
0
  } else { /* string key */
275
0
    php_printf("%*c[\"", level + 1, ' ');
276
0
    PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
277
0
    php_printf("\"]=>\n");
278
0
  }
279
0
  php_debug_zval_dump(zv, level + 2);
280
0
}
281
/* }}} */
282
283
static void zval_object_property_dump(zend_property_info *prop_info, zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
284
138
{
285
138
  const char *prop_name, *class_name;
286
287
138
#ifdef ZEND_CHECK_STACK_LIMIT
288
138
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
289
0
    php_printf("%*cnesting level too deep", level + 1, ' ');
290
0
    return;
291
0
  }
292
138
#endif
293
138
  if (key == NULL) { /* numeric key */
294
0
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
295
138
  } else { /* string key */
296
138
    zend_unmangle_property_name(key, &class_name, &prop_name);
297
138
    php_printf("%*c[", level + 1, ' ');
298
299
138
    if (class_name) {
300
18
      if (class_name[0] == '*') {
301
9
        php_printf("\"%s\":protected", prop_name);
302
9
      } else {
303
9
        php_printf("\"%s\":\"%s\":private", prop_name, class_name);
304
9
      }
305
120
    } else {
306
120
      php_printf("\"%s\"", prop_name);
307
120
    }
308
138
    ZEND_PUTS("]=>\n");
309
138
  }
310
138
  if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) {
311
55
    zend_string *type_str = zend_type_to_string(prop_info->type);
312
55
    php_printf("%*cuninitialized(%s)\n",
313
55
      level + 1, ' ', ZSTR_VAL(type_str));
314
55
    zend_string_release(type_str);
315
83
  } else {
316
83
    php_debug_zval_dump(zv, level + 2);
317
83
  }
318
138
}
319
/* }}} */
320
321
PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
322
325
{
323
325
  HashTable *myht = NULL;
324
325
  zend_string *class_name;
325
325
  zend_ulong index;
326
325
  zend_string *key;
327
325
  zval *val;
328
325
  uint32_t count;
329
325
  char *packed;
330
331
325
  if (level > 1) {
332
83
    php_printf("%*c", level - 1, ' ');
333
83
  }
334
335
325
  switch (Z_TYPE_P(struc)) {
336
0
  case IS_FALSE:
337
0
    PUTS("bool(false)\n");
338
0
    break;
339
0
  case IS_TRUE:
340
0
    PUTS("bool(true)\n");
341
0
    break;
342
28
  case IS_NULL:
343
28
    PUTS("NULL\n");
344
28
    break;
345
22
  case IS_LONG:
346
22
    php_printf("int(" ZEND_LONG_FMT ")\n", Z_LVAL_P(struc));
347
22
    break;
348
0
  case IS_DOUBLE:
349
0
    php_printf_unchecked("float(%.*H)\n", (int) PG(serialize_precision), Z_DVAL_P(struc));
350
0
    break;
351
53
  case IS_STRING:
352
53
    php_printf("string(%zd) \"", Z_STRLEN_P(struc));
353
53
    PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
354
53
    if (Z_REFCOUNTED_P(struc)) {
355
13
      php_printf("\" refcount(%u)\n", Z_REFCOUNT_P(struc));
356
40
    } else {
357
40
      PUTS("\" interned\n");
358
40
    }
359
53
    break;
360
0
  case IS_ARRAY:
361
0
    myht = Z_ARRVAL_P(struc);
362
0
    if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
363
0
      if (GC_IS_RECURSIVE(myht)) {
364
0
        PUTS("*RECURSION*\n");
365
0
        return;
366
0
      }
367
0
      GC_ADDREF(myht);
368
0
      GC_PROTECT_RECURSION(myht);
369
0
    }
370
0
    count = zend_hash_num_elements(myht);
371
0
    packed = HT_IS_PACKED(myht) ? "packed " : "";
372
0
    if (Z_REFCOUNTED_P(struc)) {
373
      /* -1 because of ADDREF above. */
374
0
      php_printf("array(%d) %srefcount(%u){\n", count, packed, Z_REFCOUNT_P(struc) - 1);
375
0
    } else {
376
0
      php_printf("array(%d) %sinterned {\n", count, packed);
377
0
    }
378
0
    ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
379
0
      zval_array_element_dump(val, index, key, level);
380
0
    } ZEND_HASH_FOREACH_END();
381
0
    if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
382
0
      GC_UNPROTECT_RECURSION(myht);
383
0
      GC_DTOR_NO_REF(myht);
384
0
    }
385
0
    if (level > 1) {
386
0
      php_printf("%*c", level - 1, ' ');
387
0
    }
388
0
    PUTS("}\n");
389
0
    break;
390
222
  case IS_OBJECT: {
391
    /* Check if this is already recursing on the object before calling zend_get_properties_for,
392
     * to allow infinite recursion detection to work even if classes return temporary arrays,
393
     * and to avoid the need to update the properties table in place to reflect the state
394
     * if the result won't be used. (https://github.com/php/php-src/issues/8044) */
395
222
    zend_object *zobj = Z_OBJ_P(struc);
396
222
    uint32_t *guard = zend_get_recursion_guard(zobj);
397
222
    if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
398
0
      PUTS("*RECURSION*\n");
399
0
      return;
400
0
    }
401
222
    ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
402
403
222
    myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
404
222
    class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
405
222
    const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
406
407
222
    php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0, Z_REFCOUNT_P(struc));
408
222
    zend_string_release_ex(class_name, 0);
409
222
    if (myht) {
410
442
      ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
411
442
        zend_property_info *prop_info = NULL;
412
413
442
        if (Z_TYPE_P(val) == IS_INDIRECT) {
414
124
          val = Z_INDIRECT_P(val);
415
124
          if (key) {
416
124
            prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
417
124
          }
418
124
        }
419
420
442
        if (!Z_ISUNDEF_P(val) || prop_info) {
421
138
          zval_object_property_dump(prop_info, val, index, key, level);
422
138
        }
423
442
      } ZEND_HASH_FOREACH_END();
424
166
      zend_release_properties(myht);
425
166
    }
426
222
    if (level > 1) {
427
8
      php_printf("%*c", level - 1, ' ');
428
8
    }
429
222
    PUTS("}\n");
430
222
    ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
431
222
    break;
432
222
  }
433
0
  case IS_RESOURCE: {
434
0
    const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc));
435
0
    php_printf("resource(" ZEND_LONG_FMT ") of type (%s) refcount(%u)\n", Z_RES_P(struc)->handle, type_name ? type_name : "Unknown", Z_REFCOUNT_P(struc));
436
0
    break;
437
222
  }
438
0
  case IS_REFERENCE:
439
0
    php_printf("reference refcount(%u) {\n", Z_REFCOUNT_P(struc));
440
0
    php_debug_zval_dump(Z_REFVAL_P(struc), level + 2);
441
0
    if (level > 1) {
442
0
      php_printf("%*c", level - 1, ' ');
443
0
    }
444
0
    PUTS("}\n");
445
0
    break;
446
0
  default:
447
0
    PUTS("UNKNOWN:0\n");
448
0
    break;
449
325
  }
450
325
}
451
/* }}} */
452
453
/* {{{ Dumps a string representation of an internal zend value to output. */
454
PHP_FUNCTION(debug_zval_dump)
455
236
{
456
236
  zval *args;
457
236
  int argc;
458
236
  int i;
459
460
708
  ZEND_PARSE_PARAMETERS_START(1, -1)
461
708
    Z_PARAM_VARIADIC('+', args, argc)
462
708
  ZEND_PARSE_PARAMETERS_END();
463
464
478
  for (i = 0; i < argc; i++) {
465
242
    php_debug_zval_dump(&args[i], 1);
466
242
  }
467
236
}
468
/* }}} */
469
470
#define buffer_append_spaces(buf, num_spaces) \
471
877
  do { \
472
877
    char *tmp_spaces; \
473
877
    size_t tmp_spaces_len; \
474
877
    tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \
475
877
    smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \
476
877
    efree(tmp_spaces); \
477
877
  } while(0);
478
479
static zend_result php_array_element_export(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf) /* {{{ */
480
199
{
481
199
  if (key == NULL) { /* numeric key */
482
62
    buffer_append_spaces(buf, level+1);
483
62
    smart_str_append_long(buf, (zend_long) index);
484
62
    smart_str_appendl(buf, " => ", 4);
485
486
137
  } else { /* string key */
487
137
    zend_string *tmp_str;
488
137
    zend_string *ckey = php_addcslashes(key, "'\\", 2);
489
137
    tmp_str = php_str_to_str(ZSTR_VAL(ckey), ZSTR_LEN(ckey), "\0", 1, "' . \"\\0\" . '", 12);
490
491
137
    buffer_append_spaces(buf, level + 1);
492
493
137
    smart_str_appendc(buf, '\'');
494
137
    smart_str_append(buf, tmp_str);
495
137
    smart_str_appendl(buf, "' => ", 5);
496
497
137
    zend_string_free(ckey);
498
137
    zend_string_free(tmp_str);
499
137
  }
500
199
  zend_result result = php_var_export_ex(zv, level + 2, buf);
501
502
199
  smart_str_appendc(buf, ',');
503
199
  smart_str_appendc(buf, '\n');
504
505
199
  return result;
506
199
}
507
/* }}} */
508
509
static zend_result php_object_element_export(zval *zv, zend_ulong index, zend_string *key, int level, smart_str *buf) /* {{{ */
510
645
{
511
645
  buffer_append_spaces(buf, level + 2);
512
645
  if (key != NULL) {
513
645
    const char *class_name, *prop_name;
514
645
    size_t prop_name_len;
515
645
    zend_string *pname_esc;
516
517
645
    zend_unmangle_property_name_ex(key, &class_name, &prop_name, &prop_name_len);
518
645
    pname_esc = php_addcslashes_str(prop_name, prop_name_len, "'\\", 2);
519
520
645
    smart_str_appendc(buf, '\'');
521
645
    smart_str_append(buf, pname_esc);
522
645
    smart_str_appendc(buf, '\'');
523
645
    zend_string_release_ex(pname_esc, 0);
524
645
  } else {
525
0
    smart_str_append_long(buf, (zend_long) index);
526
0
  }
527
645
  smart_str_appendl(buf, " => ", 4);
528
645
  zend_result result = php_var_export_ex(zv, level + 2, buf);
529
645
  smart_str_appendc(buf, ',');
530
645
  smart_str_appendc(buf, '\n');
531
532
645
  return result;
533
645
}
534
/* }}} */
535
536
PHPAPI zend_result php_var_export_ex(zval *struc, int level, smart_str *buf) /* {{{ */
537
1.28k
{
538
1.28k
  HashTable *myht;
539
1.28k
  zend_string *ztmp, *ztmp2;
540
1.28k
  zend_ulong index;
541
1.28k
  zend_string *key;
542
1.28k
  zval *val;
543
544
1.28k
again:
545
1.28k
  switch (Z_TYPE_P(struc)) {
546
28
    case IS_FALSE:
547
28
      smart_str_appendl(buf, "false", 5);
548
28
      break;
549
19
    case IS_TRUE:
550
19
      smart_str_appendl(buf, "true", 4);
551
19
      break;
552
71
    case IS_NULL:
553
71
      smart_str_appendl(buf, "NULL", 4);
554
71
      break;
555
202
    case IS_LONG:
556
      /* INT_MIN as a literal will be parsed as a float. Emit something like
557
       * -9223372036854775807-1 to avoid this. */
558
202
      if (Z_LVAL_P(struc) == ZEND_LONG_MIN) {
559
0
        smart_str_append_long(buf, ZEND_LONG_MIN+1);
560
0
        smart_str_appends(buf, "-1");
561
0
        break;
562
0
      }
563
202
      smart_str_append_long(buf, Z_LVAL_P(struc));
564
202
      break;
565
17
    case IS_DOUBLE:
566
17
      smart_str_append_double(
567
17
        buf, Z_DVAL_P(struc), (int) PG(serialize_precision), /* zero_fraction */ true);
568
17
      break;
569
628
    case IS_STRING:
570
628
      ztmp = php_addcslashes(Z_STR_P(struc), "'\\", 2);
571
628
      ztmp2 = php_str_to_str(ZSTR_VAL(ztmp), ZSTR_LEN(ztmp), "\0", 1, "' . \"\\0\" . '", 12);
572
573
628
      smart_str_appendc(buf, '\'');
574
628
      smart_str_append(buf, ztmp2);
575
628
      smart_str_appendc(buf, '\'');
576
577
628
      zend_string_free(ztmp);
578
628
      zend_string_free(ztmp2);
579
628
      break;
580
88
    case IS_ARRAY:
581
88
      myht = Z_ARRVAL_P(struc);
582
88
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
583
66
        if (GC_IS_RECURSIVE(myht)) {
584
0
          smart_str_appendl(buf, "NULL", 4);
585
0
          zend_error(E_WARNING, "var_export does not handle circular references");
586
0
          return SUCCESS;
587
0
        }
588
66
        GC_ADDREF(myht);
589
66
        GC_PROTECT_RECURSION(myht);
590
66
      }
591
88
      if (level > 1) {
592
12
        smart_str_appendc(buf, '\n');
593
12
        buffer_append_spaces(buf, level - 1);
594
12
      }
595
88
      smart_str_appendl(buf, "array (\n", 8);
596
495
      ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
597
495
        if (php_array_element_export(val, index, key, level, buf) == FAILURE) {
598
0
          if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
599
0
            GC_UNPROTECT_RECURSION(myht);
600
0
            GC_DELREF(myht);
601
0
          }
602
0
          return FAILURE;
603
0
        }
604
495
      } ZEND_HASH_FOREACH_END();
605
88
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
606
66
        GC_UNPROTECT_RECURSION(myht);
607
66
        GC_DELREF(myht);
608
66
      }
609
88
      if (level > 1) {
610
12
        buffer_append_spaces(buf, level - 1);
611
12
      }
612
88
      smart_str_appendc(buf, ')');
613
614
88
      break;
615
616
235
    case IS_OBJECT: {
617
      /* Check if this is already recursing on the object before calling zend_get_properties_for,
618
       * to allow infinite recursion detection to work even if classes return temporary arrays,
619
       * and to avoid the need to update the properties table in place to reflect the state
620
       * if the result won't be used. (https://github.com/php/php-src/issues/8044) */
621
235
      zend_object *zobj = Z_OBJ_P(struc);
622
235
      uint32_t *guard = zend_get_recursion_guard(zobj);
623
235
      if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, EXPORT, zobj)) {
624
0
        smart_str_appendl(buf, "NULL", 4);
625
0
        zend_error(E_WARNING, "var_export does not handle circular references");
626
0
        return SUCCESS;
627
0
      }
628
235
      ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, EXPORT, zobj);
629
235
      myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_VAR_EXPORT);
630
235
      if (level > 1) {
631
8
        smart_str_appendc(buf, '\n');
632
8
        buffer_append_spaces(buf, level - 1);
633
8
      }
634
635
235
      zend_class_entry *ce = Z_OBJCE_P(struc);
636
235
      bool is_enum = ce->ce_flags & ZEND_ACC_ENUM;
637
638
      /* stdClass has no __set_state method, but can be casted to */
639
235
      if (ce == zend_standard_class_def) {
640
0
        smart_str_appendl(buf, "(object) array(\n", 16);
641
235
      } else {
642
235
        smart_str_appendc(buf, '\\');
643
235
        smart_str_append(buf, ce->name);
644
235
        if (is_enum) {
645
48
          zend_object *zobj = Z_OBJ_P(struc);
646
48
          zval *case_name_zval = zend_enum_fetch_case_name(zobj);
647
48
          smart_str_appendl(buf, "::", 2);
648
48
          smart_str_append(buf, Z_STR_P(case_name_zval));
649
187
        } else {
650
187
          smart_str_appendl(buf, "::__set_state(array(\n", 21);
651
187
        }
652
235
      }
653
654
235
      if (myht) {
655
179
        if (!is_enum) {
656
1.61k
          ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
657
            /* data is IS_PTR for properties with hooks. */
658
1.61k
            zval tmp;
659
1.61k
            if (UNEXPECTED(Z_TYPE_P(val) == IS_PTR)) {
660
569
              zend_property_info *prop_info = Z_PTR_P(val);
661
569
              if ((prop_info->flags & ZEND_ACC_VIRTUAL) && !prop_info->hooks[ZEND_PROPERTY_HOOK_GET]) {
662
89
                continue;
663
89
              }
664
480
              const char *unmangled_name_cstr = zend_get_unmangled_property_name(prop_info->name);
665
480
              zend_string *unmangled_name = zend_string_init(unmangled_name_cstr, strlen(unmangled_name_cstr), false);
666
480
              val = zend_read_property_ex(prop_info->ce, zobj, unmangled_name, /* silent */ true, &tmp);
667
480
              zend_string_release_ex(unmangled_name, false);
668
480
              if (EG(exception)) {
669
9
                ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
670
9
                zend_release_properties(myht);
671
9
                return FAILURE;
672
9
              }
673
480
            }
674
645
            php_object_element_export(val, index, key, level, buf);
675
645
            if (val == &tmp) {
676
459
              zval_ptr_dtor(val);
677
459
            }
678
645
          } ZEND_HASH_FOREACH_END();
679
131
        }
680
170
        zend_release_properties(myht);
681
170
      }
682
226
      ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
683
226
      if (level > 1 && !is_enum) {
684
1
        buffer_append_spaces(buf, level - 1);
685
1
      }
686
226
      if (ce == zend_standard_class_def) {
687
0
        smart_str_appendc(buf, ')');
688
226
      } else if (!is_enum) {
689
178
        smart_str_appendl(buf, "))", 2);
690
178
      }
691
692
226
      break;
693
235
    }
694
0
    case IS_REFERENCE:
695
0
      struc = Z_REFVAL_P(struc);
696
0
      goto again;
697
0
    default:
698
0
      smart_str_appendl(buf, "NULL", 4);
699
0
      break;
700
1.28k
  }
701
702
1.27k
  return SUCCESS;
703
1.28k
}
704
/* }}} */
705
706
/* FOR BC reasons, this will always perform and then print */
707
PHPAPI void php_var_export(zval *struc, int level) /* {{{ */
708
0
{
709
0
  smart_str buf = {0};
710
0
  zend_result result = php_var_export_ex(struc, level, &buf);
711
0
  smart_str_0(&buf);
712
0
  if (result == SUCCESS) {
713
0
    PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
714
0
  }
715
0
  smart_str_free(&buf);
716
0
}
717
/* }}} */
718
719
/* {{{ Outputs or returns a string representation of a variable */
720
PHP_FUNCTION(var_export)
721
444
{
722
444
  zval *var;
723
444
  bool return_output = 0;
724
444
  smart_str buf = {0};
725
726
1.33k
  ZEND_PARSE_PARAMETERS_START(1, 2)
727
1.77k
    Z_PARAM_ZVAL(var)
728
1.77k
    Z_PARAM_OPTIONAL
729
1.77k
    Z_PARAM_BOOL(return_output)
730
444
  ZEND_PARSE_PARAMETERS_END();
731
732
444
  zend_result result = php_var_export_ex(var, 1, &buf);
733
444
  smart_str_0 (&buf);
734
735
444
  if (result == FAILURE) {
736
9
    smart_str_free(&buf);
737
435
  } else if (return_output) {
738
108
    RETURN_STR(smart_str_extract(&buf));
739
327
  } else {
740
327
    PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
741
327
    smart_str_free(&buf);
742
327
  }
743
444
}
744
/* }}} */
745
746
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root);
747
748
/**
749
 * @param bool in_rcn_array Whether the element appears in a potentially nested array with RC > 1.
750
 */
751
static inline zend_long php_add_var_hash(php_serialize_data_t data, zval *var, bool in_rcn_array) /* {{{ */
752
53.8k
{
753
53.8k
  zval *zv;
754
53.8k
  zend_ulong key;
755
53.8k
  bool is_ref = Z_ISREF_P(var);
756
757
53.8k
  data->n += 1;
758
759
53.8k
  if (is_ref) {
760
    /* pass */
761
46.4k
  } else if (Z_TYPE_P(var) != IS_OBJECT) {
762
45.8k
    return 0;
763
45.8k
  } else if (!in_rcn_array
764
556
   && Z_REFCOUNT_P(var) == 1
765
104
   && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)
766
   /* __serialize and __sleep may arbitrarily increase the refcount */
767
104
   && Z_OBJCE_P(var)->__serialize == NULL
768
101
   && zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) {
769
101
    return 0;
770
101
  }
771
772
  /* References to objects are treated as if the reference didn't exist */
773
7.88k
  if (is_ref && Z_TYPE_P(Z_REFVAL_P(var)) == IS_OBJECT) {
774
0
    var = Z_REFVAL_P(var);
775
0
  }
776
777
  /* Index for the variable is stored using the numeric value of the pointer to
778
   * the zend_refcounted struct */
779
7.88k
  key = (zend_ulong) (uintptr_t) Z_COUNTED_P(var);
780
7.88k
  zv = zend_hash_index_find(&data->ht, key);
781
782
7.88k
  if (zv) {
783
    /* References are only counted once, undo the data->n increment above */
784
4.76k
    if (is_ref && Z_LVAL_P(zv) != -1) {
785
4.72k
      data->n -= 1;
786
4.72k
    }
787
788
4.76k
    return Z_LVAL_P(zv);
789
4.76k
  } else {
790
3.12k
    zval zv_n;
791
3.12k
    ZVAL_LONG(&zv_n, data->n);
792
3.12k
    zend_hash_index_add_new(&data->ht, key, &zv_n);
793
794
    /* Additionally to the index, we also store the variable, to ensure that it is
795
     * not destroyed during serialization and its pointer reused. The variable is
796
     * stored at the numeric value of the pointer + 1, which cannot be the location
797
     * of another zend_refcounted structure. */
798
3.12k
    zend_hash_index_add_new(&data->ht, key + 1, var);
799
3.12k
    Z_ADDREF_P(var);
800
801
3.12k
    return 0;
802
3.12k
  }
803
7.88k
}
804
/* }}} */
805
806
static inline void php_var_serialize_long(smart_str *buf, zend_long val) /* {{{ */
807
26.4k
{
808
26.4k
  char b[32];
809
26.4k
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, val);
810
26.4k
  size_t l = b + sizeof(b) - 1 - s;
811
26.4k
  char *res = smart_str_extend(buf, 2 + l + 1);
812
26.4k
  res = zend_mempcpy(res, "i:", 2);
813
26.4k
  memcpy(res, s, l);
814
26.4k
  res[l] = ';';
815
26.4k
}
816
/* }}} */
817
818
static inline void php_var_serialize_string(smart_str *buf, char *str, size_t len) /* {{{ */
819
38.1k
{
820
38.1k
  char b[32];
821
38.1k
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, len);
822
38.1k
  size_t l = b + sizeof(b) - 1 - s;
823
38.1k
  char *res = smart_str_extend(buf, 2 + l + 2 + len + 2);
824
38.1k
  res = zend_mempcpy(res, "s:", 2);
825
38.1k
  res = zend_mempcpy(res, s, l);
826
38.1k
  res = zend_mempcpy(res, ":\"", 2);
827
38.1k
  res = zend_mempcpy(res, str, len);
828
38.1k
  memcpy(res, "\";", 2);
829
38.1k
}
830
/* }}} */
831
832
static inline bool php_var_serialize_class_name(smart_str *buf, zval *struc) /* {{{ */
833
480
{
834
480
  char b[32];
835
480
  PHP_CLASS_ATTRIBUTES;
836
837
480
  PHP_SET_CLASS_ATTRIBUTES(struc);
838
480
  size_t class_name_len = ZSTR_LEN(class_name);
839
480
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, class_name_len);
840
480
  size_t l = b + sizeof(b) - 1 - s;
841
480
  char *res = smart_str_extend(buf, 2 + l + 2 + class_name_len + 2);
842
480
  res = zend_mempcpy(res, "O:", 2);
843
480
  res = zend_mempcpy(res, s, l);
844
480
  res = zend_mempcpy(res, ":\"", 2);
845
480
  res = zend_mempcpy(res, ZSTR_VAL(class_name), class_name_len);
846
480
  memcpy(res, "\":", 2);
847
480
  PHP_CLEANUP_CLASS_ATTRIBUTES();
848
480
  return incomplete_class;
849
480
}
850
/* }}} */
851
852
static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function *fn) /* {{{ */
853
69
{
854
69
  zval retval;
855
856
69
  BG(serialize_lock)++;
857
69
  zend_call_known_instance_method(fn, obj, &retval, /* param_count */ 0, /* params */ NULL);
858
69
  BG(serialize_lock)--;
859
860
69
  if (Z_ISUNDEF(retval) || EG(exception)) {
861
6
    zval_ptr_dtor(&retval);
862
6
    return NULL;
863
6
  }
864
865
63
  if (Z_TYPE(retval) != IS_ARRAY) {
866
2
    zval_ptr_dtor(&retval);
867
2
    php_error_docref(NULL, E_WARNING, "%s::__sleep() should return an array only containing the names of instance-variables to serialize", ZSTR_VAL(obj->ce->name));
868
2
    return NULL;
869
2
  }
870
871
61
  return Z_ARRVAL(retval);
872
63
}
873
/* }}} */
874
875
static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{{ */
876
109
{
877
109
  BG(serialize_lock)++;
878
109
  zend_call_known_instance_method_with_0_params(
879
109
    Z_OBJCE_P(obj)->__serialize, Z_OBJ_P(obj), retval);
880
109
  BG(serialize_lock)--;
881
882
109
  if (EG(exception)) {
883
2
    zval_ptr_dtor(retval);
884
2
    return FAILURE;
885
2
  }
886
887
107
  if (Z_TYPE_P(retval) != IS_ARRAY) {
888
0
    zval_ptr_dtor(retval);
889
0
    zend_type_error("%s::__serialize() must return an array", ZSTR_VAL(Z_OBJCE_P(obj)->name));
890
0
    return FAILURE;
891
0
  }
892
893
107
  return SUCCESS;
894
107
}
895
/* }}} */
896
897
static int php_var_serialize_try_add_sleep_prop(
898
    HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name, zval *struc) /* {{{ */
899
89
{
900
89
  zval *val = zend_hash_find(props, name);
901
89
  if (val == NULL) {
902
31
    return FAILURE;
903
31
  }
904
905
58
  if (Z_TYPE_P(val) == IS_INDIRECT) {
906
58
    val = Z_INDIRECT_P(val);
907
58
    if (Z_TYPE_P(val) == IS_UNDEF) {
908
29
      zend_property_info *info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
909
29
      if (info) {
910
29
        return SUCCESS;
911
29
      }
912
0
      return FAILURE;
913
29
    }
914
58
  }
915
916
29
  if (!zend_hash_add(ht, name, val)) {
917
0
    php_error_docref(NULL, E_WARNING,
918
0
      "\"%s\" is returned from __sleep() multiple times", ZSTR_VAL(error_name));
919
0
    return SUCCESS;
920
0
  }
921
922
29
  Z_TRY_ADDREF_P(val);
923
29
  return SUCCESS;
924
29
}
925
/* }}} */
926
927
static int php_var_serialize_get_sleep_props(
928
    HashTable *ht, zval *struc, HashTable *sleep_retval) /* {{{ */
929
61
{
930
61
  zend_class_entry *ce = Z_OBJCE_P(struc);
931
61
  HashTable *props = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
932
61
  zval *name_val;
933
61
  int retval = SUCCESS;
934
935
61
  zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0);
936
  /* TODO: Rewrite this by fetching the property info instead of trying out different
937
   * name manglings? */
938
201
  ZEND_HASH_FOREACH_VAL_IND(sleep_retval, name_val) {
939
201
    zend_string *name, *tmp_name, *priv_name, *prot_name;
940
941
201
    ZVAL_DEREF(name_val);
942
201
    if (Z_TYPE_P(name_val) != IS_STRING) {
943
0
      php_error_docref(NULL, E_WARNING,
944
0
          "%s::__sleep() should return an array only containing the names of instance-variables to serialize",
945
0
          ZSTR_VAL(ce->name));
946
0
    }
947
948
201
    name = zval_get_tmp_string(name_val, &tmp_name);
949
201
    if (php_var_serialize_try_add_sleep_prop(ht, props, name, name, struc) == SUCCESS) {
950
49
      zend_tmp_string_release(tmp_name);
951
49
      continue;
952
49
    }
953
954
21
    if (EG(exception)) {
955
7
      zend_tmp_string_release(tmp_name);
956
7
      retval = FAILURE;
957
7
      break;
958
7
    }
959
960
14
    priv_name = zend_mangle_property_name(
961
14
      ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
962
14
      ZSTR_VAL(name), ZSTR_LEN(name), ce->type == ZEND_INTERNAL_CLASS);
963
14
    if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name, struc) == SUCCESS) {
964
9
      zend_tmp_string_release(tmp_name);
965
9
      zend_string_release(priv_name);
966
9
      continue;
967
9
    }
968
5
    zend_string_release(priv_name);
969
970
5
    if (EG(exception)) {
971
0
      zend_tmp_string_release(tmp_name);
972
0
      retval = FAILURE;
973
0
      break;
974
0
    }
975
976
5
    prot_name = zend_mangle_property_name(
977
5
      "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type == ZEND_INTERNAL_CLASS);
978
5
    if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name, struc) == SUCCESS) {
979
0
      zend_tmp_string_release(tmp_name);
980
0
      zend_string_release(prot_name);
981
0
      continue;
982
0
    }
983
5
    zend_string_release(prot_name);
984
985
5
    if (EG(exception)) {
986
0
      zend_tmp_string_release(tmp_name);
987
0
      retval = FAILURE;
988
0
      break;
989
0
    }
990
991
5
    php_error_docref(NULL, E_WARNING,
992
5
      "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
993
5
    zend_tmp_string_release(tmp_name);
994
5
  } ZEND_HASH_FOREACH_END();
995
996
61
  zend_release_properties(props);
997
61
  return retval;
998
61
}
999
/* }}} */
1000
1001
static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable *ht, uint32_t count, bool incomplete_class, php_serialize_data_t var_hash, bool in_rcn_array) /* {{{ */
1002
29.8k
{
1003
29.8k
  smart_str_append_unsigned(buf, count);
1004
29.8k
  smart_str_appendl(buf, ":{", 2);
1005
29.8k
  if (count > 0) {
1006
7.13k
    zend_string *key;
1007
7.13k
    zval *data;
1008
7.13k
    zend_ulong index;
1009
1010
108k
    ZEND_HASH_FOREACH_KEY_VAL_IND(ht, index, key, data) {
1011
108k
      if (incomplete_class && zend_string_equals_literal(key, MAGIC_MEMBER)) {
1012
0
        incomplete_class = false;
1013
0
        continue;
1014
0
      }
1015
1016
50.7k
      if (!key) {
1017
17.8k
        php_var_serialize_long(buf, index);
1018
32.8k
      } else {
1019
32.8k
        php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
1020
32.8k
      }
1021
1022
50.7k
      if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
1023
171
        data = Z_REFVAL_P(data);
1024
171
      }
1025
1026
      /* we should still add element even if it's not OK,
1027
       * since we already wrote the length of the array before */
1028
50.7k
      if (Z_TYPE_P(data) == IS_ARRAY) {
1029
26.8k
        if (UNEXPECTED(Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))) {
1030
0
          php_add_var_hash(var_hash, struc, in_rcn_array);
1031
0
          smart_str_appendl(buf, "N;", 2);
1032
26.8k
        } else {
1033
26.8k
          php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
1034
26.8k
        }
1035
26.8k
      } else {
1036
23.9k
        php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
1037
23.9k
      }
1038
50.7k
    } ZEND_HASH_FOREACH_END();
1039
7.13k
  }
1040
29.8k
  smart_str_appendc(buf, '}');
1041
29.8k
}
1042
/* }}} */
1043
1044
static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht, php_serialize_data_t var_hash) /* {{{ */
1045
61
{
1046
61
  HashTable props;
1047
1048
61
  if (php_var_serialize_get_sleep_props(&props, struc, ht) == SUCCESS) {
1049
54
    php_var_serialize_class_name(buf, struc);
1050
54
    php_var_serialize_nested_data(
1051
54
      buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ false, var_hash,
1052
54
      GC_REFCOUNT(&props) > 1);
1053
54
  }
1054
61
  zend_hash_destroy(&props);
1055
61
}
1056
/* }}} */
1057
1058
static zend_always_inline bool php_serialize_check_stack_limit(void)
1059
53.8k
{
1060
53.8k
#ifdef ZEND_CHECK_STACK_LIMIT
1061
53.8k
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
1062
0
    zend_call_stack_size_error();
1063
0
    return true;
1064
0
  }
1065
53.8k
#endif
1066
53.8k
  return false;
1067
53.8k
}
1068
1069
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash, bool in_rcn_array, bool is_root) /* {{{ */
1070
53.8k
{
1071
53.8k
  zend_long var_already;
1072
53.8k
  HashTable *myht;
1073
1074
53.8k
  if (EG(exception)) {
1075
0
    return;
1076
0
  }
1077
1078
53.8k
  if (UNEXPECTED(php_serialize_check_stack_limit())) {
1079
0
    return;
1080
0
  }
1081
1082
53.8k
  if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
1083
4.76k
    if (var_already == -1) {
1084
      /* Reference to an object that failed to serialize, replace with null. */
1085
0
      smart_str_appendl(buf, "N;", 2);
1086
0
      return;
1087
4.76k
    } else if (Z_ISREF_P(struc)) {
1088
4.72k
      smart_str_appendl(buf, "R:", 2);
1089
4.72k
      smart_str_append_long(buf, var_already);
1090
4.72k
      smart_str_appendc(buf, ';');
1091
4.72k
      return;
1092
4.72k
    } else if (Z_TYPE_P(struc) == IS_OBJECT) {
1093
36
      smart_str_appendl(buf, "r:", 2);
1094
36
      smart_str_append_long(buf, var_already);
1095
36
      smart_str_appendc(buf, ';');
1096
36
      return;
1097
36
    }
1098
4.76k
  }
1099
1100
51.7k
again:
1101
51.7k
  switch (Z_TYPE_P(struc)) {
1102
229
    case IS_FALSE:
1103
229
      smart_str_appendl(buf, "b:0;", 4);
1104
229
      return;
1105
1106
685
    case IS_TRUE:
1107
685
      smart_str_appendl(buf, "b:1;", 4);
1108
685
      return;
1109
1110
4.44k
    case IS_NULL:
1111
4.44k
      smart_str_appendl(buf, "N;", 2);
1112
4.44k
      return;
1113
1114
8.49k
    case IS_LONG:
1115
8.49k
      php_var_serialize_long(buf, Z_LVAL_P(struc));
1116
8.49k
      return;
1117
1118
600
    case IS_DOUBLE: {
1119
600
      char tmp_str[ZEND_DOUBLE_MAX_LENGTH];
1120
600
      zend_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
1121
1122
600
      size_t len = strlen(tmp_str);
1123
600
      char *res = smart_str_extend(buf, 2 + len + 1);
1124
600
      res = zend_mempcpy(res, "d:", 2);
1125
600
      memcpy(res, tmp_str, len);
1126
600
      res[len] = ';';
1127
600
      return;
1128
0
    }
1129
1130
4.45k
    case IS_STRING:
1131
4.45k
      php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
1132
4.45k
      return;
1133
1134
569
    case IS_OBJECT: {
1135
569
        zend_class_entry *ce = Z_OBJCE_P(struc);
1136
569
        bool incomplete_class;
1137
569
        uint32_t count;
1138
1139
569
        if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1140
25
          zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed",
1141
25
            ZSTR_VAL(ce->name));
1142
25
          return;
1143
25
        }
1144
1145
544
        if (ce->ce_flags & ZEND_ACC_ENUM) {
1146
30
          PHP_CLASS_ATTRIBUTES;
1147
1148
30
          zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
1149
1150
30
          PHP_SET_CLASS_ATTRIBUTES(struc);
1151
30
          smart_str_appendl(buf, "E:", 2);
1152
30
          smart_str_append_unsigned(buf, ZSTR_LEN(class_name) + strlen(":") + Z_STRLEN_P(case_name_zval));
1153
30
          smart_str_appendl(buf, ":\"", 2);
1154
30
          smart_str_append(buf, class_name);
1155
30
          smart_str_appendc(buf, ':');
1156
30
          smart_str_append(buf, Z_STR_P(case_name_zval));
1157
30
          smart_str_appendl(buf, "\";", 2);
1158
30
          PHP_CLEANUP_CLASS_ATTRIBUTES();
1159
30
          return;
1160
30
        }
1161
1162
514
        if (ce->__serialize) {
1163
109
          zval retval, obj;
1164
109
          zend_string *key;
1165
109
          zval *data;
1166
109
          zend_ulong index;
1167
1168
109
          ZVAL_OBJ_COPY(&obj, Z_OBJ_P(struc));
1169
109
          if (php_var_serialize_call_magic_serialize(&retval, &obj) == FAILURE) {
1170
2
            if (!EG(exception)) {
1171
0
              smart_str_appendl(buf, "N;", 2);
1172
0
            }
1173
2
            zval_ptr_dtor(&obj);
1174
2
            return;
1175
2
          }
1176
1177
107
          php_var_serialize_class_name(buf, &obj);
1178
107
          smart_str_append_unsigned(buf, zend_hash_num_elements(Z_ARRVAL(retval)));
1179
107
          smart_str_appendl(buf, ":{", 2);
1180
423
          ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(retval), index, key, data) {
1181
423
            if (!key) {
1182
106
              php_var_serialize_long(buf, index);
1183
106
            } else {
1184
52
              php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
1185
52
            }
1186
1187
423
            if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
1188
0
              data = Z_REFVAL_P(data);
1189
0
            }
1190
423
            php_var_serialize_intern(buf, data, var_hash, Z_REFCOUNT(retval) > 1, false);
1191
423
          } ZEND_HASH_FOREACH_END();
1192
107
          smart_str_appendc(buf, '}');
1193
1194
107
          zval_ptr_dtor(&obj);
1195
107
          zval_ptr_dtor(&retval);
1196
107
          return;
1197
109
        }
1198
1199
405
        if (ce->serialize != NULL) {
1200
          /* has custom handler */
1201
17
          unsigned char *serialized_data = NULL;
1202
17
          size_t serialized_length;
1203
1204
17
          if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) {
1205
10
            char b1[32], b2[32];
1206
10
            char *s1 = zend_print_long_to_buf(b1 + sizeof(b1) - 1, ZSTR_LEN(Z_OBJCE_P(struc)->name));
1207
10
            size_t l1 = b1 + sizeof(b1) - 1 - s1;
1208
10
            char *s2 = zend_print_long_to_buf(b2 + sizeof(b2) - 1, serialized_length);
1209
10
            size_t l2 = b2 + sizeof(b2) - 1 - s2;
1210
10
            char *res = smart_str_extend(buf, 2 + l1 + 2 + ZSTR_LEN(Z_OBJCE_P(struc)->name) + 2 + l2 + 2 + serialized_length + 1);
1211
10
            res = zend_mempcpy(res, "C:", 2);
1212
10
            res = zend_mempcpy(res, s1, l1);
1213
10
            res = zend_mempcpy(res, ":\"", 2);
1214
10
            res = zend_mempcpy(res, ZSTR_VAL(Z_OBJCE_P(struc)->name), ZSTR_LEN(Z_OBJCE_P(struc)->name));
1215
10
            res = zend_mempcpy(res, "\":", 2);
1216
10
            res = zend_mempcpy(res, s2, l2);
1217
10
            res = zend_mempcpy(res, ":{", 2);
1218
10
            memcpy(res, (char *) serialized_data, serialized_length);
1219
10
            res[serialized_length] = '}';
1220
10
          } else {
1221
            /* Mark this value in the var_hash, to avoid creating references to it. */
1222
7
            zval *var_idx = zend_hash_index_find(&var_hash->ht,
1223
7
              (zend_ulong) (uintptr_t) Z_COUNTED_P(struc));
1224
7
            if (var_idx) {
1225
0
              ZVAL_LONG(var_idx, -1);
1226
0
            }
1227
7
            smart_str_appendl(buf, "N;", 2);
1228
7
          }
1229
17
          if (serialized_data) {
1230
10
            efree(serialized_data);
1231
10
          }
1232
17
          return;
1233
17
        }
1234
1235
388
        if (ce != PHP_IC_ENTRY) {
1236
388
          zval *zv = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP));
1237
1238
388
          if (zv) {
1239
69
            HashTable *ht;
1240
69
            zval tmp;
1241
1242
69
            ZVAL_OBJ_COPY(&tmp, Z_OBJ_P(struc));
1243
69
            if (!(ht = php_var_serialize_call_sleep(Z_OBJ(tmp), Z_FUNC_P(zv)))) {
1244
8
              if (!EG(exception)) {
1245
                /* we should still add element even if it's not OK,
1246
                 * since we already wrote the length of the array before */
1247
2
                smart_str_appendl(buf, "N;", 2);
1248
2
              }
1249
8
              OBJ_RELEASE(Z_OBJ(tmp));
1250
8
              return;
1251
8
            }
1252
1253
61
            php_var_serialize_class(buf, &tmp, ht, var_hash);
1254
61
            zend_array_release(ht);
1255
61
            OBJ_RELEASE(Z_OBJ(tmp));
1256
61
            return;
1257
69
          }
1258
388
        }
1259
1260
319
        incomplete_class = php_var_serialize_class_name(buf, struc);
1261
1262
319
        if (Z_OBJ_P(struc)->properties == NULL
1263
246
         && Z_OBJ_HT_P(struc)->get_properties_for == NULL
1264
246
         && Z_OBJ_HT_P(struc)->get_properties == zend_std_get_properties
1265
246
         && !zend_object_is_lazy(Z_OBJ_P(struc))) {
1266
          /* Optimized version without rebuilding properties HashTable */
1267
175
          zend_object *obj = Z_OBJ_P(struc);
1268
175
          zend_class_entry *ce = obj->ce;
1269
175
          zend_property_info *prop_info;
1270
175
          zval *prop;
1271
175
          int i;
1272
1273
175
          count = ce->default_properties_count;
1274
972
          for (i = 0; i < ce->default_properties_count; i++) {
1275
797
            prop_info = ce->properties_info_table[i];
1276
797
            if (!prop_info) {
1277
5
              count--;
1278
5
              continue;
1279
5
            }
1280
792
            prop = OBJ_PROP(obj, prop_info->offset);
1281
792
            if (Z_TYPE_P(prop) == IS_UNDEF) {
1282
1
              count--;
1283
1
              continue;
1284
1
            }
1285
792
          }
1286
175
          if (count) {
1287
165
            smart_str_append_unsigned(buf, count);
1288
165
            smart_str_appendl(buf, ":{", 2);
1289
961
            for (i = 0; i < ce->default_properties_count; i++) {
1290
796
              prop_info = ce->properties_info_table[i];
1291
796
              if (!prop_info) {
1292
5
                continue;
1293
5
              }
1294
791
              prop = OBJ_PROP(obj, prop_info->offset);
1295
791
              if (Z_TYPE_P(prop) == IS_UNDEF) {
1296
0
                continue;
1297
0
              }
1298
1299
791
              php_var_serialize_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name));
1300
1301
791
              if (Z_ISREF_P(prop) && Z_REFCOUNT_P(prop) == 1) {
1302
0
                prop = Z_REFVAL_P(prop);
1303
0
              }
1304
1305
791
              php_var_serialize_intern(buf, prop, var_hash, false, false);
1306
791
            }
1307
165
            smart_str_appendc(buf, '}');
1308
165
          } else {
1309
10
            smart_str_appendl(buf, "0:{}", 4);
1310
10
          }
1311
175
          return;
1312
175
        }
1313
144
        myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
1314
        /* count after serializing name, since php_var_serialize_class_name
1315
         * changes the count if the variable is incomplete class */
1316
144
        count = zend_array_count(myht);
1317
144
        if (count > 0 && incomplete_class) {
1318
0
          --count;
1319
0
        }
1320
144
        php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash, GC_REFCOUNT(myht) > 1);
1321
144
        zend_release_properties(myht);
1322
144
        return;
1323
319
      }
1324
29.6k
    case IS_ARRAY: {
1325
29.6k
      smart_str_appendl(buf, "a:", 2);
1326
29.6k
      myht = Z_ARRVAL_P(struc);
1327
29.6k
      bool rcn = !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1);
1328
29.6k
      GC_TRY_ADDREF(myht);
1329
29.6k
      php_var_serialize_nested_data(
1330
29.6k
        buf, struc, myht, zend_array_count(myht), /* incomplete_class */ false, var_hash,
1331
29.6k
        rcn);
1332
29.6k
      GC_TRY_DTOR_NO_REF(myht);
1333
29.6k
      return;
1334
319
    }
1335
2.65k
    case IS_REFERENCE:
1336
2.65k
      struc = Z_REFVAL_P(struc);
1337
2.65k
      goto again;
1338
0
    default:
1339
0
      smart_str_appendl(buf, "i:0;", 4);
1340
0
      return;
1341
51.7k
  }
1342
51.7k
}
1343
/* }}} */
1344
1345
PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
1346
2.16k
{
1347
2.16k
  php_var_serialize_intern(buf, struc, *data, false, true);
1348
2.16k
  smart_str_0(buf);
1349
2.16k
}
1350
/* }}} */
1351
1352
2.16k
PHPAPI php_serialize_data_t php_var_serialize_init(void) {
1353
2.16k
  struct php_serialize_data *d;
1354
  /* fprintf(stderr, "SERIALIZE_INIT      == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
1355
2.16k
  if (BG(serialize_lock) || !BG(serialize).level) {
1356
2.16k
    d = emalloc(sizeof(struct php_serialize_data));
1357
2.16k
    zend_hash_init(&d->ht, 16, NULL, ZVAL_PTR_DTOR, 0);
1358
2.16k
    d->n = 0;
1359
2.16k
    if (!BG(serialize_lock)) {
1360
2.16k
      BG(serialize).data = d;
1361
2.16k
      BG(serialize).level = 1;
1362
2.16k
    }
1363
2.16k
  } else {
1364
0
    d = BG(serialize).data;
1365
0
    ++BG(serialize).level;
1366
0
  }
1367
2.16k
  return d;
1368
2.16k
}
1369
1370
2.16k
PHPAPI void php_var_serialize_destroy(php_serialize_data_t d) {
1371
  /* fprintf(stderr, "SERIALIZE_DESTROY   == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
1372
2.16k
  if (BG(serialize_lock) || BG(serialize).level == 1) {
1373
2.16k
    zend_hash_destroy(&d->ht);
1374
2.16k
    efree(d);
1375
2.16k
  }
1376
2.16k
  if (!BG(serialize_lock) && !--BG(serialize).level) {
1377
2.16k
    BG(serialize).data = NULL;
1378
2.16k
  }
1379
2.16k
}
1380
1381
/* {{{ Returns a string representation of variable (which can later be unserialized) */
1382
PHP_FUNCTION(serialize)
1383
2.16k
{
1384
2.16k
  zval *struc;
1385
2.16k
  php_serialize_data_t var_hash;
1386
2.16k
  smart_str buf = {0};
1387
1388
6.49k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1389
8.66k
    Z_PARAM_ZVAL(struc)
1390
8.66k
  ZEND_PARSE_PARAMETERS_END();
1391
1392
2.16k
  PHP_VAR_SERIALIZE_INIT(var_hash);
1393
2.16k
  php_var_serialize(&buf, struc, &var_hash);
1394
2.16k
  PHP_VAR_SERIALIZE_DESTROY(var_hash);
1395
1396
2.16k
  if (EG(exception)) {
1397
66
    smart_str_free(&buf);
1398
66
    RETURN_THROWS();
1399
66
  }
1400
1401
2.10k
  RETURN_STR(smart_str_extract(&buf));
1402
2.10k
}
1403
/* }}} */
1404
1405
/* {{{ Takes a string representation of variable and recreates it, subject to the optional unserialize options HashTable */
1406
PHPAPI void php_unserialize_with_options(zval *return_value, const char *buf, const size_t buf_len, HashTable *options, const char* function_name)
1407
2.81k
{
1408
2.81k
  const unsigned char *p;
1409
2.81k
  php_unserialize_data_t var_hash;
1410
2.81k
  zval *retval;
1411
2.81k
  HashTable *class_hash = NULL, *prev_class_hash;
1412
2.81k
  zend_long prev_max_depth, prev_cur_depth;
1413
1414
2.81k
  if (buf_len == 0) {
1415
303
    RETURN_FALSE;
1416
303
  }
1417
1418
2.50k
  p = (const unsigned char*) buf;
1419
2.50k
  PHP_VAR_UNSERIALIZE_INIT(var_hash);
1420
1421
2.50k
  prev_class_hash = php_var_unserialize_get_allowed_classes(var_hash);
1422
2.50k
  prev_max_depth = php_var_unserialize_get_max_depth(var_hash);
1423
2.50k
  prev_cur_depth = php_var_unserialize_get_cur_depth(var_hash);
1424
2.50k
  if (options != NULL) {
1425
2
    zval *classes, *max_depth;
1426
1427
2
    classes = zend_hash_str_find_deref(options, "allowed_classes", sizeof("allowed_classes")-1);
1428
2
    if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) {
1429
0
      zend_type_error("%s(): Option \"allowed_classes\" must be of type array|bool, %s given", function_name, zend_zval_value_name(classes));
1430
0
      goto cleanup;
1431
0
    }
1432
1433
2
    if (classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
1434
0
      ALLOC_HASHTABLE(class_hash);
1435
0
      zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0);
1436
0
    }
1437
2
    if (class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
1438
0
      zval *entry;
1439
1440
0
      ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) {
1441
0
        ZVAL_DEREF(entry);
1442
0
        if (UNEXPECTED(Z_TYPE_P(entry) != IS_STRING && Z_TYPE_P(entry) != IS_OBJECT)) {
1443
0
          zend_type_error("%s(): Option \"allowed_classes\" must be an array of class names, %s given",
1444
0
            function_name, zend_zval_value_name(entry));
1445
0
          goto cleanup;
1446
0
        }
1447
0
        zend_string *tmp_str;
1448
0
        zend_string *name = zval_try_get_tmp_string(entry, &tmp_str);
1449
0
        if (UNEXPECTED(name == NULL)) {
1450
0
          goto cleanup;
1451
0
        }
1452
0
        if (UNEXPECTED(!zend_is_valid_class_name(name))) {
1453
0
          zend_value_error("%s(): Option \"allowed_classes\" must be an array of class names, \"%s\" given", function_name, ZSTR_VAL(name));
1454
0
          zend_tmp_string_release(tmp_str);
1455
0
          goto cleanup;
1456
0
        }
1457
0
        zend_string *lcname = zend_string_tolower(name);
1458
0
        zend_hash_add_empty_element(class_hash, lcname);
1459
0
            zend_string_release_ex(lcname, false);
1460
0
            zend_tmp_string_release(tmp_str);
1461
0
      } ZEND_HASH_FOREACH_END();
1462
0
    }
1463
2
    php_var_unserialize_set_allowed_classes(var_hash, class_hash);
1464
1465
2
    max_depth = zend_hash_str_find_deref(options, "max_depth", sizeof("max_depth") - 1);
1466
2
    if (max_depth) {
1467
0
      if (Z_TYPE_P(max_depth) != IS_LONG) {
1468
0
        zend_type_error("%s(): Option \"max_depth\" must be of type int, %s given", function_name, zend_zval_value_name(max_depth));
1469
0
        goto cleanup;
1470
0
      }
1471
0
      if (Z_LVAL_P(max_depth) < 0) {
1472
0
        zend_value_error("%s(): Option \"max_depth\" must be greater than or equal to 0", function_name);
1473
0
        goto cleanup;
1474
0
      }
1475
1476
0
      php_var_unserialize_set_max_depth(var_hash, Z_LVAL_P(max_depth));
1477
      /* If the max_depth for a nested unserialize() call has been overridden,
1478
       * start counting from zero again (for the nested call only). */
1479
0
      php_var_unserialize_set_cur_depth(var_hash, 0);
1480
0
    }
1481
2
  }
1482
1483
2.50k
  if (BG(unserialize).level > 1) {
1484
0
    retval = var_tmp_var(&var_hash);
1485
2.50k
  } else {
1486
2.50k
    retval = return_value;
1487
2.50k
  }
1488
2.50k
  if (!php_var_unserialize(retval, &p, p + buf_len, &var_hash)) {
1489
619
    if (!EG(exception)) {
1490
521
      php_error_docref(NULL, E_WARNING, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
1491
521
        (zend_long)((char*)p - buf), buf_len);
1492
521
    }
1493
619
    if (BG(unserialize).level <= 1) {
1494
619
      zval_ptr_dtor(return_value);
1495
619
    }
1496
619
    RETVAL_FALSE;
1497
1.88k
  } else {
1498
1.88k
    if ((char*)p < buf + buf_len) {
1499
2
      if (!EG(exception)) {
1500
2
        php_error_docref(NULL, E_WARNING, "Extra data starting at offset " ZEND_LONG_FMT " of %zd bytes",
1501
2
          (zend_long)((char*)p - buf), buf_len);
1502
2
      }
1503
2
    }
1504
1.88k
    if (BG(unserialize).level > 1) {
1505
0
      ZVAL_COPY(return_value, retval);
1506
1.88k
    } else if (Z_REFCOUNTED_P(return_value)) {
1507
1.57k
      zend_refcounted *ref = Z_COUNTED_P(return_value);
1508
1.57k
      gc_check_possible_root(ref);
1509
1.57k
    }
1510
1.88k
  }
1511
1512
2.50k
cleanup:
1513
2.50k
  if (class_hash) {
1514
0
    zend_hash_destroy(class_hash);
1515
0
    FREE_HASHTABLE(class_hash);
1516
0
  }
1517
1518
  /* Reset to previous options in case this is a nested call */
1519
2.50k
  php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash);
1520
2.50k
  php_var_unserialize_set_max_depth(var_hash, prev_max_depth);
1521
2.50k
  php_var_unserialize_set_cur_depth(var_hash, prev_cur_depth);
1522
2.50k
  PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1523
1524
  /* Per calling convention we must not return a reference here, so unwrap. We're doing this at
1525
   * the very end, because __wakeup() calls performed during UNSERIALIZE_DESTROY might affect
1526
   * the value we unwrap here. This is compatible with behavior in PHP <=7.0. */
1527
2.50k
  if (Z_ISREF_P(return_value)) {
1528
3
    zend_unwrap_reference(return_value);
1529
3
  }
1530
2.50k
}
1531
/* }}} */
1532
1533
/* {{{ Takes a string representation of variable and recreates it */
1534
PHP_FUNCTION(unserialize)
1535
2.81k
{
1536
2.81k
  char *buf = NULL;
1537
2.81k
  size_t buf_len;
1538
2.81k
  HashTable *options = NULL;
1539
1540
8.45k
  ZEND_PARSE_PARAMETERS_START(1, 2)
1541
11.2k
    Z_PARAM_STRING(buf, buf_len)
1542
2.81k
    Z_PARAM_OPTIONAL
1543
5.65k
    Z_PARAM_ARRAY_HT(options)
1544
2.81k
  ZEND_PARSE_PARAMETERS_END();
1545
1546
2.81k
  php_unserialize_with_options(return_value, buf, buf_len, options, "unserialize");
1547
2.81k
}
1548
/* }}} */
1549
1550
/* {{{ Returns the allocated by PHP memory */
1551
58
PHP_FUNCTION(memory_get_usage) {
1552
58
  bool real_usage = 0;
1553
1554
174
  ZEND_PARSE_PARAMETERS_START(0, 1)
1555
174
    Z_PARAM_OPTIONAL
1556
174
    Z_PARAM_BOOL(real_usage)
1557
58
  ZEND_PARSE_PARAMETERS_END();
1558
1559
58
  RETURN_LONG(zend_memory_usage(real_usage));
1560
58
}
1561
/* }}} */
1562
1563
/* {{{ Returns the peak allocated by PHP memory */
1564
0
PHP_FUNCTION(memory_get_peak_usage) {
1565
0
  bool real_usage = 0;
1566
1567
0
  ZEND_PARSE_PARAMETERS_START(0, 1)
1568
0
    Z_PARAM_OPTIONAL
1569
0
    Z_PARAM_BOOL(real_usage)
1570
0
  ZEND_PARSE_PARAMETERS_END();
1571
1572
0
  RETURN_LONG(zend_memory_peak_usage(real_usage));
1573
0
}
1574
/* }}} */
1575
1576
/* {{{ Resets the peak PHP memory usage */
1577
0
PHP_FUNCTION(memory_reset_peak_usage) {
1578
0
  ZEND_PARSE_PARAMETERS_NONE();
1579
1580
0
  zend_memory_reset_peak_usage();
1581
0
}
1582
/* }}} */
1583
1584
PHP_INI_BEGIN()
1585
  STD_PHP_INI_ENTRY("unserialize_max_depth", "4096", PHP_INI_ALL, OnUpdateLong, unserialize_max_depth, php_basic_globals, basic_globals)
1586
PHP_INI_END()
1587
1588
PHP_MINIT_FUNCTION(var)
1589
16
{
1590
16
  REGISTER_INI_ENTRIES();
1591
16
  return SUCCESS;
1592
16
}