1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 5                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2013 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 "php_smart_str.h"
32#include "basic_functions.h"
33#include "php_incomplete_class.h"
34
35#define COMMON (Z_ISREF_PP(struc) ? "&" : "")
36/* }}} */
37
38static int php_array_element_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
39{
40    int level;
41
42    level = va_arg(args, int);
43
44    if (hash_key->nKeyLength == 0) { /* numeric key */
45        php_printf("%*c[%ld]=>\n", level + 1, ' ', hash_key->h);
46    } else { /* string key */
47        php_printf("%*c[\"", level + 1, ' ');
48        PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1);
49        php_printf("\"]=>\n");
50    }
51    php_var_dump(zv, level + 2 TSRMLS_CC);
52    return 0;
53}
54/* }}} */
55
56static int php_object_property_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
57{
58    int level;
59    char *prop_name, *class_name;
60
61    level = va_arg(args, int);
62
63    if (hash_key->nKeyLength == 0) { /* numeric key */
64        php_printf("%*c[%ld]=>\n", level + 1, ' ', hash_key->h);
65    } else { /* string key */
66        int unmangle = zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name);
67        php_printf("%*c[", level + 1, ' ');
68
69        if (class_name && unmangle == SUCCESS) {
70            if (class_name[0] == '*') {
71                php_printf("\"%s\":protected", prop_name);
72            } else {
73                php_printf("\"%s\":\"%s\":private", prop_name, class_name);
74            }
75        } else {
76            php_printf("\"");
77            PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1);
78            php_printf("\"");
79        }
80        ZEND_PUTS("]=>\n");
81    }
82    php_var_dump(zv, level + 2 TSRMLS_CC);
83    return 0;
84}
85/* }}} */
86
87PHPAPI void php_var_dump(zval **struc, int level TSRMLS_DC) /* {{{ */
88{
89    HashTable *myht;
90    char *class_name;
91    zend_uint class_name_len;
92    int (*php_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*);
93    int is_temp;
94
95    if (level > 1) {
96        php_printf("%*c", level - 1, ' ');
97    }
98
99    switch (Z_TYPE_PP(struc)) {
100    case IS_BOOL:
101        php_printf("%sbool(%s)\n", COMMON, Z_LVAL_PP(struc) ? "true" : "false");
102        break;
103    case IS_NULL:
104        php_printf("%sNULL\n", COMMON);
105        break;
106    case IS_LONG:
107        php_printf("%sint(%ld)\n", COMMON, Z_LVAL_PP(struc));
108        break;
109    case IS_DOUBLE:
110        php_printf("%sfloat(%.*G)\n", COMMON, (int) EG(precision), Z_DVAL_PP(struc));
111        break;
112    case IS_STRING:
113        php_printf("%sstring(%d) \"", COMMON, Z_STRLEN_PP(struc));
114        PHPWRITE(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc));
115        PUTS("\"\n");
116        break;
117    case IS_ARRAY:
118        myht = Z_ARRVAL_PP(struc);
119        if (++myht->nApplyCount > 1) {
120            PUTS("*RECURSION*\n");
121            --myht->nApplyCount;
122            return;
123        }
124        php_printf("%sarray(%d) {\n", COMMON, zend_hash_num_elements(myht));
125        php_element_dump_func = php_array_element_dump;
126        is_temp = 0;
127        goto head_done;
128    case IS_OBJECT:
129        myht = Z_OBJDEBUG_PP(struc, is_temp);
130        if (myht && ++myht->nApplyCount > 1) {
131            PUTS("*RECURSION*\n");
132            --myht->nApplyCount;
133            return;
134        }
135
136        Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC);
137        php_printf("%sobject(%s)#%d (%d) {\n", COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0);
138        efree(class_name);
139        php_element_dump_func = php_object_property_dump;
140head_done:
141        if (myht) {
142            zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_element_dump_func, 1, level);
143            --myht->nApplyCount;
144            if (is_temp) {
145                zend_hash_destroy(myht);
146                efree(myht);
147            }
148        }
149        if (level > 1) {
150            php_printf("%*c", level-1, ' ');
151        }
152        PUTS("}\n");
153        break;
154    case IS_RESOURCE: {
155        char *type_name;
156
157        type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC);
158        php_printf("%sresource(%ld) of type (%s)\n", COMMON, Z_LVAL_PP(struc), type_name ? type_name : "Unknown");
159        break;
160    }
161    default:
162        php_printf("%sUNKNOWN:0\n", COMMON);
163        break;
164    }
165}
166/* }}} */
167
168/* {{{ proto void var_dump(mixed var)
169   Dumps a string representation of variable to output */
170PHP_FUNCTION(var_dump)
171{
172    zval ***args;
173    int argc;
174    int i;
175
176    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
177        return;
178    }
179
180    for (i = 0; i < argc; i++) {
181        php_var_dump(args[i], 1 TSRMLS_CC);
182    }
183    efree(args);
184}
185/* }}} */
186
187static int zval_array_element_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
188{
189    int level;
190
191    level = va_arg(args, int);
192
193    if (hash_key->nKeyLength == 0) { /* numeric key */
194        php_printf("%*c[%ld]=>\n", level + 1, ' ', hash_key->h);
195    } else { /* string key */
196        /* XXX: perphaps when we are inside the class we should permit access to
197         * private & protected values
198         */
199        if (va_arg(args, int) && hash_key->arKey[0] == '\0') {
200            return 0;
201        }
202        php_printf("%*c[\"", level + 1, ' ');
203        PHPWRITE(hash_key->arKey, hash_key->nKeyLength - 1);
204        php_printf("\"]=>\n");
205    }
206    php_debug_zval_dump(zv, level + 2 TSRMLS_CC);
207    return 0;
208}
209/* }}} */
210
211static int zval_object_property_dump(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
212{
213    int level;
214    char *prop_name, *class_name;
215
216    level = va_arg(args, int);
217
218    if (hash_key->nKeyLength == 0) { /* numeric key */
219        php_printf("%*c[%ld]=>\n", level + 1, ' ', hash_key->h);
220    } else { /* string key */
221        zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &prop_name);
222        php_printf("%*c[", level + 1, ' ');
223
224        if (class_name) {
225            if (class_name[0] == '*') {
226                php_printf("\"%s\":protected", prop_name);
227            } else {
228                php_printf("\"%s\":\"%s\":private", prop_name, class_name);
229            }
230        } else {
231            php_printf("\"%s\"", prop_name);
232        }
233        ZEND_PUTS("]=>\n");
234    }
235    php_debug_zval_dump(zv, level + 2 TSRMLS_CC);
236    return 0;
237}
238/* }}} */
239
240PHPAPI void php_debug_zval_dump(zval **struc, int level TSRMLS_DC) /* {{{ */
241{
242    HashTable *myht = NULL;
243    char *class_name;
244    zend_uint class_name_len;
245    int (*zval_element_dump_func)(zval** TSRMLS_DC, int, va_list, zend_hash_key*);
246    int is_temp = 0;
247
248    if (level > 1) {
249        php_printf("%*c", level - 1, ' ');
250    }
251
252    switch (Z_TYPE_PP(struc)) {
253    case IS_BOOL:
254        php_printf("%sbool(%s) refcount(%u)\n", COMMON, Z_LVAL_PP(struc)?"true":"false", Z_REFCOUNT_PP(struc));
255        break;
256    case IS_NULL:
257        php_printf("%sNULL refcount(%u)\n", COMMON, Z_REFCOUNT_PP(struc));
258        break;
259    case IS_LONG:
260        php_printf("%slong(%ld) refcount(%u)\n", COMMON, Z_LVAL_PP(struc), Z_REFCOUNT_PP(struc));
261        break;
262    case IS_DOUBLE:
263        php_printf("%sdouble(%.*G) refcount(%u)\n", COMMON, (int) EG(precision), Z_DVAL_PP(struc), Z_REFCOUNT_PP(struc));
264        break;
265    case IS_STRING:
266        php_printf("%sstring(%d) \"", COMMON, Z_STRLEN_PP(struc));
267        PHPWRITE(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc));
268        php_printf("\" refcount(%u)\n", Z_REFCOUNT_PP(struc));
269        break;
270    case IS_ARRAY:
271        myht = Z_ARRVAL_PP(struc);
272        if (myht->nApplyCount > 1) {
273            PUTS("*RECURSION*\n");
274            return;
275        }
276        php_printf("%sarray(%d) refcount(%u){\n", COMMON, zend_hash_num_elements(myht), Z_REFCOUNT_PP(struc));
277        zval_element_dump_func = zval_array_element_dump;
278        goto head_done;
279    case IS_OBJECT:
280        myht = Z_OBJDEBUG_PP(struc, is_temp);
281        if (myht && myht->nApplyCount > 1) {
282            PUTS("*RECURSION*\n");
283            return;
284        }
285        if (Z_OBJ_HANDLER_PP(struc, get_class_name)) {
286            Z_OBJ_HANDLER_PP(struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC);
287            php_printf("%sobject(%s)#%d (%d) refcount(%u){\n", COMMON, class_name, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0, Z_REFCOUNT_PP(struc));
288            efree(class_name);
289        } else {
290            php_printf("%sobject(unknown class)#%d (%d) refcount(%u){\n", COMMON, Z_OBJ_HANDLE_PP(struc), myht ? zend_hash_num_elements(myht) : 0, Z_REFCOUNT_PP(struc));
291        }
292        zval_element_dump_func = zval_object_property_dump;
293head_done:
294        if (myht) {
295            zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) zval_element_dump_func, 1, level, (Z_TYPE_PP(struc) == IS_ARRAY ? 0 : 1));
296            if (is_temp) {
297                zend_hash_destroy(myht);
298                efree(myht);
299            }
300        }
301        if (level > 1) {
302            php_printf("%*c", level - 1, ' ');
303        }
304        PUTS("}\n");
305        break;
306    case IS_RESOURCE: {
307        char *type_name;
308
309        type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(struc) TSRMLS_CC);
310        php_printf("%sresource(%ld) of type (%s) refcount(%u)\n", COMMON, Z_LVAL_PP(struc), type_name ? type_name : "Unknown", Z_REFCOUNT_PP(struc));
311        break;
312    }
313    default:
314        php_printf("%sUNKNOWN:0\n", COMMON);
315        break;
316    }
317}
318/* }}} */
319
320/* {{{ proto void debug_zval_dump(mixed var)
321   Dumps a string representation of an internal zend value to output. */
322PHP_FUNCTION(debug_zval_dump)
323{
324    zval ***args;
325    int argc;
326    int i;
327
328    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
329        return;
330    }
331
332    for (i = 0; i < argc; i++) {
333        php_debug_zval_dump(args[i], 1 TSRMLS_CC);
334    }
335    efree(args);
336}
337/* }}} */
338
339#define buffer_append_spaces(buf, num_spaces) \
340    do { \
341        char *tmp_spaces; \
342        int tmp_spaces_len; \
343        tmp_spaces_len = spprintf(&tmp_spaces, 0,"%*c", num_spaces, ' '); \
344        smart_str_appendl(buf, tmp_spaces, tmp_spaces_len); \
345        efree(tmp_spaces); \
346    } while(0);
347
348static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
349{
350    int level;
351    smart_str *buf;
352
353    level = va_arg(args, int);
354    buf = va_arg(args, smart_str *);
355
356    if (hash_key->nKeyLength == 0) { /* numeric key */
357        buffer_append_spaces(buf, level+1);
358        smart_str_append_long(buf, (long) hash_key->h);
359        smart_str_appendl(buf, " => ", 4);
360    } else { /* string key */
361        char *key, *tmp_str;
362        int key_len, tmp_len;
363        key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
364        tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
365
366        buffer_append_spaces(buf, level + 1);
367
368        smart_str_appendc(buf, '\'');
369        smart_str_appendl(buf, tmp_str, tmp_len);
370        smart_str_appendl(buf, "' => ", 5);
371
372        efree(key);
373        efree(tmp_str);
374    }
375    php_var_export_ex(zv, level + 2, buf TSRMLS_CC);
376
377    smart_str_appendc(buf, ',');
378    smart_str_appendc(buf, '\n');
379
380    return 0;
381}
382/* }}} */
383
384static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
385{
386    int level;
387    smart_str *buf;
388
389    level = va_arg(args, int);
390    buf = va_arg(args, smart_str *);
391
392    buffer_append_spaces(buf, level + 2);
393    if (hash_key->nKeyLength != 0) {
394        char *class_name, /* ignored, but must be passed to unmangle */
395             *pname,
396             *pname_esc;
397        int  pname_esc_len;
398
399        zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1,
400                &class_name, &pname);
401        pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0,
402            "'\\", 2 TSRMLS_CC);
403
404        smart_str_appendc(buf, '\'');
405        smart_str_appendl(buf, pname_esc, pname_esc_len);
406        smart_str_appendc(buf, '\'');
407        efree(pname_esc);
408    } else {
409        smart_str_append_long(buf, hash_key->h);
410    }
411
412    smart_str_appendl(buf, " => ", 4);
413    php_var_export_ex(zv, level + 2, buf TSRMLS_CC);
414    smart_str_appendc(buf, ',');
415    smart_str_appendc(buf, '\n');
416
417    return 0;
418}
419/* }}} */
420
421PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) /* {{{ */
422{
423    HashTable *myht;
424    char *tmp_str, *tmp_str2;
425    int tmp_len, tmp_len2;
426    char *class_name;
427    zend_uint class_name_len;
428
429    switch (Z_TYPE_PP(struc)) {
430    case IS_BOOL:
431    if (Z_LVAL_PP(struc)) {
432        smart_str_appendl(buf, "true", 4);
433    } else {
434        smart_str_appendl(buf, "false", 5);
435    }
436        break;
437    case IS_NULL:
438        smart_str_appendl(buf, "NULL", 4);
439        break;
440    case IS_LONG:
441        smart_str_append_long(buf, Z_LVAL_PP(struc));
442        break;
443    case IS_DOUBLE:
444        tmp_len = spprintf(&tmp_str, 0,"%.*H", (int) EG(precision), Z_DVAL_PP(struc));
445        smart_str_appendl(buf, tmp_str, tmp_len);
446        efree(tmp_str);
447        break;
448    case IS_STRING:
449        tmp_str = php_addcslashes(Z_STRVAL_PP(struc), Z_STRLEN_PP(struc), &tmp_len, 0, "'\\", 2 TSRMLS_CC);
450        tmp_str2 = php_str_to_str_ex(tmp_str, tmp_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len2, 0, NULL);
451
452        smart_str_appendc(buf, '\'');
453        smart_str_appendl(buf, tmp_str2, tmp_len2);
454        smart_str_appendc(buf, '\'');
455
456        efree(tmp_str2);
457        efree(tmp_str);
458        break;
459    case IS_ARRAY:
460        myht = Z_ARRVAL_PP(struc);
461        if (level > 1) {
462            smart_str_appendc(buf, '\n');
463            buffer_append_spaces(buf, level - 1);
464        }
465        smart_str_appendl(buf, "array (\n", 8);
466        zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_array_element_export, 2, level, buf);
467
468        if (level > 1) {
469            buffer_append_spaces(buf, level - 1);
470        }
471        smart_str_appendc(buf, ')');
472
473        break;
474    case IS_OBJECT:
475        myht = Z_OBJPROP_PP(struc);
476        if (level > 1) {
477            smart_str_appendc(buf, '\n');
478            buffer_append_spaces(buf, level - 1);
479        }
480        Z_OBJ_HANDLER(**struc, get_class_name)(*struc, &class_name, &class_name_len, 0 TSRMLS_CC);
481
482        smart_str_appendl(buf, class_name, class_name_len);
483        smart_str_appendl(buf, "::__set_state(array(\n", 21);
484
485        efree(class_name);
486        if (myht) {
487            zend_hash_apply_with_arguments(myht TSRMLS_CC, (apply_func_args_t) php_object_element_export, 2, level, buf);
488        }
489        if (level > 1) {
490            buffer_append_spaces(buf, level - 1);
491        }
492        smart_str_appendl(buf, "))", 2);
493
494        break;
495    default:
496        smart_str_appendl(buf, "NULL", 4);
497        break;
498    }
499}
500/* }}} */
501
502/* FOR BC reasons, this will always perform and then print */
503PHPAPI void php_var_export(zval **struc, int level TSRMLS_DC) /* {{{ */
504{
505    smart_str buf = {0};
506    php_var_export_ex(struc, level, &buf TSRMLS_CC);
507    smart_str_0 (&buf);
508    PHPWRITE(buf.c, buf.len);
509    smart_str_free(&buf);
510}
511/* }}} */
512
513/* {{{ proto mixed var_export(mixed var [, bool return])
514   Outputs or returns a string representation of a variable */
515PHP_FUNCTION(var_export)
516{
517    zval *var;
518    zend_bool return_output = 0;
519    smart_str buf = {0};
520
521    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &var, &return_output) == FAILURE) {
522        return;
523    }
524
525    php_var_export_ex(&var, 1, &buf TSRMLS_CC);
526    smart_str_0 (&buf);
527
528    if (return_output) {
529        RETVAL_STRINGL(buf.c, buf.len, 1);
530    } else {
531        PHPWRITE(buf.c, buf.len);
532    }
533    smart_str_free(&buf);
534}
535/* }}} */
536
537static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var_hash TSRMLS_DC);
538
539static inline int php_add_var_hash(HashTable *var_hash, zval *var, void *var_old TSRMLS_DC) /* {{{ */
540{
541    ulong var_no;
542    char id[32], *p;
543    register int len;
544
545    /* relies on "(long)" being a perfect hash function for data pointers,
546     * however the actual identity of an object has had to be determined
547     * by its object handle since 5.0. */
548    if ((Z_TYPE_P(var) == IS_OBJECT) && Z_OBJ_HT_P(var)->get_class_entry) {
549        p = smart_str_print_long(id + sizeof(id) - 1, (long) Z_OBJ_HANDLE_P(var));
550        *(--p) = 'O';
551        len = id + sizeof(id) - 1 - p;
552    } else {
553        p = smart_str_print_long(id + sizeof(id) - 1, (long) var);
554        len = id + sizeof(id) - 1 - p;
555    }
556
557    if (var_old && zend_hash_find(var_hash, p, len, var_old) == SUCCESS) {
558        if (!Z_ISREF_P(var)) {
559            /* we still need to bump up the counter, since non-refs will
560             * be counted separately by unserializer */
561            var_no = -1;
562            zend_hash_next_index_insert(var_hash, &var_no, sizeof(var_no), NULL);
563        }
564        return FAILURE;
565    }
566
567    /* +1 because otherwise hash will think we are trying to store NULL pointer */
568    var_no = zend_hash_num_elements(var_hash) + 1;
569    zend_hash_add(var_hash, p, len, &var_no, sizeof(var_no), NULL);
570    return SUCCESS;
571}
572/* }}} */
573
574static inline void php_var_serialize_long(smart_str *buf, long val) /* {{{ */
575{
576    smart_str_appendl(buf, "i:", 2);
577    smart_str_append_long(buf, val);
578    smart_str_appendc(buf, ';');
579}
580/* }}} */
581
582static inline void php_var_serialize_string(smart_str *buf, char *str, int len) /* {{{ */
583{
584    smart_str_appendl(buf, "s:", 2);
585    smart_str_append_long(buf, len);
586    smart_str_appendl(buf, ":\"", 2);
587    smart_str_appendl(buf, str, len);
588    smart_str_appendl(buf, "\";", 2);
589}
590/* }}} */
591
592static inline zend_bool php_var_serialize_class_name(smart_str *buf, zval *struc TSRMLS_DC) /* {{{ */
593{
594    PHP_CLASS_ATTRIBUTES;
595
596    PHP_SET_CLASS_ATTRIBUTES(struc);
597    smart_str_appendl(buf, "O:", 2);
598    smart_str_append_long(buf, (int)name_len);
599    smart_str_appendl(buf, ":\"", 2);
600    smart_str_appendl(buf, class_name, name_len);
601    smart_str_appendl(buf, "\":", 2);
602    PHP_CLEANUP_CLASS_ATTRIBUTES();
603    return incomplete_class;
604}
605/* }}} */
606
607static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, HashTable *var_hash TSRMLS_DC) /* {{{ */
608{
609    int count;
610    zend_bool incomplete_class;
611
612    incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC);
613    /* count after serializing name, since php_var_serialize_class_name
614     * changes the count if the variable is incomplete class */
615    count = zend_hash_num_elements(HASH_OF(retval_ptr));
616    if (incomplete_class) {
617        --count;
618    }
619    smart_str_append_long(buf, count);
620    smart_str_appendl(buf, ":{", 2);
621
622    if (count > 0) {
623        char *key;
624        zval **d, **name;
625        ulong index;
626        HashPosition pos;
627        int i;
628        zval nval, *nvalp;
629        HashTable *propers;
630
631        ZVAL_NULL(&nval);
632        nvalp = &nval;
633
634        zend_hash_internal_pointer_reset_ex(HASH_OF(retval_ptr), &pos);
635
636        for (;; zend_hash_move_forward_ex(HASH_OF(retval_ptr), &pos)) {
637            i = zend_hash_get_current_key_ex(HASH_OF(retval_ptr), &key, NULL, &index, 0, &pos);
638
639            if (i == HASH_KEY_NON_EXISTANT) {
640                break;
641            }
642
643            if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) {
644                continue;
645            }
646            zend_hash_get_current_data_ex(HASH_OF(retval_ptr), (void **) &name, &pos);
647
648            if (Z_TYPE_PP(name) != IS_STRING) {
649                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize.");
650                /* we should still add element even if it's not OK,
651                 * since we already wrote the length of the array before */
652                smart_str_appendl(buf,"N;", 2);
653                continue;
654            }
655            propers = Z_OBJPROP_P(struc);
656            if (zend_hash_find(propers, Z_STRVAL_PP(name), Z_STRLEN_PP(name) + 1, (void *) &d) == SUCCESS) {
657                php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
658                php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC);
659            } else {
660                zend_class_entry *ce;
661                ce = zend_get_class_entry(struc TSRMLS_CC);
662                if (ce) {
663                    char *prot_name, *priv_name;
664                    int prop_name_length;
665
666                    do {
667                        zend_mangle_property_name(&priv_name, &prop_name_length, ce->name, ce->name_length, Z_STRVAL_PP(name), Z_STRLEN_PP(name), ce->type & ZEND_INTERNAL_CLASS);
668                        if (zend_hash_find(propers, priv_name, prop_name_length + 1, (void *) &d) == SUCCESS) {
669                            php_var_serialize_string(buf, priv_name, prop_name_length);
670                            pefree(priv_name, ce->type & ZEND_INTERNAL_CLASS);
671                            php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC);
672                            break;
673                        }
674                        pefree(priv_name, ce->type & ZEND_INTERNAL_CLASS);
675                        zend_mangle_property_name(&prot_name, &prop_name_length, "*", 1, Z_STRVAL_PP(name), Z_STRLEN_PP(name), ce->type & ZEND_INTERNAL_CLASS);
676                        if (zend_hash_find(propers, prot_name, prop_name_length + 1, (void *) &d) == SUCCESS) {
677                            php_var_serialize_string(buf, prot_name, prop_name_length);
678                            pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS);
679                            php_var_serialize_intern(buf, *d, var_hash TSRMLS_CC);
680                            break;
681                        }
682                        pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS);
683                        php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
684                        php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC);
685                        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "\"%s\" returned as member variable from __sleep() but does not exist", Z_STRVAL_PP(name));
686                    } while (0);
687                } else {
688                    php_var_serialize_string(buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
689                    php_var_serialize_intern(buf, nvalp, var_hash TSRMLS_CC);
690                }
691            }
692        }
693    }
694    smart_str_appendc(buf, '}');
695}
696/* }}} */
697
698static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var_hash TSRMLS_DC) /* {{{ */
699{
700    int i;
701    ulong *var_already;
702    HashTable *myht;
703
704    if (var_hash && php_add_var_hash(var_hash, struc, (void *) &var_already TSRMLS_CC) == FAILURE) {
705        if (Z_ISREF_P(struc)) {
706            smart_str_appendl(buf, "R:", 2);
707            smart_str_append_long(buf, (long)*var_already);
708            smart_str_appendc(buf, ';');
709            return;
710        } else if (Z_TYPE_P(struc) == IS_OBJECT) {
711            smart_str_appendl(buf, "r:", 2);
712            smart_str_append_long(buf, (long)*var_already);
713            smart_str_appendc(buf, ';');
714            return;
715        }
716    }
717
718    switch (Z_TYPE_P(struc)) {
719        case IS_BOOL:
720            smart_str_appendl(buf, "b:", 2);
721            smart_str_append_long(buf, Z_LVAL_P(struc));
722            smart_str_appendc(buf, ';');
723            return;
724
725        case IS_NULL:
726            smart_str_appendl(buf, "N;", 2);
727            return;
728
729        case IS_LONG:
730            php_var_serialize_long(buf, Z_LVAL_P(struc));
731            return;
732
733        case IS_DOUBLE: {
734                char *s;
735
736                smart_str_appendl(buf, "d:", 2);
737                s = (char *) safe_emalloc(PG(serialize_precision), 1, MAX_LENGTH_OF_DOUBLE + 1);
738                php_gcvt(Z_DVAL_P(struc), PG(serialize_precision), '.', 'E', s);
739                smart_str_appends(buf, s);
740                smart_str_appendc(buf, ';');
741                efree(s);
742                return;
743            }
744
745        case IS_STRING:
746            php_var_serialize_string(buf, Z_STRVAL_P(struc), Z_STRLEN_P(struc));
747            return;
748
749        case IS_OBJECT: {
750                zval *retval_ptr = NULL;
751                zval fname;
752                int res;
753                zend_class_entry *ce = NULL;
754
755                if (Z_OBJ_HT_P(struc)->get_class_entry) {
756                    ce = Z_OBJCE_P(struc);
757                }
758
759                if (ce && ce->serialize != NULL) {
760                    /* has custom handler */
761                    unsigned char *serialized_data = NULL;
762                    zend_uint serialized_length;
763
764                    if (ce->serialize(struc, &serialized_data, &serialized_length, (zend_serialize_data *)var_hash TSRMLS_CC) == SUCCESS) {
765                        smart_str_appendl(buf, "C:", 2);
766                        smart_str_append_long(buf, (int)Z_OBJCE_P(struc)->name_length);
767                        smart_str_appendl(buf, ":\"", 2);
768                        smart_str_appendl(buf, Z_OBJCE_P(struc)->name, Z_OBJCE_P(struc)->name_length);
769                        smart_str_appendl(buf, "\":", 2);
770
771                        smart_str_append_long(buf, (int)serialized_length);
772                        smart_str_appendl(buf, ":{", 2);
773                        smart_str_appendl(buf, serialized_data, serialized_length);
774                        smart_str_appendc(buf, '}');
775                    } else {
776                        smart_str_appendl(buf, "N;", 2);
777                    }
778                    if (serialized_data) {
779                        efree(serialized_data);
780                    }
781                    return;
782                }
783
784                if (ce && ce != PHP_IC_ENTRY && zend_hash_exists(&ce->function_table, "__sleep", sizeof("__sleep"))) {
785                    INIT_PZVAL(&fname);
786                    ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1, 0);
787                    res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
788
789                    if (res == SUCCESS && !EG(exception)) {
790                        if (retval_ptr) {
791                            if (HASH_OF(retval_ptr)) {
792                                php_var_serialize_class(buf, struc, retval_ptr, var_hash TSRMLS_CC);
793                            } else {
794                                php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize");
795                                /* we should still add element even if it's not OK,
796                                 * since we already wrote the length of the array before */
797                                smart_str_appendl(buf,"N;", 2);
798                            }
799                            zval_ptr_dtor(&retval_ptr);
800                        }
801                        return;
802                    }
803                }
804
805                if (retval_ptr) {
806                    zval_ptr_dtor(&retval_ptr);
807                }
808                /* fall-through */
809            }
810        case IS_ARRAY: {
811            zend_bool incomplete_class = 0;
812            if (Z_TYPE_P(struc) == IS_ARRAY) {
813                smart_str_appendl(buf, "a:", 2);
814                myht = HASH_OF(struc);
815            } else {
816                incomplete_class = php_var_serialize_class_name(buf, struc TSRMLS_CC);
817                myht = Z_OBJPROP_P(struc);
818            }
819            /* count after serializing name, since php_var_serialize_class_name
820             * changes the count if the variable is incomplete class */
821            i = myht ? zend_hash_num_elements(myht) : 0;
822            if (i > 0 && incomplete_class) {
823                --i;
824            }
825            smart_str_append_long(buf, i);
826            smart_str_appendl(buf, ":{", 2);
827            if (i > 0) {
828                char *key;
829                zval **data;
830                ulong index;
831                uint key_len;
832                HashPosition pos;
833
834                zend_hash_internal_pointer_reset_ex(myht, &pos);
835                for (;; zend_hash_move_forward_ex(myht, &pos)) {
836                    i = zend_hash_get_current_key_ex(myht, &key, &key_len, &index, 0, &pos);
837                    if (i == HASH_KEY_NON_EXISTANT) {
838                        break;
839                    }
840                    if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0) {
841                        continue;
842                    }
843
844                    switch (i) {
845                        case HASH_KEY_IS_LONG:
846                            php_var_serialize_long(buf, index);
847                            break;
848                        case HASH_KEY_IS_STRING:
849                            php_var_serialize_string(buf, key, key_len - 1);
850                            break;
851                    }
852
853                    /* we should still add element even if it's not OK,
854                     * since we already wrote the length of the array before */
855                    if (zend_hash_get_current_data_ex(myht, (void **) &data, &pos) != SUCCESS
856                        || !data
857                        || data == &struc
858                        || (Z_TYPE_PP(data) == IS_ARRAY && Z_ARRVAL_PP(data)->nApplyCount > 1)
859                    ) {
860                        smart_str_appendl(buf, "N;", 2);
861                    } else {
862                        if (Z_TYPE_PP(data) == IS_ARRAY) {
863                            Z_ARRVAL_PP(data)->nApplyCount++;
864                        }
865                        php_var_serialize_intern(buf, *data, var_hash TSRMLS_CC);
866                        if (Z_TYPE_PP(data) == IS_ARRAY) {
867                            Z_ARRVAL_PP(data)->nApplyCount--;
868                        }
869                    }
870                }
871            }
872            smart_str_appendc(buf, '}');
873            return;
874        }
875        default:
876            smart_str_appendl(buf, "i:0;", 4);
877            return;
878    }
879}
880/* }}} */
881
882PHPAPI void php_var_serialize(smart_str *buf, zval **struc, HashTable *var_hash TSRMLS_DC) /* {{{ */
883{
884    php_var_serialize_intern(buf, *struc, var_hash TSRMLS_CC);
885    smart_str_0(buf);
886}
887/* }}} */
888
889/* {{{ proto string serialize(mixed variable)
890   Returns a string representation of variable (which can later be unserialized) */
891PHP_FUNCTION(serialize)
892{
893    zval **struc;
894    php_serialize_data_t var_hash;
895    smart_str buf = {0};
896
897    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &struc) == FAILURE) {
898        return;
899    }
900
901    Z_TYPE_P(return_value) = IS_STRING;
902    Z_STRVAL_P(return_value) = NULL;
903    Z_STRLEN_P(return_value) = 0;
904
905    PHP_VAR_SERIALIZE_INIT(var_hash);
906    php_var_serialize(&buf, struc, &var_hash TSRMLS_CC);
907    PHP_VAR_SERIALIZE_DESTROY(var_hash);
908
909    if (buf.c) {
910        RETURN_STRINGL(buf.c, buf.len, 0);
911    } else {
912        RETURN_NULL();
913    }
914}
915/* }}} */
916
917/* {{{ proto mixed unserialize(string variable_representation)
918   Takes a string representation of variable and recreates it */
919PHP_FUNCTION(unserialize)
920{
921    char *buf = NULL;
922    int buf_len;
923    const unsigned char *p;
924    php_unserialize_data_t var_hash;
925
926    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) {
927        RETURN_FALSE;
928    }
929
930    if (buf_len == 0) {
931        RETURN_FALSE;
932    }
933
934    p = (const unsigned char*) buf;
935    PHP_VAR_UNSERIALIZE_INIT(var_hash);
936    if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) {
937        PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
938        zval_dtor(return_value);
939        php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len);
940        RETURN_FALSE;
941    }
942    PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
943}
944/* }}} */
945
946/* {{{ proto int memory_get_usage([real_usage])
947   Returns the allocated by PHP memory */
948PHP_FUNCTION(memory_get_usage) {
949    zend_bool real_usage = 0;
950
951    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &real_usage) == FAILURE) {
952        RETURN_FALSE;
953    }
954
955    RETURN_LONG(zend_memory_usage(real_usage TSRMLS_CC));
956}
957/* }}} */
958
959/* {{{ proto int memory_get_peak_usage([real_usage])
960   Returns the peak allocated by PHP memory */
961PHP_FUNCTION(memory_get_peak_usage) {
962    zend_bool real_usage = 0;
963
964    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &real_usage) == FAILURE) {
965        RETURN_FALSE;
966    }
967
968    RETURN_LONG(zend_memory_peak_usage(real_usage TSRMLS_CC));
969}
970/* }}} */
971
972/*
973 * Local variables:
974 * tab-width: 4
975 * c-basic-offset: 4
976 * End:
977 * vim600: sw=4 ts=4 fdm=marker
978 * vim<600: sw=4 ts=4
979 */
980