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