Coverage Report

Created: 2026-07-25 06:39

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
3.91M
#define COMMON (is_ref ? "&" : "")
39
40
static void php_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level) /* {{{ */
41
1.58M
{
42
1.58M
#ifdef ZEND_CHECK_STACK_LIMIT
43
1.58M
  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.58M
#endif
48
1.58M
  if (key == NULL) { /* numeric key */
49
1.33M
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
50
1.33M
  } else { /* string key */
51
257k
    php_printf("%*c[\"", level + 1, ' ');
52
257k
    PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
53
257k
    php_printf("\"]=>\n");
54
257k
  }
55
1.58M
  php_var_dump(zv, level + 2);
56
1.58M
}
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.2k
    int unmangle = zend_unmangle_property_name(key, &class_name, &prop_name);
73
13.2k
    php_printf("%*c[", level + 1, ' ');
74
75
13.2k
    if (class_name && unmangle == SUCCESS) {
76
973
      if (class_name[0] == '*') {
77
141
        php_printf("\"%s\":protected", prop_name);
78
832
      } else {
79
832
        php_printf("\"%s\":\"%s\":private", prop_name, class_name);
80
832
      }
81
12.3k
    } else {
82
12.3k
      php_printf("\"");
83
12.3k
      PHPWRITE(ZSTR_VAL(key), ZSTR_LEN(key));
84
12.3k
      php_printf("\"");
85
12.3k
    }
86
13.2k
    ZEND_PUTS("]=>\n");
87
13.2k
  }
88
89
2.11M
  if (Z_TYPE_P(zv) == IS_UNDEF) {
90
865
    ZEND_ASSERT(ZEND_TYPE_IS_SET(prop_info->type));
91
865
    zend_string *type_str = zend_type_to_string(prop_info->type);
92
865
    php_printf("%*cuninitialized(%s)\n",
93
865
      level + 1, ' ', ZSTR_VAL(type_str));
94
865
    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
11.9k
static const char *php_var_dump_object_prefix(zend_object *obj) {
102
11.9k
  if (EXPECTED(!zend_object_is_lazy(obj))) {
103
10.8k
    return "";
104
10.8k
  }
105
106
1.02k
  if (zend_object_is_lazy_proxy(obj)) {
107
615
    return "lazy proxy ";
108
615
  }
109
110
411
  return "lazy ghost ";
111
1.02k
}
112
113
PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
114
3.91M
{
115
3.91M
  HashTable *myht;
116
3.91M
  zend_string *class_name;
117
3.91M
  int is_ref = 0;
118
3.91M
  zend_ulong num;
119
3.91M
  zend_string *key;
120
3.91M
  zval *val;
121
3.91M
  uint32_t count;
122
123
3.91M
  if (level > 1) {
124
3.69M
    php_printf("%*c", level - 1, ' ');
125
3.69M
  }
126
127
3.91M
again:
128
3.91M
  switch (Z_TYPE_P(struc)) {
129
9.98k
    case IS_FALSE:
130
9.98k
      php_printf("%sbool(false)\n", COMMON);
131
9.98k
      break;
132
9.57k
    case IS_TRUE:
133
9.57k
      php_printf("%sbool(true)\n", COMMON);
134
9.57k
      break;
135
2.10M
    case IS_NULL:
136
2.10M
      php_printf("%sNULL\n", COMMON);
137
2.10M
      break;
138
855k
    case IS_LONG:
139
855k
      php_printf("%sint(" ZEND_LONG_FMT ")\n", COMMON, Z_LVAL_P(struc));
140
855k
      break;
141
248k
    case IS_DOUBLE:
142
248k
      php_printf_unchecked("%sfloat(%.*H)\n", COMMON, (int) PG(serialize_precision), Z_DVAL_P(struc));
143
248k
      break;
144
594k
    case IS_STRING:
145
594k
      php_printf("%sstring(%zd) \"", COMMON, Z_STRLEN_P(struc));
146
594k
      PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
147
594k
      PUTS("\"\n");
148
594k
      break;
149
75.1k
    case IS_ARRAY:
150
75.1k
      myht = Z_ARRVAL_P(struc);
151
75.1k
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
152
72.0k
        if (GC_IS_RECURSIVE(myht)) {
153
77
          PUTS("*RECURSION*\n");
154
77
          return;
155
77
        }
156
71.9k
        GC_ADDREF(myht);
157
71.9k
        GC_PROTECT_RECURSION(myht);
158
71.9k
      }
159
75.0k
      count = zend_hash_num_elements(myht);
160
75.0k
      php_printf("%sarray(%d) {\n", COMMON, count);
161
3.25M
      ZEND_HASH_FOREACH_KEY_VAL(myht, num, key, val) {
162
3.25M
        php_array_element_dump(val, num, key, level);
163
3.25M
      } ZEND_HASH_FOREACH_END();
164
75.0k
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
165
71.9k
        GC_UNPROTECT_RECURSION(myht);
166
71.9k
        GC_DTOR_NO_REF(myht);
167
71.9k
      }
168
75.0k
      if (level > 1) {
169
6.60k
        php_printf("%*c", level-1, ' ');
170
6.60k
      }
171
75.0k
      PUTS("}\n");
172
75.0k
      break;
173
12.6k
    case IS_OBJECT: {
174
12.6k
      zend_class_entry *ce = Z_OBJCE_P(struc);
175
12.6k
      if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->__debugInfo == NULL) {
176
656
        zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
177
656
        php_printf("%senum(%s::%s)\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval));
178
656
        return;
179
656
      }
180
11.9k
      zend_object *zobj = Z_OBJ_P(struc);
181
11.9k
      uint32_t *guard = zend_get_recursion_guard(zobj);
182
11.9k
      if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
183
274
        PUTS("*RECURSION*\n");
184
274
        return;
185
274
      }
186
11.7k
      ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
187
188
11.7k
      myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
189
11.7k
      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
11.6k
      } else {
193
11.6k
        class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
194
11.6k
        const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
195
196
11.6k
        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
11.6k
        zend_string_release_ex(class_name, 0);
198
11.6k
      }
199
200
11.7k
      if (myht) {
201
11.4k
        zend_ulong num;
202
11.4k
        zend_string *key;
203
11.4k
        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.71k
            val = Z_INDIRECT_P(val);
210
7.71k
            if (key) {
211
7.71k
              prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
212
7.71k
            }
213
7.71k
          }
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.4k
        zend_release_properties(myht);
220
11.4k
      }
221
11.7k
      if (level > 1) {
222
2.93k
        php_printf("%*c", level-1, ' ');
223
2.93k
      }
