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   | Author: Andrei Zmievski <andrei@php.net>                             |
16   +----------------------------------------------------------------------+
17 */
18
19/* $Id$ */
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
24
25#include "php.h"
26
27#if HAVE_WDDX
28
29#include "ext/xml/expat_compat.h"
30#include "php_wddx.h"
31#include "php_wddx_api.h"
32
33#define PHP_XML_INTERNAL
34#include "ext/xml/php_xml.h"
35#include "ext/standard/php_incomplete_class.h"
36#include "ext/standard/base64.h"
37#include "ext/standard/info.h"
38#include "ext/standard/php_smart_str.h"
39#include "ext/standard/html.h"
40#include "ext/standard/php_string.h"
41#include "ext/date/php_date.h"
42#include "zend_globals.h"
43
44#define WDDX_BUF_LEN            256
45#define PHP_CLASS_NAME_VAR      "php_class_name"
46
47#define EL_ARRAY                "array"
48#define EL_BINARY               "binary"
49#define EL_BOOLEAN              "boolean"
50#define EL_CHAR                 "char"
51#define EL_CHAR_CODE            "code"
52#define EL_NULL                 "null"
53#define EL_NUMBER               "number"
54#define EL_PACKET               "wddxPacket"
55#define EL_STRING               "string"
56#define EL_STRUCT               "struct"
57#define EL_VALUE                "value"
58#define EL_VAR                  "var"
59#define EL_NAME                 "name"
60#define EL_VERSION              "version"
61#define EL_RECORDSET            "recordset"
62#define EL_FIELD                "field"
63#define EL_DATETIME             "dateTime"
64
65#define php_wddx_deserialize(a,b) \
66    php_wddx_deserialize_ex((a)->value.str.val, (a)->value.str.len, (b))
67
68#define SET_STACK_VARNAME                           \
69        if (stack->varname) {                       \
70            ent.varname = estrdup(stack->varname);  \
71            efree(stack->varname);                  \
72            stack->varname = NULL;                  \
73        } else                                      \
74            ent.varname = NULL;                     \
75
76static int le_wddx;
77
78typedef struct {
79    zval *data;
80    enum {
81        ST_ARRAY,
82        ST_BOOLEAN,
83        ST_NULL,
84        ST_NUMBER,
85        ST_STRING,
86        ST_BINARY,
87        ST_STRUCT,
88        ST_RECORDSET,
89        ST_FIELD,
90        ST_DATETIME
91    } type;
92    char *varname;
93} st_entry;
94
95typedef struct {
96    int top, max;
97    char *varname;
98    zend_bool done;
99    void **elements;
100} wddx_stack;
101
102
103static void php_wddx_process_data(void *user_data, const XML_Char *s, int len);
104
105/* {{{ arginfo */
106ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_value, 0, 0, 1)
107    ZEND_ARG_INFO(0, var)
108    ZEND_ARG_INFO(0, comment)
109ZEND_END_ARG_INFO()
110
111ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_vars, 0, 0, 1)
112    ZEND_ARG_INFO(0, var_name)
113    ZEND_ARG_INFO(0, ...)
114ZEND_END_ARG_INFO()
115
116ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_serialize_start, 0, 0, 0)
117    ZEND_ARG_INFO(0, comment)
118ZEND_END_ARG_INFO()
119
120ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_packet_end, 0, 0, 1)
121    ZEND_ARG_INFO(0, packet_id)
122ZEND_END_ARG_INFO()
123
124ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_add_vars, 0, 0, 2)
125    ZEND_ARG_INFO(0, packet_id)
126    ZEND_ARG_INFO(0, var_name)
127    ZEND_ARG_INFO(0, ...)
128ZEND_END_ARG_INFO()
129
130ZEND_BEGIN_ARG_INFO_EX(arginfo_wddx_deserialize, 0, 0, 1)
131    ZEND_ARG_INFO(0, packet)
132ZEND_END_ARG_INFO()
133/* }}} */
134
135/* {{{ wddx_functions[]
136 */
137const zend_function_entry wddx_functions[] = {
138    PHP_FE(wddx_serialize_value, arginfo_wddx_serialize_value)
139    PHP_FE(wddx_serialize_vars, arginfo_wddx_serialize_vars)
140    PHP_FE(wddx_packet_start,   arginfo_wddx_serialize_start)
141    PHP_FE(wddx_packet_end,     arginfo_wddx_packet_end)
142    PHP_FE(wddx_add_vars,       arginfo_wddx_add_vars)
143    PHP_FE(wddx_deserialize,    arginfo_wddx_deserialize)
144    PHP_FE_END
145};
146/* }}} */
147
148PHP_MINIT_FUNCTION(wddx);
149PHP_MINFO_FUNCTION(wddx);
150
151/* {{{ dynamically loadable module stuff */
152#ifdef COMPILE_DL_WDDX
153ZEND_GET_MODULE(wddx)
154#endif /* COMPILE_DL_WDDX */
155/* }}} */
156
157/* {{{ wddx_module_entry
158 */
159zend_module_entry wddx_module_entry = {
160    STANDARD_MODULE_HEADER,
161    "wddx",
162    wddx_functions,
163    PHP_MINIT(wddx),
164    NULL,
165    NULL,
166    NULL,
167    PHP_MINFO(wddx),
168    NO_VERSION_YET,
169    STANDARD_MODULE_PROPERTIES
170};
171/* }}} */
172
173/* {{{ wddx_stack_init
174 */
175static int wddx_stack_init(wddx_stack *stack)
176{
177    stack->top = 0;
178    stack->elements = (void **) safe_emalloc(sizeof(void **), STACK_BLOCK_SIZE, 0);
179    stack->max = STACK_BLOCK_SIZE;
180    stack->varname = NULL;
181    stack->done = 0;
182
183    return SUCCESS;
184}
185/* }}} */
186
187/* {{{ wddx_stack_push
188 */
189static int wddx_stack_push(wddx_stack *stack, void *element, int size)
190{
191    if (stack->top >= stack->max) {     /* we need to allocate more memory */
192        stack->elements = (void **) erealloc(stack->elements,
193                   (sizeof(void **) * (stack->max += STACK_BLOCK_SIZE)));
194    }
195    stack->elements[stack->top] = (void *) emalloc(size);
196    memcpy(stack->elements[stack->top], element, size);
197    return stack->top++;
198}
199/* }}} */
200
201/* {{{ wddx_stack_top
202 */
203static int wddx_stack_top(wddx_stack *stack, void **element)
204{
205    if (stack->top > 0) {
206        *element = stack->elements[stack->top - 1];
207        return SUCCESS;
208    } else {
209        *element = NULL;
210        return FAILURE;
211    }
212}
213/* }}} */
214
215/* {{{ wddx_stack_is_empty
216 */
217static int wddx_stack_is_empty(wddx_stack *stack)
218{
219    if (stack->top == 0) {
220        return 1;
221    } else {
222        return 0;
223    }
224}
225/* }}} */
226
227/* {{{ wddx_stack_destroy
228 */
229static int wddx_stack_destroy(wddx_stack *stack)
230{
231    register int i;
232
233    if (stack->elements) {
234        for (i = 0; i < stack->top; i++) {
235            if (((st_entry *)stack->elements[i])->data) {
236                zval_ptr_dtor(&((st_entry *)stack->elements[i])->data);
237            }
238            if (((st_entry *)stack->elements[i])->varname) {
239                efree(((st_entry *)stack->elements[i])->varname);
240            }
241            efree(stack->elements[i]);
242        }
243        efree(stack->elements);
244    }
245    return SUCCESS;
246}
247/* }}} */
248
249/* {{{ release_wddx_packet_rsrc
250 */
251static void release_wddx_packet_rsrc(zend_rsrc_list_entry *rsrc TSRMLS_DC)
252{
253    smart_str *str = (smart_str *)rsrc->ptr;
254    smart_str_free(str);
255    efree(str);
256}
257/* }}} */
258
259#include "ext/session/php_session.h"
260
261#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
262/* {{{ PS_SERIALIZER_ENCODE_FUNC
263 */
264PS_SERIALIZER_ENCODE_FUNC(wddx)
265{
266    wddx_packet *packet;
267    PS_ENCODE_VARS;
268
269    packet = php_wddx_constructor();
270
271    php_wddx_packet_start(packet, NULL, 0);
272    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
273
274    PS_ENCODE_LOOP(
275        php_wddx_serialize_var(packet, *struc, key, key_length TSRMLS_CC);
276    );
277
278    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
279    php_wddx_packet_end(packet);
280    *newstr = php_wddx_gather(packet);
281    php_wddx_destructor(packet);
282
283    if (newlen) {
284        *newlen = strlen(*newstr);
285    }
286
287    return SUCCESS;
288}
289/* }}} */
290
291/* {{{ PS_SERIALIZER_DECODE_FUNC
292 */
293PS_SERIALIZER_DECODE_FUNC(wddx)
294{
295    zval *retval;
296    zval **ent;
297    char *key;
298    uint key_length;
299    char tmp[128];
300    ulong idx;
301    int hash_type;
302    int ret;
303
304    if (vallen == 0) {
305        return SUCCESS;
306    }
307
308    MAKE_STD_ZVAL(retval);
309
310    if ((ret = php_wddx_deserialize_ex((char *)val, vallen, retval)) == SUCCESS) {
311
312        for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(retval));
313             zend_hash_get_current_data(Z_ARRVAL_P(retval), (void **) &ent) == SUCCESS;
314             zend_hash_move_forward(Z_ARRVAL_P(retval))) {
315            hash_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(retval), &key, &key_length, &idx, 0, NULL);
316
317            switch (hash_type) {
318                case HASH_KEY_IS_LONG:
319                    key_length = slprintf(tmp, sizeof(tmp), "%ld", idx) + 1;
320                    key = tmp;
321                    /* fallthru */
322                case HASH_KEY_IS_STRING:
323                    php_set_session_var(key, key_length-1, *ent, NULL TSRMLS_CC);
324                    PS_ADD_VAR(key);
325            }
326        }
327    }
328
329    zval_ptr_dtor(&retval);
330
331    return ret;
332}
333/* }}} */
334#endif
335
336/* {{{ PHP_MINIT_FUNCTION
337 */
338PHP_MINIT_FUNCTION(wddx)
339{
340    le_wddx = zend_register_list_destructors_ex(release_wddx_packet_rsrc, NULL, "wddx", module_number);
341
342#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
343    php_session_register_serializer("wddx",
344                                    PS_SERIALIZER_ENCODE_NAME(wddx),
345                                    PS_SERIALIZER_DECODE_NAME(wddx));
346#endif
347
348    return SUCCESS;
349}
350/* }}} */
351
352/* {{{ PHP_MINFO_FUNCTION
353 */
354PHP_MINFO_FUNCTION(wddx)
355{
356    php_info_print_table_start();
357#if HAVE_PHP_SESSION && !defined(COMPILE_DL_SESSION)
358    php_info_print_table_header(2, "WDDX Support", "enabled" );
359    php_info_print_table_row(2, "WDDX Session Serializer", "enabled" );
360#else
361    php_info_print_table_row(2, "WDDX Support", "enabled" );
362#endif
363    php_info_print_table_end();
364}
365/* }}} */
366
367/* {{{ php_wddx_packet_start
368 */
369void php_wddx_packet_start(wddx_packet *packet, char *comment, int comment_len)
370{
371    php_wddx_add_chunk_static(packet, WDDX_PACKET_S);
372    if (comment) {
373        php_wddx_add_chunk_static(packet, WDDX_HEADER_S);
374        php_wddx_add_chunk_static(packet, WDDX_COMMENT_S);
375        php_wddx_add_chunk_ex(packet, comment, comment_len);
376        php_wddx_add_chunk_static(packet, WDDX_COMMENT_E);
377        php_wddx_add_chunk_static(packet, WDDX_HEADER_E);
378    } else {
379        php_wddx_add_chunk_static(packet, WDDX_HEADER);
380    }
381    php_wddx_add_chunk_static(packet, WDDX_DATA_S);
382}
383/* }}} */
384
385/* {{{ php_wddx_packet_end
386 */
387void php_wddx_packet_end(wddx_packet *packet)
388{
389    php_wddx_add_chunk_static(packet, WDDX_DATA_E);
390    php_wddx_add_chunk_static(packet, WDDX_PACKET_E);
391}
392/* }}} */
393
394#define FLUSH_BUF()                               \
395    if (l > 0) {                                  \
396        php_wddx_add_chunk_ex(packet, buf, l);    \
397        l = 0;                                    \
398    }
399
400/* {{{ php_wddx_serialize_string
401 */
402static void php_wddx_serialize_string(wddx_packet *packet, zval *var TSRMLS_DC)
403{
404    php_wddx_add_chunk_static(packet, WDDX_STRING_S);
405
406    if (Z_STRLEN_P(var) > 0) {
407        char *buf;
408        size_t buf_len;
409
410        buf = php_escape_html_entities(Z_STRVAL_P(var), Z_STRLEN_P(var), &buf_len, 0, ENT_QUOTES, NULL TSRMLS_CC);
411
412        php_wddx_add_chunk_ex(packet, buf, buf_len);
413
414        efree(buf);
415    }
416    php_wddx_add_chunk_static(packet, WDDX_STRING_E);
417}
418/* }}} */
419
420/* {{{ php_wddx_serialize_number
421 */
422static void php_wddx_serialize_number(wddx_packet *packet, zval *var)
423{
424    char tmp_buf[WDDX_BUF_LEN];
425    zval tmp;
426
427    tmp = *var;
428    zval_copy_ctor(&tmp);
429    convert_to_string(&tmp);
430    snprintf(tmp_buf, sizeof(tmp_buf), WDDX_NUMBER, Z_STRVAL(tmp));
431    zval_dtor(&tmp);
432
433    php_wddx_add_chunk(packet, tmp_buf);
434}
435/* }}} */
436
437/* {{{ php_wddx_serialize_boolean
438 */
439static void php_wddx_serialize_boolean(wddx_packet *packet, zval *var)
440{
441    php_wddx_add_chunk(packet, Z_LVAL_P(var) ? WDDX_BOOLEAN_TRUE : WDDX_BOOLEAN_FALSE);
442}
443/* }}} */
444
445/* {{{ php_wddx_serialize_unset
446 */
447static void php_wddx_serialize_unset(wddx_packet *packet)
448{
449    php_wddx_add_chunk_static(packet, WDDX_NULL);
450}
451/* }}} */
452
453/* {{{ php_wddx_serialize_object
454 */
455static void php_wddx_serialize_object(wddx_packet *packet, zval *obj)
456{
457/* OBJECTS_FIXME */
458    zval **ent, *fname, **varname;
459    zval *retval = NULL;
460    const char *key;
461    ulong idx;
462    char tmp_buf[WDDX_BUF_LEN];
463    HashTable *objhash, *sleephash;
464    TSRMLS_FETCH();
465
466    MAKE_STD_ZVAL(fname);
467    ZVAL_STRING(fname, "__sleep", 1);
468
469    /*
470     * We try to call __sleep() method on object. It's supposed to return an
471     * array of property names to be serialized.
472     */
473    if (call_user_function_ex(CG(function_table), &obj, fname, &retval, 0, 0, 1, NULL TSRMLS_CC) == SUCCESS) {
474        if (retval && (sleephash = HASH_OF(retval))) {
475            PHP_CLASS_ATTRIBUTES;
476
477            PHP_SET_CLASS_ATTRIBUTES(obj);
478
479            php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
480            snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR);
481            php_wddx_add_chunk(packet, tmp_buf);
482            php_wddx_add_chunk_static(packet, WDDX_STRING_S);
483            php_wddx_add_chunk_ex(packet, class_name, name_len);
484            php_wddx_add_chunk_static(packet, WDDX_STRING_E);
485            php_wddx_add_chunk_static(packet, WDDX_VAR_E);
486
487            PHP_CLEANUP_CLASS_ATTRIBUTES();
488
489            objhash = HASH_OF(obj);
490
491            for (zend_hash_internal_pointer_reset(sleephash);
492                 zend_hash_get_current_data(sleephash, (void **)&varname) == SUCCESS;
493                 zend_hash_move_forward(sleephash)) {
494                if (Z_TYPE_PP(varname) != IS_STRING) {
495                    php_error_docref(NULL TSRMLS_CC, E_NOTICE, "__sleep should return an array only containing the names of instance-variables to serialize.");
496                    continue;
497                }
498
499                if (zend_hash_find(objhash, Z_STRVAL_PP(varname), Z_STRLEN_PP(varname)+1, (void **)&ent) == SUCCESS) {
500                    php_wddx_serialize_var(packet, *ent, Z_STRVAL_PP(varname), Z_STRLEN_PP(varname) TSRMLS_CC);
501                }
502            }
503
504            php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
505        }
506    } else {
507        uint key_len;
508
509        PHP_CLASS_ATTRIBUTES;
510
511        PHP_SET_CLASS_ATTRIBUTES(obj);
512
513        php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
514        snprintf(tmp_buf, WDDX_BUF_LEN, WDDX_VAR_S, PHP_CLASS_NAME_VAR);
515        php_wddx_add_chunk(packet, tmp_buf);
516        php_wddx_add_chunk_static(packet, WDDX_STRING_S);
517        php_wddx_add_chunk_ex(packet, class_name, name_len);
518        php_wddx_add_chunk_static(packet, WDDX_STRING_E);
519        php_wddx_add_chunk_static(packet, WDDX_VAR_E);
520
521        PHP_CLEANUP_CLASS_ATTRIBUTES();
522
523        objhash = HASH_OF(obj);
524        for (zend_hash_internal_pointer_reset(objhash);
525             zend_hash_get_current_data(objhash, (void**)&ent) == SUCCESS;
526             zend_hash_move_forward(objhash)) {
527            if (*ent == obj) {
528                continue;
529            }
530
531            if (zend_hash_get_current_key_ex(objhash, &key, &key_len, &idx, 0, NULL) == HASH_KEY_IS_STRING) {
532                const char *class_name, *prop_name;
533
534                zend_unmangle_property_name(key, key_len-1, &class_name, &prop_name);
535                php_wddx_serialize_var(packet, *ent, prop_name, strlen(prop_name)+1 TSRMLS_CC);
536            } else {
537                key_len = slprintf(tmp_buf, sizeof(tmp_buf), "%ld", idx);
538                php_wddx_serialize_var(packet, *ent, tmp_buf, key_len TSRMLS_CC);
539            }
540        }
541        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
542    }
543
544    zval_dtor(fname);
545    FREE_ZVAL(fname);
546
547    if (retval) {
548        zval_ptr_dtor(&retval);
549    }
550}
551/* }}} */
552
553/* {{{ php_wddx_serialize_array
554 */
555static void php_wddx_serialize_array(wddx_packet *packet, zval *arr)
556{
557    zval **ent;
558    char *key;
559    uint key_len;
560    int is_struct = 0, ent_type;
561    ulong idx;
562    HashTable *target_hash;
563    char tmp_buf[WDDX_BUF_LEN];
564    ulong ind = 0;
565    int type;
566    TSRMLS_FETCH();
567
568    target_hash = HASH_OF(arr);
569
570    for (zend_hash_internal_pointer_reset(target_hash);
571         zend_hash_get_current_data(target_hash, (void**)&ent) == SUCCESS;
572         zend_hash_move_forward(target_hash)) {
573
574        type = zend_hash_get_current_key(target_hash, &key, &idx, 0);
575
576        if (type == HASH_KEY_IS_STRING) {
577            is_struct = 1;
578            break;
579        }
580
581        if (idx != ind) {
582            is_struct = 1;
583            break;
584        }
585
586        ind++;
587    }
588
589    if (is_struct) {
590        php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
591    } else {
592        snprintf(tmp_buf, sizeof(tmp_buf), WDDX_ARRAY_S, zend_hash_num_elements(target_hash));
593        php_wddx_add_chunk(packet, tmp_buf);
594    }
595
596    for (zend_hash_internal_pointer_reset(target_hash);
597         zend_hash_get_current_data(target_hash, (void**)&ent) == SUCCESS;
598         zend_hash_move_forward(target_hash)) {
599        if (*ent == arr) {
600            continue;
601        }
602
603        if (is_struct) {
604            ent_type = zend_hash_get_current_key_ex(target_hash, &key, &key_len, &idx, 0, NULL);
605
606            if (ent_type == HASH_KEY_IS_STRING) {
607                php_wddx_serialize_var(packet, *ent, key, key_len TSRMLS_CC);
608            } else {
609                key_len = slprintf(tmp_buf, sizeof(tmp_buf), "%ld", idx);
610                php_wddx_serialize_var(packet, *ent, tmp_buf, key_len TSRMLS_CC);
611            }
612        } else {
613            php_wddx_serialize_var(packet, *ent, NULL, 0 TSRMLS_CC);
614        }
615    }
616
617    if (is_struct) {
618        php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
619    } else {
620        php_wddx_add_chunk_static(packet, WDDX_ARRAY_E);
621    }
622}
623/* }}} */
624
625/* {{{ php_wddx_serialize_var
626 */
627void php_wddx_serialize_var(wddx_packet *packet, zval *var, char *name, int name_len TSRMLS_DC)
628{
629    HashTable *ht;
630
631    if (name) {
632        size_t name_esc_len;
633        char *tmp_buf, *name_esc;
634
635        name_esc = php_escape_html_entities(name, name_len, &name_esc_len, 0, ENT_QUOTES, NULL TSRMLS_CC);
636        tmp_buf = emalloc(name_esc_len + sizeof(WDDX_VAR_S));
637        snprintf(tmp_buf, name_esc_len + sizeof(WDDX_VAR_S), WDDX_VAR_S, name_esc);
638        php_wddx_add_chunk(packet, tmp_buf);
639        efree(tmp_buf);
640        efree(name_esc);
641    }
642
643    switch(Z_TYPE_P(var)) {
644        case IS_STRING:
645            php_wddx_serialize_string(packet, var TSRMLS_CC);
646            break;
647
648        case IS_LONG:
649        case IS_DOUBLE:
650            php_wddx_serialize_number(packet, var);
651            break;
652
653        case IS_BOOL:
654            php_wddx_serialize_boolean(packet, var);
655            break;
656
657        case IS_NULL:
658            php_wddx_serialize_unset(packet);
659            break;
660
661        case IS_ARRAY:
662            ht = Z_ARRVAL_P(var);
663            if (ht->nApplyCount > 1) {
664                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
665                return;
666            }
667            ht->nApplyCount++;
668            php_wddx_serialize_array(packet, var);
669            ht->nApplyCount--;
670            break;
671
672        case IS_OBJECT:
673            ht = Z_OBJPROP_P(var);
674            if (ht->nApplyCount > 1) {
675                php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "WDDX doesn't support circular references");
676                return;
677            }
678            ht->nApplyCount++;
679            php_wddx_serialize_object(packet, var);
680            ht->nApplyCount--;
681            break;
682    }
683
684    if (name) {
685        php_wddx_add_chunk_static(packet, WDDX_VAR_E);
686    }
687}
688/* }}} */
689
690/* {{{ php_wddx_add_var
691 */
692static void php_wddx_add_var(wddx_packet *packet, zval *name_var)
693{
694    zval **val;
695    HashTable *target_hash;
696    TSRMLS_FETCH();
697
698    if (Z_TYPE_P(name_var) == IS_STRING) {
699        if (!EG(active_symbol_table)) {
700            zend_rebuild_symbol_table(TSRMLS_C);
701        }
702        if (zend_hash_find(EG(active_symbol_table), Z_STRVAL_P(name_var),
703                            Z_STRLEN_P(name_var)+1, (void**)&val) != FAILURE) {
704            php_wddx_serialize_var(packet, *val, Z_STRVAL_P(name_var), Z_STRLEN_P(name_var) TSRMLS_CC);
705        }
706    } else if (Z_TYPE_P(name_var) == IS_ARRAY || Z_TYPE_P(name_var) == IS_OBJECT)   {
707        int is_array = Z_TYPE_P(name_var) == IS_ARRAY;
708
709        target_hash = HASH_OF(name_var);
710
711        if (is_array && target_hash->nApplyCount > 1) {
712            php_error_docref(NULL TSRMLS_CC, E_WARNING, "recursion detected");
713            return;
714        }
715
716        zend_hash_internal_pointer_reset(target_hash);
717
718        while(zend_hash_get_current_data(target_hash, (void**)&val) == SUCCESS) {
719            if (is_array) {
720                target_hash->nApplyCount++;
721            }
722
723            php_wddx_add_var(packet, *val);
724
725            if (is_array) {
726                target_hash->nApplyCount--;
727            }
728            zend_hash_move_forward(target_hash);
729        }
730    }
731}
732/* }}} */
733
734/* {{{ php_wddx_push_element
735 */
736static void php_wddx_push_element(void *user_data, const XML_Char *name, const XML_Char **atts)
737{
738    st_entry ent;
739    wddx_stack *stack = (wddx_stack *)user_data;
740
741    if (!strcmp(name, EL_PACKET)) {
742        int i;
743
744        if (atts) for (i=0; atts[i]; i++) {
745            if (!strcmp(atts[i], EL_VERSION)) {
746                /* nothing for now */
747            }
748        }
749    } else if (!strcmp(name, EL_STRING)) {
750        ent.type = ST_STRING;
751        SET_STACK_VARNAME;
752
753        ALLOC_ZVAL(ent.data);
754        INIT_PZVAL(ent.data);
755        Z_TYPE_P(ent.data) = IS_STRING;
756        Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
757        Z_STRLEN_P(ent.data) = 0;
758        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
759    } else if (!strcmp(name, EL_BINARY)) {
760        ent.type = ST_BINARY;
761        SET_STACK_VARNAME;
762
763        ALLOC_ZVAL(ent.data);
764        INIT_PZVAL(ent.data);
765        Z_TYPE_P(ent.data) = IS_STRING;
766        Z_STRVAL_P(ent.data) = STR_EMPTY_ALLOC();
767        Z_STRLEN_P(ent.data) = 0;
768        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
769    } else if (!strcmp(name, EL_CHAR)) {
770        int i;
771
772        if (atts) for (i = 0; atts[i]; i++) {
773            if (!strcmp(atts[i], EL_CHAR_CODE) && atts[++i] && atts[i][0]) {
774                char tmp_buf[2];
775
776                snprintf(tmp_buf, sizeof(tmp_buf), "%c", (char)strtol(atts[i], NULL, 16));
777                php_wddx_process_data(user_data, tmp_buf, strlen(tmp_buf));
778                break;
779            }
780        }
781    } else if (!strcmp(name, EL_NUMBER)) {
782        ent.type = ST_NUMBER;
783        SET_STACK_VARNAME;
784
785        ALLOC_ZVAL(ent.data);
786        INIT_PZVAL(ent.data);
787        Z_TYPE_P(ent.data) = IS_LONG;
788        Z_LVAL_P(ent.data) = 0;
789        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
790    } else if (!strcmp(name, EL_BOOLEAN)) {
791        int i;
792
793        if (atts) for (i = 0; atts[i]; i++) {
794            if (!strcmp(atts[i], EL_VALUE) && atts[++i] && atts[i][0]) {
795                ent.type = ST_BOOLEAN;
796                SET_STACK_VARNAME;
797
798                ALLOC_ZVAL(ent.data);
799                INIT_PZVAL(ent.data);
800                Z_TYPE_P(ent.data) = IS_BOOL;
801                wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
802                php_wddx_process_data(user_data, atts[i], strlen(atts[i]));
803                break;
804            }
805        }
806    } else if (!strcmp(name, EL_NULL)) {
807        ent.type = ST_NULL;
808        SET_STACK_VARNAME;
809
810        ALLOC_ZVAL(ent.data);
811        INIT_PZVAL(ent.data);
812        ZVAL_NULL(ent.data);
813
814        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
815    } else if (!strcmp(name, EL_ARRAY)) {
816        ent.type = ST_ARRAY;
817        SET_STACK_VARNAME;
818
819        ALLOC_ZVAL(ent.data);
820        array_init(ent.data);
821        INIT_PZVAL(ent.data);
822        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
823    } else if (!strcmp(name, EL_STRUCT)) {
824        ent.type = ST_STRUCT;
825        SET_STACK_VARNAME;
826
827        ALLOC_ZVAL(ent.data);
828        array_init(ent.data);
829        INIT_PZVAL(ent.data);
830        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
831    } else if (!strcmp(name, EL_VAR)) {
832        int i;
833
834        if (atts) for (i = 0; atts[i]; i++) {
835            if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
836                stack->varname = estrdup(atts[i]);
837                break;
838            }
839        }
840    } else if (!strcmp(name, EL_RECORDSET)) {
841        int i;
842
843        ent.type = ST_RECORDSET;
844        SET_STACK_VARNAME;
845        MAKE_STD_ZVAL(ent.data);
846        array_init(ent.data);
847
848        if (atts) for (i = 0; atts[i]; i++) {
849            if (!strcmp(atts[i], "fieldNames") && atts[++i] && atts[i][0]) {
850                zval *tmp;
851                char *key;
852                char *p1, *p2, *endp;
853
854                endp = (char *)atts[i] + strlen(atts[i]);
855                p1 = (char *)atts[i];
856                while ((p2 = php_memnstr(p1, ",", sizeof(",")-1, endp)) != NULL) {
857                    key = estrndup(p1, p2 - p1);
858                    MAKE_STD_ZVAL(tmp);
859                    array_init(tmp);
860                    add_assoc_zval_ex(ent.data, key, p2 - p1 + 1, tmp);
861                    p1 = p2 + sizeof(",")-1;
862                    efree(key);
863                }
864
865                if (p1 <= endp) {
866                    MAKE_STD_ZVAL(tmp);
867                    array_init(tmp);
868                    add_assoc_zval_ex(ent.data, p1, endp - p1 + 1, tmp);
869                }
870
871                break;
872            }
873        }
874
875        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
876    } else if (!strcmp(name, EL_FIELD)) {
877        int i;
878        st_entry ent;
879
880        ent.type = ST_FIELD;
881        ent.varname = NULL;
882        ent.data = NULL;
883
884        if (atts) for (i = 0; atts[i]; i++) {
885            if (!strcmp(atts[i], EL_NAME) && atts[++i] && atts[i][0]) {
886                st_entry *recordset;
887                zval **field;
888
889                if (wddx_stack_top(stack, (void**)&recordset) == SUCCESS &&
890                    recordset->type == ST_RECORDSET &&
891                    zend_hash_find(Z_ARRVAL_P(recordset->data), (char*)atts[i], strlen(atts[i])+1, (void**)&field) == SUCCESS) {
892                    ent.data = *field;
893                }
894
895                break;
896            }
897        }
898
899        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
900    } else if (!strcmp(name, EL_DATETIME)) {
901        ent.type = ST_DATETIME;
902        SET_STACK_VARNAME;
903
904        ALLOC_ZVAL(ent.data);
905        INIT_PZVAL(ent.data);
906        Z_TYPE_P(ent.data) = IS_LONG;
907        wddx_stack_push((wddx_stack *)stack, &ent, sizeof(st_entry));
908    }
909}
910/* }}} */
911
912/* {{{ php_wddx_pop_element
913 */
914static void php_wddx_pop_element(void *user_data, const XML_Char *name)
915{
916    st_entry            *ent1, *ent2;
917    wddx_stack          *stack = (wddx_stack *)user_data;
918    HashTable           *target_hash;
919    zend_class_entry    **pce;
920    zval                *obj;
921    zval                *tmp;
922    TSRMLS_FETCH();
923
924/* OBJECTS_FIXME */
925    if (stack->top == 0) {
926        return;
927    }
928
929    if (!strcmp(name, EL_STRING) || !strcmp(name, EL_NUMBER) ||
930        !strcmp(name, EL_BOOLEAN) || !strcmp(name, EL_NULL) ||
931        !strcmp(name, EL_ARRAY) || !strcmp(name, EL_STRUCT) ||
932        !strcmp(name, EL_RECORDSET) || !strcmp(name, EL_BINARY) ||
933        !strcmp(name, EL_DATETIME)) {
934        wddx_stack_top(stack, (void**)&ent1);
935
936        if (!strcmp(name, EL_BINARY)) {
937            int new_len=0;
938            unsigned char *new_str;
939
940            new_str = php_base64_decode(Z_STRVAL_P(ent1->data), Z_STRLEN_P(ent1->data), &new_len);
941            STR_FREE(Z_STRVAL_P(ent1->data));
942            Z_STRVAL_P(ent1->data) = new_str;
943            Z_STRLEN_P(ent1->data) = new_len;
944        }
945
946        /* Call __wakeup() method on the object. */
947        if (Z_TYPE_P(ent1->data) == IS_OBJECT) {
948            zval *fname, *retval = NULL;
949
950            MAKE_STD_ZVAL(fname);
951            ZVAL_STRING(fname, "__wakeup", 1);
952
953            call_user_function_ex(NULL, &ent1->data, fname, &retval, 0, 0, 0, NULL TSRMLS_CC);
954
955            zval_dtor(fname);
956            FREE_ZVAL(fname);
957            if (retval) {
958                zval_ptr_dtor(&retval);
959            }
960        }
961
962        if (stack->top > 1) {
963            stack->top--;
964            wddx_stack_top(stack, (void**)&ent2);
965
966            /* if non-existent field */
967            if (ent2->type == ST_FIELD && ent2->data == NULL) {
968                zval_ptr_dtor(&ent1->data);
969                efree(ent1);
970                return;
971            }
972
973            if (Z_TYPE_P(ent2->data) == IS_ARRAY || Z_TYPE_P(ent2->data) == IS_OBJECT) {
974                target_hash = HASH_OF(ent2->data);
975
976                if (ent1->varname) {
977                    if (!strcmp(ent1->varname, PHP_CLASS_NAME_VAR) &&
978                        Z_TYPE_P(ent1->data) == IS_STRING && Z_STRLEN_P(ent1->data)) {
979                        zend_bool incomplete_class = 0;
980
981                        zend_str_tolower(Z_STRVAL_P(ent1->data), Z_STRLEN_P(ent1->data));
982                        if (zend_hash_find(EG(class_table), Z_STRVAL_P(ent1->data),
983                                           Z_STRLEN_P(ent1->data)+1, (void **) &pce)==FAILURE) {
984                            incomplete_class = 1;
985                            pce = &PHP_IC_ENTRY;
986                        }
987
988                        /* Initialize target object */
989                        MAKE_STD_ZVAL(obj);
990                        object_init_ex(obj, *pce);
991
992                        /* Merge current hashtable with object's default properties */
993                        zend_hash_merge(Z_OBJPROP_P(obj),
994                                        Z_ARRVAL_P(ent2->data),
995                                        (void (*)(void *)) zval_add_ref,
996                                        (void *) &tmp, sizeof(zval *), 0);
997
998                        if (incomplete_class) {
999                            php_store_class_name(obj, Z_STRVAL_P(ent1->data), Z_STRLEN_P(ent1->data));
1000                        }
1001
1002                        /* Clean up old array entry */
1003                        zval_ptr_dtor(&ent2->data);
1004
1005                        /* Set stack entry to point to the newly created object */
1006                        ent2->data = obj;
1007
1008                        /* Clean up class name var entry */
1009                        zval_ptr_dtor(&ent1->data);
1010                    } else if (Z_TYPE_P(ent2->data) == IS_OBJECT) {
1011                        zend_class_entry *old_scope = EG(scope);
1012
1013                        EG(scope) = Z_OBJCE_P(ent2->data);
1014                        Z_DELREF_P(ent1->data);
1015                        add_property_zval(ent2->data, ent1->varname, ent1->data);
1016                        EG(scope) = old_scope;
1017                    } else {
1018                        zend_symtable_update(target_hash, ent1->varname, strlen(ent1->varname)+1, &ent1->data, sizeof(zval *), NULL);
1019                    }
1020                    efree(ent1->varname);
1021                } else  {
1022                    zend_hash_next_index_insert(target_hash, &ent1->data, sizeof(zval *), NULL);
1023                }
1024            }
1025            efree(ent1);
1026        } else {
1027            stack->done = 1;
1028        }
1029    } else if (!strcmp(name, EL_VAR) && stack->varname) {
1030        efree(stack->varname);
1031    } else if (!strcmp(name, EL_FIELD)) {
1032        st_entry *ent;
1033        wddx_stack_top(stack, (void **)&ent);
1034        efree(ent);
1035        stack->top--;
1036    }
1037}
1038/* }}} */
1039
1040/* {{{ php_wddx_process_data
1041 */
1042static void php_wddx_process_data(void *user_data, const XML_Char *s, int len)
1043{
1044    st_entry *ent;
1045    wddx_stack *stack = (wddx_stack *)user_data;
1046    TSRMLS_FETCH();
1047
1048    if (!wddx_stack_is_empty(stack) && !stack->done) {
1049        wddx_stack_top(stack, (void**)&ent);
1050        switch (Z_TYPE_P(ent)) {
1051            case ST_STRING:
1052                if (Z_STRLEN_P(ent->data) == 0) {
1053                    STR_FREE(Z_STRVAL_P(ent->data));
1054                    Z_STRVAL_P(ent->data) = estrndup(s, len);
1055                    Z_STRLEN_P(ent->data) = len;
1056                } else {
1057                    Z_STRVAL_P(ent->data) = erealloc(Z_STRVAL_P(ent->data), Z_STRLEN_P(ent->data) + len + 1);
1058                    memcpy(Z_STRVAL_P(ent->data) + Z_STRLEN_P(ent->data), s, len);
1059                    Z_STRLEN_P(ent->data) += len;
1060                    Z_STRVAL_P(ent->data)[Z_STRLEN_P(ent->data)] = '\0';
1061                }
1062                break;
1063
1064            case ST_BINARY:
1065                if (Z_STRLEN_P(ent->data) == 0) {
1066                    STR_FREE(Z_STRVAL_P(ent->data));
1067                    Z_STRVAL_P(ent->data) = estrndup(s, len + 1);
1068                } else {
1069                    Z_STRVAL_P(ent->data) = erealloc(Z_STRVAL_P(ent->data), Z_STRLEN_P(ent->data) + len + 1);
1070                    memcpy(Z_STRVAL_P(ent->data) + Z_STRLEN_P(ent->data), s, len);
1071                }
1072                Z_STRLEN_P(ent->data) += len;
1073                Z_STRVAL_P(ent->data)[Z_STRLEN_P(ent->data)] = '\0';
1074                break;
1075
1076            case ST_NUMBER:
1077                Z_TYPE_P(ent->data) = IS_STRING;
1078                Z_STRLEN_P(ent->data) = len;
1079                Z_STRVAL_P(ent->data) = estrndup(s, len);
1080                convert_scalar_to_number(ent->data TSRMLS_CC);
1081                break;
1082
1083            case ST_BOOLEAN:
1084                if (!strcmp(s, "true")) {
1085                    Z_LVAL_P(ent->data) = 1;
1086                } else if (!strcmp(s, "false")) {
1087                    Z_LVAL_P(ent->data) = 0;
1088                } else {
1089                    stack->top--;
1090                    zval_ptr_dtor(&ent->data);
1091                    if (ent->varname)
1092                        efree(ent->varname);
1093                    efree(ent);
1094                }
1095                break;
1096
1097            case ST_DATETIME: {
1098                char *tmp;
1099
1100                tmp = emalloc(len + 1);
1101                memcpy(tmp, s, len);
1102                tmp[len] = '\0';
1103
1104                Z_LVAL_P(ent->data) = php_parse_date(tmp, NULL);
1105                /* date out of range < 1969 or > 2038 */
1106                if (Z_LVAL_P(ent->data) == -1) {
1107                    Z_TYPE_P(ent->data) = IS_STRING;
1108                    Z_STRLEN_P(ent->data) = len;
1109                    Z_STRVAL_P(ent->data) = estrndup(s, len);
1110                }
1111                efree(tmp);
1112            }
1113                break;
1114
1115            default:
1116                break;
1117        }
1118    }
1119}
1120/* }}} */
1121
1122/* {{{ php_wddx_deserialize_ex
1123 */
1124int php_wddx_deserialize_ex(char *value, int vallen, zval *return_value)
1125{
1126    wddx_stack stack;
1127    XML_Parser parser;
1128    st_entry *ent;
1129    int retval;
1130
1131    wddx_stack_init(&stack);
1132    parser = XML_ParserCreate("UTF-8");
1133
1134    XML_SetUserData(parser, &stack);
1135    XML_SetElementHandler(parser, php_wddx_push_element, php_wddx_pop_element);
1136    XML_SetCharacterDataHandler(parser, php_wddx_process_data);
1137
1138    XML_Parse(parser, value, vallen, 1);
1139
1140    XML_ParserFree(parser);
1141
1142    if (stack.top == 1) {
1143        wddx_stack_top(&stack, (void**)&ent);
1144        *return_value = *(ent->data);
1145        zval_copy_ctor(return_value);
1146        retval = SUCCESS;
1147    } else {
1148        retval = FAILURE;
1149    }
1150
1151    wddx_stack_destroy(&stack);
1152
1153    return retval;
1154}
1155/* }}} */
1156
1157/* {{{ proto string wddx_serialize_value(mixed var [, string comment])
1158   Creates a new packet and serializes the given value */
1159PHP_FUNCTION(wddx_serialize_value)
1160{
1161    zval *var;
1162    char *comment = NULL;
1163    int comment_len = 0;
1164    wddx_packet *packet;
1165
1166    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &var, &comment, &comment_len) == FAILURE) {
1167        return;
1168    }
1169
1170    packet = php_wddx_constructor();
1171
1172    php_wddx_packet_start(packet, comment, comment_len);
1173    php_wddx_serialize_var(packet, var, NULL, 0 TSRMLS_CC);
1174    php_wddx_packet_end(packet);
1175
1176    ZVAL_STRINGL(return_value, packet->c, packet->len, 1);
1177    smart_str_free(packet);
1178    efree(packet);
1179}
1180/* }}} */
1181
1182/* {{{ proto string wddx_serialize_vars(mixed var_name [, mixed ...])
1183   Creates a new packet and serializes given variables into a struct */
1184PHP_FUNCTION(wddx_serialize_vars)
1185{
1186    int num_args, i;
1187    wddx_packet *packet;
1188    zval ***args = NULL;
1189
1190    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args) == FAILURE) {
1191        return;
1192    }
1193
1194    packet = php_wddx_constructor();
1195
1196    php_wddx_packet_start(packet, NULL, 0);
1197    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1198
1199    for (i=0; i<num_args; i++) {
1200        if (Z_TYPE_PP(args[i]) != IS_ARRAY && Z_TYPE_PP(args[i]) != IS_OBJECT) {
1201            convert_to_string_ex(args[i]);
1202        }
1203        php_wddx_add_var(packet, *args[i]);
1204    }
1205
1206    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1207    php_wddx_packet_end(packet);
1208
1209    efree(args);
1210
1211    ZVAL_STRINGL(return_value, packet->c, packet->len, 1);
1212    smart_str_free(packet);
1213    efree(packet);
1214}
1215/* }}} */
1216
1217/* {{{ php_wddx_constructor
1218 */
1219wddx_packet *php_wddx_constructor(void)
1220{
1221    smart_str *packet;
1222
1223    packet = (smart_str *)emalloc(sizeof(smart_str));
1224    packet->c = NULL;
1225
1226    return packet;
1227}
1228/* }}} */
1229
1230/* {{{ php_wddx_destructor
1231 */
1232void php_wddx_destructor(wddx_packet *packet)
1233{
1234    smart_str_free(packet);
1235    efree(packet);
1236}
1237/* }}} */
1238
1239/* {{{ proto resource wddx_packet_start([string comment])
1240   Starts a WDDX packet with optional comment and returns the packet id */
1241PHP_FUNCTION(wddx_packet_start)
1242{
1243    char *comment = NULL;
1244    int comment_len = 0;
1245    wddx_packet *packet;
1246
1247    comment = NULL;
1248
1249    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &comment, &comment_len) == FAILURE) {
1250        return;
1251    }
1252
1253    packet = php_wddx_constructor();
1254
1255    php_wddx_packet_start(packet, comment, comment_len);
1256    php_wddx_add_chunk_static(packet, WDDX_STRUCT_S);
1257
1258    ZEND_REGISTER_RESOURCE(return_value, packet, le_wddx);
1259}
1260/* }}} */
1261
1262/* {{{ proto string wddx_packet_end(resource packet_id)
1263   Ends specified WDDX packet and returns the string containing the packet */
1264PHP_FUNCTION(wddx_packet_end)
1265{
1266    zval *packet_id;
1267    wddx_packet *packet = NULL;
1268
1269    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &packet_id) == FAILURE) {
1270        return;
1271    }
1272
1273    ZEND_FETCH_RESOURCE(packet, wddx_packet *, &packet_id, -1, "WDDX packet ID", le_wddx);
1274
1275    php_wddx_add_chunk_static(packet, WDDX_STRUCT_E);
1276
1277    php_wddx_packet_end(packet);
1278
1279    ZVAL_STRINGL(return_value, packet->c, packet->len, 1);
1280
1281    zend_list_delete(Z_LVAL_P(packet_id));
1282}
1283/* }}} */
1284
1285/* {{{ proto int wddx_add_vars(resource packet_id,  mixed var_names [, mixed ...])
1286   Serializes given variables and adds them to packet given by packet_id */
1287PHP_FUNCTION(wddx_add_vars)
1288{
1289    int num_args, i;
1290    zval ***args = NULL;
1291    zval *packet_id;
1292    wddx_packet *packet = NULL;
1293
1294    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r+", &packet_id, &args, &num_args) == FAILURE) {
1295        return;
1296    }
1297
1298    if (!ZEND_FETCH_RESOURCE_NO_RETURN(packet, wddx_packet *, &packet_id, -1, "WDDX packet ID", le_wddx)) {
1299        efree(args);
1300        RETURN_FALSE;
1301    }
1302
1303    if (!packet) {
1304        efree(args);
1305        RETURN_FALSE;
1306    }
1307
1308    for (i=0; i<num_args; i++) {
1309        if (Z_TYPE_PP(args[i]) != IS_ARRAY && Z_TYPE_PP(args[i]) != IS_OBJECT) {
1310            convert_to_string_ex(args[i]);
1311        }
1312        php_wddx_add_var(packet, (*args[i]));
1313    }
1314
1315    efree(args);
1316    RETURN_TRUE;
1317}
1318/* }}} */
1319
1320/* {{{ proto mixed wddx_deserialize(mixed packet)
1321   Deserializes given packet and returns a PHP value */
1322PHP_FUNCTION(wddx_deserialize)
1323{
1324    zval *packet;
1325    char *payload;
1326    int payload_len;
1327    php_stream *stream = NULL;
1328
1329    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &packet) == FAILURE) {
1330        return;
1331    }
1332
1333    if (Z_TYPE_P(packet) == IS_STRING) {
1334        payload     = Z_STRVAL_P(packet);
1335        payload_len = Z_STRLEN_P(packet);
1336    } else if (Z_TYPE_P(packet) == IS_RESOURCE) {
1337        php_stream_from_zval(stream, &packet);
1338        if (stream) {
1339            payload_len = php_stream_copy_to_mem(stream, &payload, PHP_STREAM_COPY_ALL, 0);
1340        }
1341    } else {
1342        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expecting parameter 1 to be a string or a stream");
1343        return;
1344    }
1345
1346    if (payload_len == 0) {
1347        return;
1348    }
1349
1350    php_wddx_deserialize_ex(payload, payload_len, return_value);
1351
1352    if (stream) {
1353        pefree(payload, 0);
1354    }
1355}
1356/* }}} */
1357
1358#endif /* HAVE_LIBEXPAT */
1359
1360/*
1361 * Local variables:
1362 * tab-width: 4
1363 * c-basic-offset: 4
1364 * End:
1365 * vim600: sw=4 ts=4 fdm=marker
1366 * vim<600: sw=4 ts=4
1367 */
1368