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