224
11.7k
      PUTS("}\n");
225
11.7k
      ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
226
11.7k
      break;
227
11.9k
    }
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
11.9k
    }
233
2.58k
    case IS_REFERENCE:
234
      //??? hide references with refcount==1 (for compatibility)
235
2.58k
      if (Z_REFCOUNT_P(struc) > 1) {
236
1.58k
        is_ref = 1;
237
1.58k
      }
238
2.58k
      struc = Z_REFVAL_P(struc);
239
2.58k
      goto again;
240
1.01k
    default:
241
1.01k
      php_printf("%sUNKNOWN:0\n", COMMON);
242
1.01k
      break;
243
3.91M
  }
244
3.91M
}
245
/* }}} */
246
247
/* {{{ Dumps a string representation of variable to output */
248
PHP_FUNCTION(var_dump)
249
202k
{
250
202k
  zval *args;
251
202k
  int argc;
252
202k
  int i;
253
254
606k
  ZEND_PARSE_PARAMETERS_START(1, -1)
255
606k
    Z_PARAM_VARIADIC('+', args, argc)
256
606k
  ZEND_PARSE_PARAMETERS_END();
257
258
411k
  for (i = 0; i < argc; i++) {
259
209k
    php_var_dump(&args[i], 1);
260
209k
  }
261
202k
}
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
115
{
285
115
  const char *prop_name, *class_name;
286
287
115
#ifdef ZEND_CHECK_STACK_LIMIT
288
115
  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
115
#endif
293
115
  if (key == NULL) { /* numeric key */
294
0
    php_printf("%*c[" ZEND_LONG_FMT "]=>\n", level + 1, ' ', index);
295
115
  } else { /* string key */
296
115
    zend_unmangle_property_name(key, &class_name, &prop_name);
297
115
    php_printf("%*c[", level + 1, ' ');
298
299
115
    if (class_name) {
300
10
      if (class_name[0] == '*') {
301
5
        php_printf("\"%s\":protected", prop_name);
302
5
      } else {
303
5
        php_printf("\"%s\":\"%s\":private", prop_name, class_name);
304
5
      }
305
105
    } else {
306
105
      php_printf("\"%s\"", prop_name);
307
105
    }
308
115
    ZEND_PUTS("]=>\n");
309
115
  }
310
115
  if (prop_info && Z_TYPE_P(zv) == IS_UNDEF) {
311
36
    zend_string *type_str = zend_type_to_string(prop_info->type);
312
36
    php_printf("%*cuninitialized(%s)\n",
313
36
      level + 1, ' ', ZSTR_VAL(type_str));
314
36
    zend_string_release(type_str);
315
79
  } else {
316
79
    php_debug_zval_dump(zv, level + 2);
317
79
  }
318
115
}
319
/* }}} */
320
321
PHPAPI void php_debug_zval_dump(zval *struc, int level) /* {{{ */
322
341
{
323
341
  HashTable *myht = NULL;
324
341
  zend_string *class_name;
325
341
  zend_ulong index;
326
341
  zend_string *key;
327
341
  zval *val;
328
341
  uint32_t count;
329
341
  char *packed;
330
331
341
  if (level > 1) {
332
79
    php_printf("%*c", level - 1, ' ');
333
79
  }
334
335
341
  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
21
  case IS_NULL:
343
21
    PUTS("NULL\n");
344
21
    break;
345
20
  case IS_LONG:
346
20
    php_printf("int(" ZEND_LONG_FMT ")\n", Z_LVAL_P(struc));
347
20
    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
58
  case IS_STRING:
352
58
    php_printf("string(%zd) \"", Z_STRLEN_P(struc));
353
58
    PHPWRITE(Z_STRVAL_P(struc), Z_STRLEN_P(struc));
354
58
    if (Z_REFCOUNTED_P(struc)) {
355
12
      php_printf("\" refcount(%u)\n", Z_REFCOUNT_P(struc));
356
46
    } else {
357
46
      PUTS("\" interned\n");
358
46
    }
359
58
    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
242
  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
242
    zend_object *zobj = Z_OBJ_P(struc);
396
242
    uint32_t *guard = zend_get_recursion_guard(zobj);
397
242
    if (ZEND_GUARD_OR_GC_IS_RECURSIVE(guard, DEBUG, zobj)) {
398
0
      PUTS("*RECURSION*\n");
399
0
      return;
400
0
    }
401
242
    ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
402
403
242
    myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
404
242
    class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
405
242
    const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
406
407
242
    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
242
    zend_string_release_ex(class_name, 0);
409
242
    if (myht) {
410
386
      ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
411
386
        zend_property_info *prop_info = NULL;
412
413
386
        if (Z_TYPE_P(val) == IS_INDIRECT) {
414
108
          val = Z_INDIRECT_P(val);
415
108
          if (key) {
416
108
            prop_info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
417
108
          }
418
108
        }
419
420
386
        if (!Z_ISUNDEF_P(val) || prop_info) {
421
115
          zval_object_property_dump(prop_info, val, index, key, level);
422
115
        }
423
386
      } ZEND_HASH_FOREACH_END();
424
156
      zend_release_properties(myht);
425
156
    }
426
242
    if (level > 1) {
427
7
      php_printf("%*c", level - 1, ' ');
428
7
    }
429
242
    PUTS("}\n");
430
242
    ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, DEBUG, zobj);
431
242
    break;
432
242
  }
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
242
  }
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
341
  }
450
341
}
451
/* }}} */
452
453
/* {{{ Dumps a string representation of an internal zend value to output. */
454
PHP_FUNCTION(debug_zval_dump)
455
259
{
456
259
  zval *args;
457
259
  int argc;
458
259
  int i;
459
460
777
  ZEND_PARSE_PARAMETERS_START(1, -1)
461
777
    Z_PARAM_VARIADIC('+', args, argc)
462
777
  ZEND_PARSE_PARAMETERS_END();
463
464
521
  for (i = 0; i < argc; i++) {
465
262
    php_debug_zval_dump(&args[i], 1);
466
262
  }
467
259
}
468
/* }}} */
469
470
#define buffer_append_spaces(buf, num_spaces) \
471
871
  do { \
472
871
    char *tmp_spaces; \
473
871
    size_t tmp_spaces_len; \
474
871
    tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \
475
871
    smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \
476
871
    efree(tmp_spaces); \
477
871
  } 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
