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