1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2014 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 TSRMLS_DC) /* {{{ */
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(key->val, key->len);
64        php_printf("\"]=>\n");
65        }
66    php_var_dump(zv, level + 2 TSRMLS_CC);
67}
68/* }}} */
69
70static void php_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level TSRMLS_DC) /* {{{ */
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(key->val, key->len);
89            php_printf("\"");
90        }
91        ZEND_PUTS("]=>\n");
92    }
93    php_var_dump(zv, level + 2 TSRMLS_CC);
94}
95/* }}} */
96
97PHPAPI void php_var_dump(zval *struc, int level TSRMLS_DC) /* {{{ */
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 TSRMLS_CC);
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            myht = Z_OBJDEBUG_P(struc, is_temp);
160            if (myht && ++myht->u.v.nApplyCount > 1) {
161                PUTS("*RECURSION*\n");
162                --myht->u.v.nApplyCount;
163                return;
164            }
165
166            class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc) TSRMLS_CC);
167            php_printf("%sobject(%s)#%d (%d) {\n", COMMON, class_name->val, 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 TSRMLS_CC);
177                } ZEND_HASH_FOREACH_END();
178                --myht->u.v.nApplyCount;
179                if (is_temp) {
180                    zend_hash_destroy(myht);
181                    efree(myht);
182                }
183            }
184            if (level > 1) {
185                php_printf("%*c", level-1, ' ');
186            }
187            PUTS("}\n");
188            break;
189        case IS_RESOURCE: {
190            const char *type_name = zend_rsrc_list_get_rsrc_type(Z_RES_P(struc) TSRMLS_CC);
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() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
218        return;
219    }
220
221    for (i = 0; i < argc; i++) {
222        php_var_dump(&args[i], 1 TSRMLS_CC);
223    }
224}
225/* }}} */
226
227static void zval_array_element_dump(zval *zv, zend_ulong index, zend_string *key, int level TSRMLS_DC) /* {{{ */
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(key->val, key->len);
234        php_printf("\"]=>\n");
235    }
236    php_debug_zval_dump(zv, level + 2 TSRMLS_CC);
237}
238/* }}} */
239
240static void zval_object_property_dump(zval *zv, zend_ulong index, zend_string *key, int level TSRMLS_DC) /* {{{ */
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 TSRMLS_CC);
262}
263/* }}} */
264
265PHPAPI void php_debug_zval_dump(zval *struc, int level TSRMLS_DC) /* {{{ */
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 TSRMLS_CC);
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) TSRMLS_CC);
335        php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, class_name->val, 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 TSRMLS_CC);
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) TSRMLS_CC);
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() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
380        return;
381    }
382
383    for (i = 0; i < argc; i++) {
384        php_debug_zval_dump(&args[i], 1 TSRMLS_CC);
385    }
386}
387/* }}} */
388
389#define buffer_append_spaces(buf, num_spaces) \
390    do { \
391        char *tmp_spaces; \
392        int 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 TSRMLS_DC) /* {{{ */
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->val, key->len, 0, "'\\", 2 TSRMLS_CC);
408        tmp_str = php_str_to_str_ex(ckey->val, ckey->len, "\0", 1, "' . \"\\0\" . '", 12, 0, NULL);
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 TSRMLS_CC);
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 TSRMLS_DC) /* {{{ */
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(prop_name, prop_name_len, 0, "'\\", 2 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_DC) /* {{{ */
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_STRVAL_P(struc), Z_STRLEN_P(struc), 0, "'\\", 2 TSRMLS_CC);
482            ztmp2 = php_str_to_str_ex(ztmp->val, ztmp->len, "\0", 1, "' . \"\\0\" . '", 12, 0, NULL);
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 TSRMLS_CC);
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 TSRMLS_CC);
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 TSRMLS_DC) /* {{{ */
561{
562    smart_str buf = {0};
563    php_var_export_ex(struc, level, &buf TSRMLS_CC);
564    smart_str_0(&buf);
565    PHPWRITE(buf.s->val, buf.s->len);
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() TSRMLS_CC, "z|b", &var, &return_output) == FAILURE) {
580        return;
581    }
582
583    php_var_export_ex(var, 1, &buf TSRMLS_CC);
584    smart_str_0 (&buf);
585
586    if (return_output) {
587        RETURN_STR(buf.s);
588    } else {
589        PHPWRITE(buf.s->val, buf.s->len);
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 TSRMLS_DC);
596
597static inline uint32_t php_add_var_hash(php_serialize_data_t data, zval *var TSRMLS_DC) /* {{{ */
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 TSRMLS_DC) /* {{{ */
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, class_name->len);
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 TSRMLS_DC) /* {{{ */
677{
678    uint32_t count;
679    zend_bool incomplete_class;
680
681    incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC);
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, *name;
694        zval nval, *nvalp;
695        HashTable *propers, *ht;
696
697        ZVAL_NULL(&nval);
698        nvalp = &nval;
699
700        ht = HASH_OF(retval_ptr);
701        ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, name) {
702            if (incomplete_class && strcmp(key->val, MAGIC_MEMBER) == 0) {
703                continue;
704            }
705
706            if (Z_TYPE_P(name) != IS_STRING) {
707                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize.");
708                /* we should still add element even if it's not OK,
709                 * since we already wrote the length of the array before */
710                smart_str_appendl(buf,"N;", 2);
711                continue;
712            }
713            propers = Z_OBJPROP_P(struc);
714            if ((d = zend_hash_find(propers, Z_STR_P(name))) != NULL) {
715                if (Z_TYPE_P(d) == IS_INDIRECT) {
716                    d = Z_INDIRECT_P(d);
717                    if (Z_TYPE_P(d) == IS_UNDEF) {
718                        continue;
719                    }
720                }
721                php_var_serialize_string(buf, Z_STRVAL_P(name), Z_STRLEN_P(name));
722                php_var_serialize_intern(buf, d, var_hash TSRMLS_CC);
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(ce->name->val, ce->name->len, Z_STRVAL_P(name), Z_STRLEN_P(name), ce->type & ZEND_INTERNAL_CLASS);
730                        if ((d = zend_hash_find(propers, priv_name)) != NULL) {
731                            if (Z_TYPE_P(d) == IS_INDIRECT) {
732                                d = Z_INDIRECT_P(d);
733                                if (Z_ISUNDEF_P(d)) {
734                                    break;
735                                }
736                            }
737                            php_var_serialize_string(buf, priv_name->val, priv_name->len);
738                            zend_string_free(priv_name);
739                            php_var_serialize_intern(buf, d, var_hash TSRMLS_CC);
740                            break;
741                        }
742                        zend_string_free(priv_name);
743                        prot_name = zend_mangle_property_name("*", 1, Z_STRVAL_P(name), Z_STRLEN_P(name), ce->type & ZEND_INTERNAL_CLASS);
744                        if ((d = zend_hash_find(propers, prot_name)) != NULL) {
745                            if (Z_TYPE_P(d) == IS_INDIRECT) {
746                                d = Z_INDIRECT_P(d);
747                                if (Z_TYPE_P(d) == IS_UNDEF) {
748                                    zend_string_free(prot_name);
749                                    break;
750                                }
751                            }
752                            php_var_serialize_string(buf, prot_name->val, prot_name->len);
753                            zend_string_free(prot_name);
754                            php_var_serialize_intern(buf, d, var_hash TSRMLS_CC);
755                            break;
756                        }
757                        zend_string_free(prot_name);
758                        php_var_serialize_string(buf, Z_STRVAL_P(name), Z_STRLEN_P(name));
759                        php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC);
760                        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_P(name));
761                    } while (0);
762                } else {
763                    php_var_serialize_string(buf, Z_STRVAL_P(name), Z_STRLEN_P(name));
764                    php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC);
765                }
766            }
767        } ZEND_HASH_FOREACH_END();
768    }
769    smart_str_appendc(buf, '}');
770}
771/* }}} */
772
773static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash TSRMLS_DC) /* {{{ */
774{
775    uint32_t var_already;
776    HashTable *myht;
777
778    if (EG(exception)) {
779        return;
780    }
781
782    if (var_hash && (var_already = php_add_var_hash(var_hash, struc TSRMLS_CC))) {
783        if (Z_ISREF_P(struc)) {
784            smart_str_appendl(buf, "R:", 2);
785            smart_str_append_long(buf, var_already);
786            smart_str_appendc(buf, ';');
787            return;
788        } else if (Z_TYPE_P(struc) == IS_OBJECT) {
789            smart_str_appendl(buf, "r:", 2);
790            smart_str_append_long(buf, var_already);
791            smart_str_appendc(buf, ';');
792            return;
793        }
794    }
795
796again:
797    switch (Z_TYPE_P(struc)) {
798        case IS_FALSE:
799            smart_str_appendl(buf, "b:0;", 4);
800            return;
801
802        case IS_TRUE:
803            smart_str_appendl(buf, "b:1;", 4);
804            return;
805
806        case IS_NULL:
807            smart_str_appendl(buf, "N;", 2);
808            return;
809
810        case IS_LONG:
811            php_var_serialize_long(buf, Z_LVAL_P(struc));
812            return;
813
814        case IS_DOUBLE: {
815                char *s;
816
817                smart_str_appendl(buf, "d:", 2);
818                s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1);
819                php_gcvt(Z_DVAL_P(struc), PG(serialize_precision), '.', 'E', s);
820                smart_str_appends(buf, s);
821                smart_str_appendc(buf, ';');
822                efree(s);
823                return;
824            }
825
826        case IS_STRING:
827            php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
828            return;
829
830        case IS_OBJECT: {
831                zval retval;
832                zval fname;
833                int res;
834                zend_class_entry *ce = Z_OBJCE_P(struc);
835
836                if (ce->serialize != NULL) {
837                    /* has custom handler */
838                    unsigned char *serialized_data = NULL;
839                    size_t serialized_length;
840
841                    if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash TSRMLS_CC) == SUCCESS) {
842                        smart_str_appendl(buf, "C:", 2);
843                        smart_str_append_unsigned(buf, Z_OBJCE_P(struc)->name->len);
844                        smart_str_appendl(buf, ":\"", 2);
845                        smart_str_append(buf, Z_OBJCE_P(struc)->name);
846                        smart_str_appendl(buf, "\":", 2);
847
848                        smart_str_append_unsigned(buf, serialized_length);
849                        smart_str_appendl(buf, ":{", 2);
850                        smart_str_appendl(buf, (char *) serialized_data, serialized_length);
851                        smart_str_appendc(buf, '}');
852                    } else {
853                        smart_str_appendl(buf, "N;", 2);
854                    }
855                    if (serialized_data) {
856                        efree(serialized_data);
857                    }
858                    return;
859                }
860
861                if (ce && ce != PHP_IC_ENTRY && zend_hash_str_exists(&ce->function_table, "__sleep", sizeof("__sleep")-1)) {
862                    ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1);
863                    BG(serialize_lock)++;
864                    res = call_user_function_ex(CG(function_table), struc, &fname, &retval, 0, 0, 1, NULL TSRMLS_CC);
865                    BG(serialize_lock)--;
866                    zval_dtor(&fname);
867
868                    if (EG(exception)) {
869                        zval_ptr_dtor(&retval);
870                        return;
871                    }
872
873                    if (res == SUCCESS) {
874                        if (Z_TYPE(retval) != IS_UNDEF) {
875                            if (HASH_OF(&retval)) {
876                                php_var_serialize_class(buf, struc, &retval, var_hash TSRMLS_CC);
877                            } else {
878                                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize");
879                                /* we should still add element even if it's not OK,
880                                 * since we already wrote the length of the array before */
881                                smart_str_appendl(buf,"N;", 2);
882                            }
883                            zval_ptr_dtor(&retval);
884                        }
885                        return;
886                    }
887                    zval_ptr_dtor(&retval);
888                }
889
890                /* fall-through */
891            }
892        case IS_ARRAY: {
893            uint32_t i;
894            zend_bool incomplete_class = 0;
895            if (Z_TYPE_P(struc) == IS_ARRAY) {
896                smart_str_appendl(buf, "a:", 2);
897                myht = HASH_OF(struc);
898            } else {
899                incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC);
900                myht = Z_OBJPROP_P(struc);
901            }
902            /* count after serializing name, since php_var_serialize_class_name
903             * changes the count if the variable is incomplete class */
904            i = myht ? zend_hash_num_elements(myht) : 0;
905            if (i > 0 && incomplete_class) {
906                --i;
907            }
908            smart_str_append_unsigned(buf, i);
909            smart_str_appendl(buf, ":{", 2);
910            if (i > 0) {
911                zend_string *key;
912                zval *data;
913                zend_ulong index;
914
915                ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) {
916
917                    if (incomplete_class && strcmp(key->val, MAGIC_MEMBER) == 0) {
918                        continue;
919                    }
920
921                    if (!key) {
922                        php_var_serialize_long(buf, index);
923                    } else {
924                        php_var_serialize_string(buf, key->val, key->len);
925                    }
926
927                    /* we should still add element even if it's not OK,
928                     * since we already wrote the length of the array before */
929                    if ((Z_TYPE_P(data) == IS_ARRAY && Z_TYPE_P(struc) == IS_ARRAY && Z_ARR_P(data) == Z_ARR_P(struc))
930                        || (Z_TYPE_P(data) == IS_ARRAY && Z_ARRVAL_P(data)->u.v.nApplyCount > 1)
931                    ) {
932                        smart_str_appendl(buf, "N;", 2);
933                    } else {
934                        if (Z_TYPE_P(data) == IS_ARRAY && ZEND_HASH_APPLY_PROTECTION(Z_ARRVAL_P(data))) {
935                            Z_ARRVAL_P(data)->u.v.nApplyCount++;
936                        }
937                        php_var_serialize_intern(buf, data, var_hash TSRMLS_CC);
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                    }
942                } ZEND_HASH_FOREACH_END();
943            }
944            smart_str_appendc(buf, '}');
945            return;
946        }
947        case IS_REFERENCE:
948            struc = Z_REFVAL_P(struc);
949            goto again;
950        default:
951            smart_str_appendl(buf, "i:0;", 4);
952            return;
953    }
954}
955/* }}} */
956
957PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t *data TSRMLS_DC) /* {{{ */
958{
959    php_var_serialize_intern(buf, struc, *data TSRMLS_CC);
960    smart_str_0(buf);
961}
962/* }}} */
963
964/* {{{ proto string serialize(mixed variable)
965   Returns a string representation of variable (which can later be unserialized) */
966PHP_FUNCTION(serialize)
967{
968    zval *struc;
969    php_serialize_data_t var_hash;
970    smart_str buf = {0};
971
972    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &struc) == FAILURE) {
973        return;
974    }
975
976    PHP_VAR_SERIALIZE_INIT(var_hash);
977    php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
978    PHP_VAR_SERIALIZE_DESTROY(var_hash);
979
980    if (EG(exception)) {
981        smart_str_free(&buf);
982        RETURN_FALSE;
983    }
984
985    if (buf.s) {
986        RETURN_STR(buf.s);
987    } else {
988        RETURN_NULL();
989    }
990}
991/* }}} */
992
993/* {{{ proto mixed unserialize(string variable_representation)
994   Takes a string representation of variable and recreates it */
995PHP_FUNCTION(unserialize)
996{
997    char *buf = NULL;
998    size_t buf_len;
999    const unsigned char *p;
1000    php_unserialize_data_t var_hash;
1001
1002    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
1003        RETURN_FALSE;
1004    }
1005
1006    if (buf_len == 0) {
1007        RETURN_FALSE;
1008    }
1009
1010    p = (const unsigned char*) buf;
1011    PHP_VAR_UNSERIALIZE_INIT(var_hash);
1012    if (!php_var_unserialize(return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) {
1013        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1014        zval_dtor(return_value);
1015        if (!EG(exception)) {
1016            php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset " ZEND_LONG_FMT " of %d bytes", (zend_long)((char*)p - buf), buf_len);
1017        }
1018        RETURN_FALSE;
1019    }
1020    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
1021}
1022/* }}} */
1023
1024/* {{{ proto int memory_get_usage([real_usage])
1025   Returns the allocated by PHP memory */
1026PHP_FUNCTION(memory_get_usage) {
1027    zend_bool real_usage = 0;
1028
1029    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &real_usage) == FAILURE) {
1030        RETURN_FALSE;
1031    }
1032
1033    RETURN_LONG(zend_memory_usage(real_usage TSRMLS_CC));
1034}
1035/* }}} */
1036
1037/* {{{ proto int memory_get_peak_usage([real_usage])
1038   Returns the peak allocated by PHP memory */
1039PHP_FUNCTION(memory_get_peak_usage) {
1040    zend_bool real_usage = 0;
1041
1042    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &real_usage) == FAILURE) {
1043        RETURN_FALSE;
1044    }
1045
1046    RETURN_LONG(zend_memory_peak_usage(real_usage TSRMLS_CC));
1047}
1048/* }}} */
1049
1050/*
1051 * Local variables:
1052 * tab-width: 4
1053 * c-basic-offset: 4
1054 * End:
1055 * vim600: sw=4 ts=4 fdm=marker
1056 * vim<600: sw=4 ts=4
1057 */
1058