181
{
481
181
  if (key == NULL) { /* numeric key */
482
75
    buffer_append_spaces(buf, level+1);
483
75
    smart_str_append_long(buf, (zend_long) index);
484
75
    smart_str_appendl(buf, " => ", 4);
485
486
106
  } else { /* string key */
487
106
    zend_string *tmp_str;
488
106
    zend_string *ckey = php_addcslashes(key, "'\\", 2);
489
106
    tmp_str = php_str_to_str(ZSTR_VAL(ckey), ZSTR_LEN(ckey), "\0", 1, "' . \"\\0\" . '", 12);
490
491
106
    buffer_append_spaces(buf, level + 1);
492
493
106
    smart_str_appendc(buf, '\'');
494
106
    smart_str_append(buf, tmp_str);
495
106
    smart_str_appendl(buf, "' => ", 5);
496
497
106
    zend_string_free(ckey);
498
106
    zend_string_free(tmp_str);
499
106
  }
500
181
  zend_result result = php_var_export_ex(zv, level + 2, buf);
501
502
181
  smart_str_appendc(buf, ',');
503
181
  smart_str_appendc(buf, '\n');
504
505
181
  return result;
506
181
}
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
21
    case IS_FALSE:
547
21
      smart_str_appendl(buf, "false", 5);
548
21
      break;
549
19
    case IS_TRUE:
550
19
      smart_str_appendl(buf, "true", 4);
551
19
      break;
552
68
    case IS_NULL:
553
68
      smart_str_appendl(buf, "NULL", 4);
554
68
      break;
555
208
    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
208
      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
208
      smart_str_append_long(buf, Z_LVAL_P(struc));
564
208
      break;
565
18
    case IS_DOUBLE:
566
18
      smart_str_append_double(
567
18
        buf, Z_DVAL_P(struc), (int) PG(serialize_precision), /* zero_fraction */ true);
568
18
      break;
569
611
    case IS_STRING:
570
611
      ztmp = php_addcslashes(Z_STR_P(struc), "'\\", 2);
571
611
      ztmp2 = php_str_to_str(ZSTR_VAL(ztmp), ZSTR_LEN(ztmp), "\0", 1, "' . \"\\0\" . '", 12);
572
573
611
      smart_str_appendc(buf, '\'');
574
611
      smart_str_append(buf, ztmp2);
575
611
      smart_str_appendc(buf, '\'');
576
577
611
      zend_string_free(ztmp);
578
611
      zend_string_free(ztmp2);
579
611
      break;
580
92
    case IS_ARRAY:
581
92
      myht = Z_ARRVAL_P(struc);
582
92
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
583
60
        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
60
        GC_ADDREF(myht);
589
60
        GC_PROTECT_RECURSION(myht);
590
60
      }
591
92
      if (level > 1) {
592
18
        smart_str_appendc(buf, '\n');
593
18
        buffer_append_spaces(buf, level - 1);
594
18
      }
595
92
      smart_str_appendl(buf, "array (\n", 8);
596
471
      ZEND_HASH_FOREACH_KEY_VAL(myht, index, key, val) {
597
471
        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
471
      } ZEND_HASH_FOREACH_END();
605
92
      if (!(GC_FLAGS(myht) & GC_IMMUTABLE)) {
606
60
        GC_UNPROTECT_RECURSION(myht);
607
60
        GC_DELREF(myht);
608
60
      }
609
92
      if (level > 1) {
610
18
        buffer_append_spaces(buf, level - 1);
611
18
      }
612
92
      smart_str_appendc(buf, ')');
613
614
92
      break;
615
616
250
    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
250
      zend_object *zobj = Z_OBJ_P(struc);
622
250
      uint32_t *guard = zend_get_recursion_guard(zobj);
623
250
      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
250
      ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, EXPORT, zobj);
629
250
      myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_VAR_EXPORT);
630
250
      if (level > 1) {
631
8
        smart_str_appendc(buf, '\n');
632
8
        buffer_append_spaces(buf, level - 1);
633
8
      }
634
635
250
      zend_class_entry *ce = Z_OBJCE_P(struc);
636
250
      bool is_enum = ce->ce_flags & ZEND_ACC_ENUM;
637
638
      /* stdClass has no __set_state method, but can be casted to */
639
250
      if (ce == zend_standard_class_def) {
640
0
        smart_str_appendl(buf, "(object) array(\n", 16);
641
250
      } else {
642
250
        smart_str_appendc(buf, '\\');
643
250
        smart_str_append(buf, ce->name);
644
250
        if (is_enum) {
645
39
          zend_object *zobj = Z_OBJ_P(struc);
646
39
          zval *case_name_zval = zend_enum_fetch_case_name(zobj);
647
39
          smart_str_appendl(buf, "::", 2);
648
39
          smart_str_append(buf, Z_STR_P(case_name_zval));
649
211
        } else {
650
211
          smart_str_appendl(buf, "::__set_state(array(\n", 21);
651
211
        }
652
250
      }
653
654
250
      if (myht) {
655
167
        if (!is_enum) {
656
1.59k
          ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, val) {
657
            /* data is IS_PTR for properties with hooks. */
658
1.59k
            zval tmp;
659
1.59k
            if (UNEXPECTED(Z_TYPE_P(val) == IS_PTR)) {
660
519
              zend_property_info *prop_info = Z_PTR_P(val);
661
519
              if ((prop_info->flags & ZEND_ACC_VIRTUAL) && !prop_info->hooks[ZEND_PROPERTY_HOOK_GET]) {
662
80
                continue;
663
80
              }
664
439
              const char *unmangled_name_cstr = zend_get_unmangled_property_name(prop_info->name);
665
439
              zend_string *unmangled_name = zend_string_init(unmangled_name_cstr, strlen(unmangled_name_cstr), false);
666
439
              val = zend_read_property_ex(prop_info->ce, zobj, unmangled_name, /* silent */ true, &tmp);
667
439
              zend_string_release_ex(unmangled_name, false);
668
439
              if (EG(exception)) {
669
6
                ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
670
6
                zend_release_properties(myht);
671
6
                return FAILURE;
672
6
              }
673
439
            }
674
645
            php_object_element_export(val, index, key, level, buf);
675
645
            if (val == &tmp) {
676
421
              zval_ptr_dtor(val);
677
421
            }
678
645
          } ZEND_HASH_FOREACH_END();
679
128
        }
680
161
        zend_release_properties(myht);
681
161
      }
682
244
      ZEND_GUARD_OR_GC_UNPROTECT_RECURSION(guard, EXPORT, zobj);
683
244
      if (level > 1 && !is_enum) {
684
1
        buffer_append_spaces(buf, level - 1);
685
1
      }
