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