686
244
      if (ce == zend_standard_class_def) {
687
0
        smart_str_appendc(buf, ')');
688
244
      } else if (!is_enum) {
689
205
        smart_str_appendl(buf, "))", 2);
690
205
      }
691
692
244
      break;
693
250
    }
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.28k
  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
461
{
722
461
  zval *var;
723
461
  bool return_output = 0;
724
461
  smart_str buf = {0};
725
726
1.38k
  ZEND_PARSE_PARAMETERS_START(1, 2)
727
1.84k
    Z_PARAM_ZVAL(var)
728
1.84k
    Z_PARAM_OPTIONAL
729
1.84k
    Z_PARAM_BOOL(return_output)
730
461
  ZEND_PARSE_PARAMETERS_END();
731
732
461
  zend_result result = php_var_export_ex(var, 1, &buf);
733
461
  smart_str_0 (&buf);
734
735
461
  if (result == FAILURE) {
736
6
    smart_str_free(&buf);
737
455
  } else if (return_output) {
738
125
    RETURN_STR(smart_str_extract(&buf));
739
330
  } else {
740
330
    PHPWRITE(ZSTR_VAL(buf.s), ZSTR_LEN(buf.s));
741
330
    smart_str_free(&buf);
742
330
  }
743
461
}
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
63.7k
{
753
63.7k
  zval *zv;
754
63.7k
  zend_ulong key;
755
63.7k
  bool is_ref = Z_ISREF_P(var);
756
757
63.7k
  data->n += 1;
758
759
63.7k
  if (is_ref) {
760
    /* pass */
761
56.5k
  } else if (Z_TYPE_P(var) != IS_OBJECT) {
762
55.9k
    return 0;
763
55.9k
  } else if (!in_rcn_array
764
537
   && Z_REFCOUNT_P(var) == 1
765
80
   && (Z_OBJ_P(var)->properties == NULL || GC_REFCOUNT(Z_OBJ_P(var)->properties) == 1)
766
   /* __serialize and __sleep may arbitrarily increase the refcount */
767
80
   && Z_OBJCE_P(var)->__serialize == NULL
768
78
   && zend_hash_find_known_hash(&Z_OBJCE_P(var)->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP)) == NULL) {
769
78
    return 0;
770
78
  }
771
772
  /* References to objects are treated as if the reference didn't exist */
773
7.72k
  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.72k
  key = (zend_ulong) (uintptr_t) Z_COUNTED_P(var);
780
7.72k
  zv = zend_hash_index_find(&data->ht, key);
781
782
7.72k
  if (zv) {
783
    /* References are only counted once, undo the data->n increment above */
784
4.13k
    if (is_ref && Z_LVAL_P(zv) != -1) {
785
4.10k
      data->n -= 1;
786
4.10k
    }
787
788
4.13k
    return Z_LVAL_P(zv);
789
4.13k
  } else {
790
3.58k
    zval zv_n;
791
3.58k
    ZVAL_LONG(&zv_n, data->n);
792
3.58k
    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.58k
    zend_hash_index_add_new(&data->ht, key + 1, var);
799
3.58k
    Z_ADDREF_P(var);
800
801
3.58k
    return 0;
802
3.58k
  }
803
7.72k
}
804
/* }}} */
805
806
static inline void php_var_serialize_long(smart_str *buf, zend_long val) /* {{{ */
807
29.1k
{
808
29.1k
  char b[32];
809
29.1k
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, val);
810
29.1k
  size_t l = b + sizeof(b) - 1 - s;
811
29.1k
  char *res = smart_str_extend(buf, 2 + l + 1);
812
29.1k
  res = zend_mempcpy(res, "i:", 2);
813
29.1k
  memcpy(res, s, l);
814
29.1k
  res[l] = ';';
815
29.1k
}
816
/* }}} */
817
818
static inline void php_var_serialize_string(smart_str *buf, char *str, size_t len) /* {{{ */
819
49.4k
{
820
49.4k
  char b[32];
821
49.4k
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, len);
822
49.4k
  size_t l = b + sizeof(b) - 1 - s;
823
49.4k
  char *res = smart_str_extend(buf, 2 + l + 2 + len + 2);
824
49.4k
  res = zend_mempcpy(res, "s:", 2);
825
49.4k
  res = zend_mempcpy(res, s, l);
826
49.4k
  res = zend_mempcpy(res, ":\"", 2);
827
49.4k
  res = zend_mempcpy(res, str, len);
828
49.4k
  memcpy(res, "\";", 2);
829
49.4k
}
830
/* }}} */
831
832
static inline bool php_var_serialize_class_name(smart_str *buf, zval *struc) /* {{{ */
833
447
{
834
447
  char b[32];
835
447
  PHP_CLASS_ATTRIBUTES;
836
837
447
  PHP_SET_CLASS_ATTRIBUTES(struc);
838
447
  size_t class_name_len = ZSTR_LEN(class_name);
839
447
  char *s = zend_print_long_to_buf(b + sizeof(b) - 1, class_name_len);
840
447
  size_t l = b + sizeof(b) - 1 - s;
841
447
  char *res = smart_str_extend(buf, 2 + l + 2 + class_name_len + 2);
842
447
  res = zend_mempcpy(res, "O:", 2);
843
447
  res = zend_mempcpy(res, s, l);
844
447
  res = zend_mempcpy(res, ":\"", 2);
845
447
  res = zend_mempcpy(res, ZSTR_VAL(class_name), class_name_len);
846
447
  memcpy(res, "\":", 2);
847
447
  PHP_CLEANUP_CLASS_ATTRIBUTES();
848
447
  return incomplete_class;
849
447
}
850
/* }}} */
851
852
static HashTable* php_var_serialize_call_sleep(zend_object *obj, zend_function *fn) /* {{{ */
853
90
{
854
90
  zval retval;
855
856
90
  BG(serialize_lock)++;
857
90
  zend_call_known_instance_method(fn, obj, &retval, /* param_count */ 0, /* params */ NULL);
858
90
  BG(serialize_lock)--;
859
860
90
  if (Z_ISUNDEF(retval) || EG(exception)) {
861
5
    zval_ptr_dtor(&retval);
862
5
    return NULL;
863
5
  }
864
865
85
  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
83
  return Z_ARRVAL(retval);
872
85
}
873
/* }}} */
874
875
static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{{ */
876
95
{
877
95
  BG(serialize_lock)++;
878
95
  zend_call_known_instance_method_with_0_params(
879
95
    Z_OBJCE_P(obj)->__serialize, Z_OBJ_P(obj), retval);
880
95
  BG(serialize_lock)--;
881
882
95
  if (EG(exception)) {
883
2
    zval_ptr_dtor(retval);
884
2
    return FAILURE;
885
2
  }
886
887
93
  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
93
  return SUCCESS;
894
93
}
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
152
{
900
152
  zval *val = zend_hash_find(props, name);
901
152
  if (val == NULL) {
902
78
    return FAILURE;
903
78
  }
904
905
74
  if (Z_TYPE_P(val) == IS_INDIRECT) {
906
74
    val = Z_INDIRECT_P(val);
907
74
    if (Z_TYPE_P(val) == IS_UNDEF) {
908
34
      zend_property_info *info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
909
34
      if (info) {
910
34
        return SUCCESS;
911
34
      }
912
0
      return FAILURE;
913
34
    }
914
74
  }
915
916
40
  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
40
  Z_TRY_ADDREF_P(val);
923
40
  return SUCCESS;
924
40
}
925
/* }}} */
926
927
static int php_var_serialize_get_sleep_props(
928
    HashTable *ht, zval *struc, HashTable *sleep_retval) /* {{{ */
929
83
{
930
83
  zend_class_entry *ce = Z_OBJCE_P(struc);
931
83
  HashTable *props = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
932
83
  zval *name_val;
933
83
  int retval = SUCCESS;
934
935
83
  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
295
  ZEND_HASH_FOREACH_VAL_IND(sleep_retval, name_val) {
939
295
    zend_string *name, *tmp_name, *priv_name, *prot_name;
940
941
295
    ZVAL_DEREF(name_val);
942
295
    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
295
    name = zval_get_tmp_string(name_val, &tmp_name);
949
295
    if (php_var_serialize_try_add_sleep_prop(ht, props, name, name, struc) == SUCCESS) {
950
54
      zend_tmp_string_release(tmp_name);
951
54
      continue;
952
54
    }
953
954
52
    if (EG(exception)) {
955
19
      zend_tmp_string_release(tmp_name);
956
19
      retval = FAILURE;
957
19
      break;
958
19
    }
959
960
33
    priv_name = zend_mangle_property_name(
961
33
      ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
962
33
      ZSTR_VAL(name), ZSTR_LEN(name), ce->type == ZEND_INTERNAL_CLASS);
963
33
    if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name, struc) == SUCCESS) {
964
20
      zend_tmp_string_release(tmp_name);
965
20
      zend_string_release(priv_name);
966
20
      continue;
967
20
    }
968
13
    zend_string_release(priv_name);
969
970
13
    if (EG(exception)) {
971
0
      zend_tmp_string_release(tmp_name);
972
0
      retval = FAILURE;
973
0
      break;
974
0
    }
975
976
13
    prot_name = zend_mangle_property_name(
977
13
      "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type == ZEND_INTERNAL_CLASS);
978
13
    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
13
    zend_string_release(prot_name);
984
985
13
    if (EG(exception)) {
986
0
      zend_tmp_string_release(tmp_name);
987
0
      retval = FAILURE;
988
0
      break;
989
0
    }
990
991
13
    php_error_docref(NULL, E_WARNING,
992
13
      "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
993
13
    zend_tmp_string_release(tmp_name);
994
13
  } ZEND_HASH_FOREACH_END();
995
996
83
  zend_release_properties(props);
997
83
  return retval;
998
83
}
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
35.8k
{
1003
35.8k
  smart_str_append_unsigned(buf, count);
1004
35.8k
  smart_str_appendl(buf, ":{", 2);
1005
35.8k
  if (count > 0) {
1006
7.72k
    zend_string *key;
1007
7.72k
    zval *data;
1008
7.72k
    zend_ulong index;
1009
1010
130k
    ZEND_HASH_FOREACH_KEY_VAL_IND(ht, index, key, data) {
1011
130k
      if (incomplete_class && zend_string_equals_literal(key, MAGIC_MEMBER)) {
1012
0
        incomplete_class = false;
1013
0
        continue;
1014
0
      }
1015
1016
61.2k
      if (!key) {
1017
22.0k
        php_var_serialize_long(buf, index);
1018
39.1k
      } else {
1019
39.1k
        php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
1020
39.1k
      }
1021
1022
61.2k
      if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
1023
115
        data = Z_REFVAL_P(data);
1024
115
      }
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
61.2k
      if (Z_TYPE_P(data) == IS_ARRAY) {
1029
32.7k
        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
32.7k
        } else {
1033
32.7k
          php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
1034
32.7k
        }
1035
32.7k
      } else {
1036
28.4k
        php_var_serialize_intern(buf, data, var_hash, in_rcn_array, false);
1037
28.4k
      }
1038
61.2k
    } ZEND_HASH_FOREACH_END();
1039
7.72k
  }
1040
35.8k
  smart_str_appendc(buf, '}');
1041
35.8k
}
1042
/* }}} */
1043
1044
static void php_var_serialize_class(smart_str *buf, zval *struc, HashTable *ht, php_serialize_data_t var_hash) /* {{{ */
1045
83
{
1046
83
  HashTable props;
1047
1048
83
  if (php_var_serialize_get_sleep_props(&props, struc, ht) == SUCCESS) {
1049
64
    php_var_serialize_class_name(buf, struc);
1050
64
    php_var_serialize_nested_data(
1051
64
      buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ false, var_hash,
1052
64
      GC_REFCOUNT(&props) > 1);
1053
64
  }
1054
83
  zend_hash_destroy(&props);
1055
83
}
1056
/* }}} */
1057
1058
static zend_always_inline bool php_serialize_check_stack_limit(void)
1059
63.7k
{
1060
63.7k
#ifdef ZEND_CHECK_STACK_LIMIT
1061
63.7k
  if (UNEXPECTED(zend_call_stack_overflowed(EG(stack_limit)))) {
1062
0
    zend_call_stack_size_error();
1063
0
    return true;
1064
0
  }
1065
63.7k
#endif
1066
63.7k
  return false;
1067
63.7k
}
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
63.7k
{
1071
63.7k
  zend_long var_already;
1072
63.7k
  HashTable *myht;
1073
1074
63.7k
  if (EG(exception)) {
1075
0
    return;
1076
0
  }
1077
1078
63.7k
  if (UNEXPECTED(php_serialize_check_stack_limit())) {
1079
0
    return;
1080
0
  }
1081
1082
63.7k
  if (var_hash && (var_already = php_add_var_hash(var_hash, struc, in_rcn_array))) {
1083
4.13k
    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.13k
    } else if (Z_ISREF_P(struc)) {
1088
4.10k
      smart_str_appendl(buf, "R:", 2);
1089
4.10k
      smart_str_append_long(buf, var_already);
1090
4.10k
      smart_str_appendc(buf, ';');
1091
4.10k
      return;
1092
4.10k
    } else if (Z_TYPE_P(struc) == IS_OBJECT) {
1093
37
      smart_str_appendl(buf, "r:", 2);
1094
37
      smart_str_append_long(buf, var_already);
1095
37
      smart_str_appendc(buf, ';');
1096
37
      return;
1097
37
    }
1098
4.13k
  }
1099
1100
62.7k
again:
1101
62.7k
  switch (Z_TYPE_P(struc)) {
1102
92
    case IS_FALSE:
1103
92
      smart_str_appendl(buf, "b:0;", 4);
1104
92
      return;
1105
1106
14
    case IS_TRUE:
1107
14
      smart_str_appendl(buf, "b:1;", 4);
1108
14
      return;
1109
1110
6.59k
    case IS_NULL:
1111
6.59k
      smart_str_appendl(buf, "N;", 2);
1112
6.59k
      return;
1113
1114
6.97k
    case IS_LONG:
1115
6.97k
      php_var_serialize_long(buf, Z_LVAL_P(struc));
1116
6.97k
      return;
1117
1118
230
    case IS_DOUBLE: {
1119
230
      char tmp_str[ZEND_DOUBLE_MAX_LENGTH];
1120
230
      zend_gcvt(Z_DVAL_P(struc), (int)PG(serialize_precision), '.', 'E', tmp_str);
1121
1122
230
      size_t len = strlen(tmp_str);
1123
230
      char *res = smart_str_extend(buf, 2 + len + 1);
1124
230
      res = zend_mempcpy(res, "d:", 2);
1125
230
      memcpy(res, tmp_str, len);
1126
230
      res[len] = ';';
1127
230
      return;
1128
0
    }
1129
1130
9.57k
    case IS_STRING:
1131
9.57k
      php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
1132
9.57k
      return;
1133
1134
553
    case IS_OBJECT: {
1135
553
        zend_class_entry *ce = Z_OBJCE_P(struc);
1136
553
        bool incomplete_class;
1137
553
        uint32_t count;
1138
1139
553
        if (ce->ce_flags & ZEND_ACC_NOT_SERIALIZABLE) {
1140
23
          zend_throw_exception_ex(NULL, 0, "Serialization of '%s' is not allowed",
1141
23
            ZSTR_VAL(ce->name));
1142
23
          return;
1143
23
        }
1144
1145
530
        if (ce->ce_flags & ZEND_ACC_ENUM) {
1146
37
          PHP_CLASS_ATTRIBUTES;
1147
1148
37
          zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
1149
1150
37
          PHP_SET_CLASS_ATTRIBUTES(struc);
1151
37
          smart_str_appendl(buf, "E:", 2);
1152
37
          smart_str_append_unsigned(buf, ZSTR_LEN(class_name) + strlen(":") + Z_STRLEN_P(case_name_zval));
1153
37
          smart_str_appendl(buf, ":\"", 2);
1154
37
          smart_str_append(buf, class_name);
1155
37
          smart_str_appendc(buf, ':');
1156
37
          smart_str_append(buf, Z_STR_P(case_name_zval));
1157
37
          smart_str_appendl(buf, "\";", 2);
1158
37
          PHP_CLEANUP_CLASS_ATTRIBUTES();
1159
37
          return;
1160
37
        }
1161
1162
493
        if (ce->__serialize) {
1163
95
          zval retval, obj;
1164
95
          zend_string *key;
1165
95
          zval *data;
1166
95
          zend_ulong index;
1167
1168
95
          ZVAL_OBJ_COPY(&obj, Z_OBJ_P(struc));
1169
95
          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
93
          php_var_serialize_class_name(buf, &obj);
1178
93
          smart_str_append_unsigned(buf, zend_hash_num_elements(Z_ARRVAL(retval)));
1179
93
          smart_str_appendl(buf, ":{", 2);
1180
381
          ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(retval), index, key, data) {
1181
381
            if (!key) {
1182
104
              php_var_serialize_long(buf, index);
1183
104
            } else {
1184
40
              php_var_serialize_string(buf, ZSTR_VAL(key), ZSTR_LEN(key));
1185
40
            }
1186
1187
381
            if (Z_ISREF_P(data) && Z_REFCOUNT_P(data) == 1) {
1188
0
              data = Z_REFVAL_P(data);
1189
0
            }
1190
381
            php_var_serialize_intern(buf, data, var_hash, Z_REFCOUNT(retval) > 1, false);
1191
381
          } ZEND_HASH_FOREACH_END();
1192
93
          smart_str_appendc(buf, '}');
1193
1194
93
          zval_ptr_dtor(&obj);
1195
93
          zval_ptr_dtor(&retval);
1196
93
          return;
1197
95
        }
1198
1199
398
        if (ce->serialize != NULL) {
1200
          /* has custom handler */
1201
18
          unsigned char *serialized_data = NULL;
1202
18
          size_t serialized_length;
1203
1204
18
          if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash) == SUCCESS) {
1205
13
            char b1[32], b2[32];
1206
13
            char *s1 = zend_print_long_to_buf(b1 + sizeof(b1) - 1, ZSTR_LEN(Z_OBJCE_P(struc)->name));
1207
13
            size_t l1 = b1 + sizeof(b1) - 1 - s1;
1208
13
            char *s2 = zend_print_long_to_buf(b2 + sizeof(b2) - 1, serialized_length);
1209
13
            size_t l2 = b2 + sizeof(b2) - 1 - s2;
1210
13
            char *res = smart_str_extend(buf, 2 + l1 + 2 + ZSTR_LEN(Z_OBJCE_P(struc)->name) + 2 + l2 + 2 + serialized_length + 1);
1211
13
            res = zend_mempcpy(res, "C:", 2);
1212
13
            res = zend_mempcpy(res, s1, l1);
1213
13
            res = zend_mempcpy(res, ":\"", 2);
1214
13
            res = zend_mempcpy(res, ZSTR_VAL(Z_OBJCE_P(struc)->name), ZSTR_LEN(Z_OBJCE_P(struc)->name));
1215
13
            res = zend_mempcpy(res, "\":", 2);
1216
13
            res = zend_mempcpy(res, s2, l2);
1217
13
            res = zend_mempcpy(res, ":{", 2);
1218
13
            memcpy(res, (char *) serialized_data, serialized_length);
1219
13
            res[serialized_length] = '}';
1220
13
          } else {
1221
            /* Mark this value in the var_hash, to avoid creating references to it. */
1222
5
            zval *var_idx = zend_hash_index_find(&var_hash->ht,
1223
5
              (zend_ulong) (uintptr_t) Z_COUNTED_P(struc));
1224
5
            if (var_idx) {
1225
0
              ZVAL_LONG(var_idx, -1);
1226
0
            }
1227
5
            smart_str_appendl(buf, "N;", 2);
1228
5
          }
1229
18
          if (serialized_data) {
1230
13
            efree(serialized_data);
1231
13
          }
1232
18
          return;
1233
18
        }
1234
1235
380
        if (ce != PHP_IC_ENTRY) {
1236
380
          zval *zv = zend_hash_find_known_hash(&ce->function_table, ZSTR_KNOWN(ZEND_STR_SLEEP));
1237
1238
380
          if (zv) {
1239
90
            HashTable *ht;
1240
90
            zval tmp;
1241
1242
90
            ZVAL_OBJ_COPY(&tmp, Z_OBJ_P(struc));
1243
90
            if (!(ht = php_var_serialize_call_sleep(Z_OBJ(tmp), Z_FUNC_P(zv)))) {
1244
7
              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
7
              OBJ_RELEASE(Z_OBJ(tmp));
1250
7
              return;
1251
7
            }
1252
1253
83
            php_var_serialize_class(buf, &tmp, ht, var_hash);
1254
83
            zend_array_release(ht);
1255
83
            OBJ_RELEASE(Z_OBJ(tmp));
1256
83
            return;
1257
90
          }
1258
380
        }
1259
1260
290
        incomplete_class = php_var_serialize_class_name(buf, struc);
1261
1262
290
        if (Z_OBJ_P(struc)->properties == NULL
1263
223
         && Z_OBJ_HT_P(struc)->get_properties_for == NULL
1264
223
         && Z_OBJ_HT_P(struc)->get_properties == zend_std_get_properties
1265
223
         && !zend_object_is_lazy(Z_OBJ_P(struc))) {
1266
          /* Optimized version without rebuilding properties HashTable */
1267
148
          zend_object *obj = Z_OBJ_P(struc);
1268
148
          zend_class_entry *ce = obj->ce;
1269
148
          zend_property_info *prop_info;
1270
148
          zval *prop;
1271
148
          int i;
1272
1273
148
          count = ce->default_properties_count;
1274
893
          for (i = 0; i < ce->default_properties_count; i++) {
1275
745
            prop_info = ce->properties_info_table[i];
1276
745
            if (!prop_info) {
1277
5
              count--;
1278
5
              continue;
1279
5
            }
1280
740
            prop = OBJ_PROP(obj, prop_info->offset);
1281
740
            if (Z_TYPE_P(prop) == IS_UNDEF) {
1282
0
              count--;
1283
0
              continue;
1284
0
            }
1285
740
          }
1286
148
          if (count) {
1287
140
            smart_str_append_unsigned(buf, count);
1288
140
            smart_str_appendl(buf, ":{", 2);
1289
885
            for (i = 0; i < ce->default_properties_count; i++) {
1290
745
              prop_info = ce->properties_info_table[i];
1291
745
              if (!prop_info) {
1292
5
                continue;
1293
5
              }
1294
740
              prop = OBJ_PROP(obj, prop_info->offset);
1295
740
              if (Z_TYPE_P(prop) == IS_UNDEF) {
1296
0
                continue;
1297
0
              }
1298
1299
740
              php_var_serialize_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name));
1300
1301
740
              if (Z_ISREF_P(prop) && Z_REFCOUNT_P(prop) == 1) {
1302
0
                prop = Z_REFVAL_P(prop);
1303
0
              }
1304
1305
740
              php_var_serialize_intern(buf, prop, var_hash, false, false);
1306
740
            }
1307
140
            smart_str_appendc(buf, '}');
1308
140
          } else {
1309
8
            smart_str_appendl(buf, "0:{}", 4);
1310
8
          }
1311
148
          return;
1312
148
        }
1313
142
        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
142
        count = zend_array_count(myht);
1317
142
        if (count > 0 && incomplete_class) {
1318
0
          --count;
1319
0
        }
1320
142
        php_var_serialize_nested_data(buf, struc, myht, count, incomplete_class, var_hash, GC_REFCOUNT(myht) > 1);
1321
142
        zend_release_properties(myht);
1322
142
        return;
1323
290
      }
1324
35.5k
    case IS_ARRAY: {
1325
35.5k
      smart_str_appendl(buf, "a:", 2);
1326
35.5k
      myht = Z_ARRVAL_P(struc);
1327
35.5k
      bool rcn = !is_root && (in_rcn_array || GC_REFCOUNT(myht) > 1);
1328
35.5k
      GC_TRY_ADDREF(myht);
1329
35.5k
      php_var_serialize_nested_data(
1330
35.5k
        buf, struc, myht, zend_array_count(myht), /* incomplete_class */ false, var_hash,
1331
35.5k
        rcn);
1332
35.5k
      GC_TRY_DTOR_NO_REF(myht);
1333
35.5k
      return;
1334
290
    }
1335
3.11k
    case IS_REFERENCE:
1336
3.11k
      struc = Z_REFVAL_P(struc);
1337
3.11k
      goto again;
1338
0
    default:
1339
0
      smart_str_appendl(buf, "i:0;", 4);
1340
0
      return;
1341
62.7k
  }
1342
62.7k
}
1343
/* }}} */
1344
1345
PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data) /* {{{ */
1346
1.68k
{
1347
1.68k
  php_var_serialize_intern(buf, struc, *data, false, true);
1348
1.68k
  smart_str_0(buf);
1349
1.68k
}
1350
/* }}} */
1351
1352
1.68k
PHPAPI php_serialize_data_t php_var_serialize_init(void) {
1353
1.68k
  struct php_serialize_data *d;
1354
  /* fprintf(stderr, "SERIALIZE_INIT      == lock: %u, level: %u\n", BG(serialize_lock), BG(serialize).level); */
1355
1.68k
  if (BG(serialize_lock) || !BG(serialize).level) {
1356
1.68k
    d = emalloc(sizeof(struct php_serialize_data));
1357
1.68k
    zend_hash_init(&d->ht, 16, NULL, ZVAL_PTR_DTOR, 0);
1358
1.68k
    d->n = 0;
1359
1.68k
    if (!BG(serialize_lock)) {
1360
1.68k
      BG(serialize).data = d;
1361
1.68k
      BG(serialize).level = 1;
1362
1.68k
    }
1363
1.68k
  } else {
1364
0
    d = BG(serialize).data;
1365
0
    ++BG(serialize).level;
1366
0
  }
1367
1.68k
  return d;
1368
1.68k
}
1369
1370
1.68k
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
1.68k
  if (BG(serialize_lock) || BG(serialize).level == 1) {
1373
1.68k
    zend_hash_destroy(&d->ht);
1374
1.68k
    efree(d);
1375
1.68k
  }
1376
1.68k
  if (!BG(serialize_lock) && !--BG(serialize).level) {
1377
1.68k
    BG(serialize).data = NULL;
1378
1.68k
  }
1379
1.68k
}
1380
1381
/* {{{ Returns a string representation of variable (which can later be unserialized) */
1382
PHP_FUNCTION(serialize)
1383
1.68k
{
1384
1.68k
  zval *struc;
1385
1.68k
  php_serialize_data_t var_hash;
1386
1.68k
  smart_str buf = {0};
1387
1388
5.06k
  ZEND_PARSE_PARAMETERS_START(1, 1)
1389
6.75k
    Z_PARAM_ZVAL(struc)
1390
6.75k
  ZEND_PARSE_PARAMETERS_END();
1391
1392
1.68k
  PHP_VAR_SERIALIZE_INIT(var_hash);
1393
1.68k
  php_var_serialize(&buf, struc, &var_hash);
1394
1.68k
  PHP_VAR_SERIALIZE_DESTROY(var_hash);
1395
1396
1.68k
  if (EG(exception)) {
1397
76
    smart_str_free(&buf);
1398
76
    RETURN_THROWS();
1399
76
  }
1400
1401
1.61k
  RETURN_STR(smart_str_extract(&buf));
1402
1.61k
}
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.23k
{
1408
2.23k
  const unsigned char *p;
1409
2.23k
  php_unserialize_data_t var_hash;
1410
2.23k
  zval *retval;
1411
2.23k
  HashTable *class_hash = NULL, *prev_class_hash;
1412
2.23k
  zend_long prev_max_depth, prev_cur_depth;
1413
1414
2.23k
  if (buf_len == 0) {
1415
241
    RETURN_FALSE;
1416
241
  }
1417
1418
1.99k
  p = (const unsigned char*) buf;
1419
1.99k
  PHP_VAR_UNSERIALIZE_INIT(var_hash);
1420
1421
1.99k
  prev_class_hash = php_var_unserialize_get_allowed_classes(var_hash);
1422
1.99k
  prev_max_depth = php_var_unserialize_get_max_depth(var_hash);
1423
1.99k
  prev_cur_depth = php_var_unserialize_get_cur_depth(var_hash);
1424
1.99k
  if (options != NULL) {
1425
0
    zval *classes, *max_depth;
1426
1427
0
    classes = zend_hash_str_find_deref(options, "allowed_classes", sizeof("allowed_classes")-1);
1428
0
    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
0
    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
0
    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
0
    php_var_unserialize_set_allowed_classes(var_hash, class_hash);
1464
1465
0
    max_depth = zend_hash_str_find_deref(options, "max_depth", sizeof("max_depth") - 1);
1466
0
    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
0
  }
1482
1483
1.99k
  if (BG(unserialize).level > 1) {
1484
0
    retval = var_tmp_var(&var_hash);
1485
1.99k
  } else {
1486
1.99k
    retval = return_value;
1487
1.99k
  }
1488
1.99k
  if (!php_var_unserialize(retval, &p, p + buf_len, &var_hash)) {
1489
545
    if (!EG(exception)) {
1490
463
      php_error_docref(NULL, E_WARNING, "Error at offset " ZEND_LONG_FMT " of %zd bytes",
1491
463
        (zend_long)((char*)p - buf), buf_len);
1492
463
    }
1493
545
    if (BG(unserialize).level <= 1) {
1494
545
      zval_ptr_dtor(return_value);
1495
545
    }
1496
545
    RETVAL_FALSE;
1497
1.45k
  } else {
1498
1.45k
    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.45k
    if (BG(unserialize).level > 1) {
1505
0
      ZVAL_COPY(return_value, retval);
1506
1.45k
    } else if (Z_REFCOUNTED_P(return_value)) {
1507
1.28k
      zend_refcounted *ref = Z_COUNTED_P(return_value);
1508
1.28k
      gc_check_possible_root(ref);
1509
1.28k
    }
1510
1.45k
  }
1511
1512
1.99k
cleanup:
1513
1.99k
  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
1.99k
  php_var_unserialize_set_allowed_classes(var_hash, prev_class_hash);
1520
1.99k
  php_var_unserialize_set_max_depth(var_hash, prev_max_depth);
1521
1.99k
  php_var_unserialize_set_cur_depth(var_hash, prev_cur_depth);
1522
1.99k
  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
1.99k
  if (Z_ISREF_P(return_value)) {
1528
3
    zend_unwrap_reference(return_value);
1529
3
  }
1530
1.99k
}
1531
/* }}} */
1532
1533
/* {{{ Takes a string representation of variable and recreates it */
1534
PHP_FUNCTION(unserialize)
1535
2.24k
{
1536
2.24k
  char *buf = NULL;
1537
2.24k
  size_t buf_len;
1538
2.24k
  HashTable *options = NULL;
1539
1540
6.73k
  ZEND_PARSE_PARAMETERS_START(1, 2)
1541
8.97k
    Z_PARAM_STRING(buf, buf_len)
1542
2.24k
    Z_PARAM_OPTIONAL
1543
4.49k
    Z_PARAM_ARRAY_HT(options)
1544
2.24k
  ZEND_PARSE_PARAMETERS_END();
1545
1546
2.23k
  php_unserialize_with_options(return_value, buf, buf_len, options, "unserialize");
1547
2.23k
}
1548
/* }}} */
1549
1550
/* {{{ Returns the allocated by PHP memory */
1551
47
PHP_FUNCTION(memory_get_usage) {
1552
47
  bool real_usage = 0;
1553
1554
141
  ZEND_PARSE_PARAMETERS_START(0, 1)
1555
141
    Z_PARAM_OPTIONAL
1556
141
    Z_PARAM_BOOL(real_usage)
1557
47
  ZEND_PARSE_PARAMETERS_END();
1558
1559
47
  RETURN_LONG(zend_memory_usage(real_usage));
1560
47
}
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
}