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: Sterling Hughes <sterling@php.net>                          |
16  |          Marcus Boerger <helly@php.net>                              |
17  |          Rob Richards <rrichards@php.net>                            |
18  +----------------------------------------------------------------------+
19*/
20
21/* $Id: f84e1e014d888b0412910755edb035e56483fcf8 $ */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "php.h"
28#if HAVE_LIBXML && HAVE_SIMPLEXML
29
30#include "php_ini.h"
31#include "ext/standard/info.h"
32#include "ext/standard/php_string.h"
33#include "php_simplexml.h"
34#include "php_simplexml_exports.h"
35#include "zend_exceptions.h"
36#include "zend_interfaces.h"
37#include "sxe.h"
38
39#define SXE_ELEMENT_BY_NAME 0
40
41zend_class_entry *sxe_class_entry = NULL;
42
43PHP_SXE_API zend_class_entry *sxe_get_element_class_entry() /* {{{ */
44{
45    return sxe_class_entry;
46}
47/* }}} */
48
49#define SXE_ME(func, arg_info, flags) PHP_ME(simplexml_element, func, arg_info, flags)
50#define SXE_MALIAS(func, alias, arg_info, flags) PHP_MALIAS(simplexml_element, func, alias, arg_info, flags)
51
52#define SXE_METHOD(func) PHP_METHOD(simplexml_element, func)
53
54static php_sxe_object* php_sxe_object_new(zend_class_entry *ce TSRMLS_DC);
55static zend_object_value php_sxe_register_object(php_sxe_object * TSRMLS_DC);
56static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data TSRMLS_DC);
57static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data TSRMLS_DC);
58static zval *sxe_get_value(zval *z TSRMLS_DC);
59static void php_sxe_iterator_dtor(zend_object_iterator *iter TSRMLS_DC);
60static int php_sxe_iterator_valid(zend_object_iterator *iter TSRMLS_DC);
61static void php_sxe_iterator_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC);
62static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC);
63static void php_sxe_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC);
64static void php_sxe_iterator_rewind(zend_object_iterator *iter TSRMLS_DC);
65
66/* {{{ _node_as_zval()
67 */
68static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE_ITER itertype, char *name, const xmlChar *nsprefix, int isprefix TSRMLS_DC)
69{
70    php_sxe_object *subnode;
71
72    subnode = php_sxe_object_new(sxe->zo.ce TSRMLS_CC);
73    subnode->document = sxe->document;
74    subnode->document->refcount++;
75    subnode->iter.type = itertype;
76    if (name) {
77        subnode->iter.name = xmlStrdup((xmlChar *)name);
78    }
79    if (nsprefix && *nsprefix) {
80        subnode->iter.nsprefix = xmlStrdup(nsprefix);
81        subnode->iter.isprefix = isprefix;
82    }
83
84    php_libxml_increment_node_ptr((php_libxml_node_object *)subnode, node, NULL TSRMLS_CC);
85
86    value->type = IS_OBJECT;
87    value->value.obj = php_sxe_register_object(subnode TSRMLS_CC);
88}
89/* }}} */
90
91#define APPEND_PREV_ELEMENT(__c, __v) \
92    if ((__c) == 1) { \
93        array_init(return_value); \
94        add_next_index_zval(return_value, __v); \
95    }
96
97#define APPEND_CUR_ELEMENT(__c, __v) \
98    if (++(__c) > 1) { \
99        add_next_index_zval(return_value, __v); \
100    }
101
102#define GET_NODE(__s, __n) { \
103    if ((__s)->node && (__s)->node->node) { \
104        __n = (__s)->node->node; \
105    } else { \
106        __n = NULL; \
107        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Node no longer exists"); \
108    } \
109}
110
111static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node TSRMLS_DC) /* {{{ */
112{
113    php_sxe_object *intern;
114    xmlNodePtr retnode = NULL;
115
116    if (sxe && sxe->iter.type != SXE_ITER_NONE) {
117        php_sxe_reset_iterator(sxe, 1 TSRMLS_CC);
118        if (sxe->iter.data) {
119            intern = (php_sxe_object *)zend_object_store_get_object(sxe->iter.data TSRMLS_CC);
120            GET_NODE(intern, retnode)
121        }
122        return retnode;
123    } else {
124        return node;
125    }
126}
127/* }}} */
128
129static inline int match_ns(php_sxe_object *sxe, xmlNodePtr node, xmlChar *name, int prefix) /* {{{ */
130{
131    if (name == NULL && (node->ns == NULL || node->ns->prefix == NULL)) {
132        return 1;
133    }
134
135    if (node->ns && !xmlStrcmp(prefix ? node->ns->prefix : node->ns->href, name)) {
136        return 1;
137    }
138
139    return 0;
140}
141/* }}} */
142
143static xmlNodePtr sxe_get_element_by_offset(php_sxe_object *sxe, long offset, xmlNodePtr node, long *cnt) /* {{{ */
144{
145    long nodendx = 0;
146
147    if (sxe->iter.type == SXE_ITER_NONE) {
148        if (offset == 0) {
149            if (cnt) {
150                *cnt = 0;
151            }
152            return node;
153        } else {
154            return NULL;
155        }
156    }
157    while (node && nodendx <= offset) {
158        SKIP_TEXT(node)
159        if (node->type == XML_ELEMENT_NODE && match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
160            if (sxe->iter.type == SXE_ITER_CHILD || (
161                sxe->iter.type == SXE_ITER_ELEMENT && !xmlStrcmp(node->name, sxe->iter.name))) {
162                if (nodendx == offset) {
163                    break;
164                }
165                nodendx++;
166            }
167        }
168next_iter:
169        node = node->next;
170    }
171
172    if (cnt) {
173        *cnt = nodendx;
174    }
175
176    return node;
177}
178/* }}} */
179
180static xmlNodePtr sxe_find_element_by_name(php_sxe_object *sxe, xmlNodePtr node, xmlChar *name TSRMLS_DC) /* {{{ */
181{
182    while (node) {
183        SKIP_TEXT(node)
184        if (node->type == XML_ELEMENT_NODE && match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
185            if (!xmlStrcmp(node->name, name)) {
186                return node;
187            }
188        }
189next_iter:
190        node = node->next;
191    }
192    return NULL;
193} /* }}} */
194
195static xmlNodePtr sxe_get_element_by_name(php_sxe_object *sxe, xmlNodePtr node, char **name, SXE_ITER *type TSRMLS_DC) /* {{{ */
196{
197    int         orgtype;
198    xmlNodePtr  orgnode = node;
199    xmlNodePtr  retnode = NULL;
200
201    if (sxe->iter.type != SXE_ITER_ATTRLIST)
202    {
203        orgtype = sxe->iter.type;
204        if (sxe->iter.type == SXE_ITER_NONE) {
205            sxe->iter.type = SXE_ITER_CHILD;
206        }
207        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
208        sxe->iter.type = orgtype;
209    }
210
211    if (sxe->iter.type == SXE_ITER_ELEMENT) {
212        orgnode = sxe_find_element_by_name(sxe, node, sxe->iter.name TSRMLS_CC);
213        if (!orgnode) {
214            return NULL;
215        }
216        node = orgnode->children;
217    }
218
219    while (node) {
220        SKIP_TEXT(node)
221        if (node->type == XML_ELEMENT_NODE && match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
222            if (!xmlStrcmp(node->name, (xmlChar *)*name)) {
223                if (1||retnode)
224                {
225                    *type = SXE_ITER_ELEMENT;
226                    return orgnode;
227                }
228                retnode = node;
229            }
230        }
231next_iter:
232        node = node->next;
233    }
234
235    if (retnode)
236    {
237        *type = SXE_ITER_NONE;
238        *name = NULL;
239        return retnode;
240    }
241
242    return NULL;
243}
244/* }}} */
245
246/* {{{ sxe_prop_dim_read()
247 */
248static zval * sxe_prop_dim_read(zval *object, zval *member, zend_bool elements, zend_bool attribs, int type TSRMLS_DC)
249{
250    zval           *return_value;
251    php_sxe_object *sxe;
252    char           *name;
253    xmlNodePtr      node;
254    xmlAttrPtr      attr = NULL;
255    zval            tmp_zv;
256    int             nodendx = 0;
257    int             test = 0;
258
259    sxe = php_sxe_fetch_object(object TSRMLS_CC);
260
261    if (!member || Z_TYPE_P(member) == IS_LONG) {
262        if (sxe->iter.type != SXE_ITER_ATTRLIST) {
263            attribs = 0;
264            elements = 1;
265        } else if (!member) {
266            /* This happens when the user did: $sxe[]->foo = $value */
267            php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot create unnamed attribute");
268            return NULL;
269        }
270        name = NULL;
271    } else {
272        if (Z_TYPE_P(member) != IS_STRING) {
273            tmp_zv = *member;
274            zval_copy_ctor(&tmp_zv);
275            member = &tmp_zv;
276            convert_to_string(member);
277        }
278        name = Z_STRVAL_P(member);
279    }
280
281    GET_NODE(sxe, node);
282
283    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
284        attribs = 1;
285        elements = 0;
286        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
287        attr = (xmlAttrPtr)node;
288        test = sxe->iter.name != NULL;
289    } else if (sxe->iter.type != SXE_ITER_CHILD) {
290        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
291        attr = node ? node->properties : NULL;
292        test = 0;
293        if (!member && node && node->parent &&
294            node->parent->type == XML_DOCUMENT_NODE) {
295            /* This happens when the user did: $sxe[]->foo = $value */
296            php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot create unnamed attribute");
297            return NULL;
298        }
299    }
300
301    MAKE_STD_ZVAL(return_value);
302    ZVAL_NULL(return_value);
303
304    if (node) {
305        if (attribs) {
306            if (Z_TYPE_P(member) != IS_LONG || sxe->iter.type == SXE_ITER_ATTRLIST) {
307                if (Z_TYPE_P(member) == IS_LONG) {
308                    while (attr && nodendx <= Z_LVAL_P(member)) {
309                        if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
310                            if (nodendx == Z_LVAL_P(member)) {
311                                _node_as_zval(sxe, (xmlNodePtr) attr, return_value, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
312                                break;
313                            }
314                            nodendx++;
315                        }
316                        attr = attr->next;
317                    }
318                } else {
319                    while (attr) {
320                        if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && !xmlStrcmp(attr->name, (xmlChar *)name) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
321                            _node_as_zval(sxe, (xmlNodePtr) attr, return_value, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
322                            break;
323                        }
324                        attr = attr->next;
325                    }
326                }
327            }
328        }
329
330        if (elements) {
331            if (!sxe->node) {
332                php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, node, NULL TSRMLS_CC);
333            }
334            if (!member || Z_TYPE_P(member) == IS_LONG) {
335                long cnt = 0;
336                xmlNodePtr mynode = node;
337
338                if (sxe->iter.type == SXE_ITER_CHILD) {
339                    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
340                }
341                if (sxe->iter.type == SXE_ITER_NONE) {
342                    if (member && Z_LVAL_P(member) > 0) {
343                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element %s number %ld when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
344                    }
345                } else if (member) {
346                    node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, &cnt);
347                } else {
348                    node = NULL;
349                }
350                if (node) {
351                    _node_as_zval(sxe, node, return_value, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
352                } else if (type == BP_VAR_W || type == BP_VAR_RW) {
353                    if (member && cnt < Z_LVAL_P(member)) {
354                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element %s number %ld when only %ld such elements exist", mynode->name, Z_LVAL_P(member), cnt);
355                    }
356                    node = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, NULL);
357                    _node_as_zval(sxe, node, return_value, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
358                }
359            } else {
360#if SXE_ELEMENT_BY_NAME
361                int newtype;
362
363                GET_NODE(sxe, node);
364                node = sxe_get_element_by_name(sxe, node, &name, &newtype TSRMLS_CC);
365                if (node) {
366                    _node_as_zval(sxe, node, return_value, newtype, name, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
367                }
368#else
369                _node_as_zval(sxe, node, return_value, SXE_ITER_ELEMENT, name, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
370#endif
371            }
372        }
373    }
374
375    Z_SET_REFCOUNT_P(return_value, 0);
376    Z_UNSET_ISREF_P(return_value);
377
378    if (member == &tmp_zv) {
379        zval_dtor(&tmp_zv);
380    }
381    if (Z_TYPE_P(return_value) == IS_NULL) {
382        FREE_ZVAL(return_value);
383        return_value = &EG(uninitialized_zval);
384    }
385
386    return return_value;
387}
388/* }}} */
389
390/* {{{ sxe_property_read()
391 */
392static zval * sxe_property_read(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC)
393{
394    return sxe_prop_dim_read(object, member, 1, 0, type TSRMLS_CC);
395}
396/* }}} */
397
398/* {{{ sxe_dimension_read()
399 */
400static zval * sxe_dimension_read(zval *object, zval *offset, int type TSRMLS_DC)
401{
402    return sxe_prop_dim_read(object, offset, 0, 1, type TSRMLS_CC);
403}
404/* }}} */
405
406/* {{{ change_node_zval()
407 */
408static void change_node_zval(xmlNodePtr node, zval *value TSRMLS_DC)
409{
410    zval value_copy;
411    xmlChar *buffer;
412    int buffer_len;
413
414    if (!value)
415    {
416        xmlNodeSetContentLen(node, (xmlChar *)"", 0);
417        return;
418    }
419    switch (Z_TYPE_P(value)) {
420        case IS_LONG:
421        case IS_BOOL:
422        case IS_DOUBLE:
423        case IS_NULL:
424            if (Z_REFCOUNT_P(value) > 1) {
425                value_copy = *value;
426                zval_copy_ctor(&value_copy);
427                value = &value_copy;
428            }
429            convert_to_string(value);
430            /* break missing intentionally */
431        case IS_STRING:
432            buffer = xmlEncodeEntitiesReentrant(node->doc, (xmlChar *)Z_STRVAL_P(value));
433            buffer_len = xmlStrlen(buffer);
434            /* check for NULL buffer in case of memory error in xmlEncodeEntitiesReentrant */
435            if (buffer) {
436                xmlNodeSetContentLen(node, buffer, buffer_len);
437                xmlFree(buffer);
438            }
439            if (value == &value_copy) {
440                zval_dtor(value);
441            }
442            break;
443        default:
444            php_error_docref(NULL TSRMLS_CC, E_WARNING, "It is not possible to assign complex types to nodes");
445            break;
446    }
447}
448/* }}} */
449
450/* {{{ sxe_property_write()
451 */
452static int sxe_prop_dim_write(zval *object, zval *member, zval *value, zend_bool elements, zend_bool attribs, xmlNodePtr *pnewnode TSRMLS_DC)
453{
454    php_sxe_object *sxe;
455    xmlNodePtr      node;
456    xmlNodePtr      newnode = NULL;
457    xmlNodePtr      mynode;
458    xmlNodePtr      tempnode;
459    xmlAttrPtr      attr = NULL;
460    int             counter = 0;
461    int             is_attr = 0;
462    int             nodendx = 0;
463    int             test = 0;
464    int             new_value = 0;
465    long            cnt = 0;
466    int             retval = SUCCESS;
467    zval            tmp_zv, trim_zv, value_copy;
468
469    sxe = php_sxe_fetch_object(object TSRMLS_CC);
470
471    if (!member || Z_TYPE_P(member) == IS_LONG) {
472        if (sxe->iter.type != SXE_ITER_ATTRLIST) {
473            attribs = 0;
474            elements = 1;
475        } else if (!member) {
476            /* This happens when the user did: $sxe[] = $value
477             * and could also be E_PARSE, but we use this only during parsing
478             * and this is during runtime.
479             */
480            php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot create unnamed attribute");
481            return FAILURE;
482        }
483    } else {
484        if (Z_TYPE_P(member) != IS_STRING) {
485            trim_zv = *member;
486            zval_copy_ctor(&trim_zv);
487            convert_to_string(&trim_zv);
488            php_trim(Z_STRVAL(trim_zv), Z_STRLEN(trim_zv), NULL, 0, &tmp_zv, 3 TSRMLS_CC);
489            zval_dtor(&trim_zv);
490            member = &tmp_zv;
491        }
492
493        if (!Z_STRLEN_P(member)) {
494            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot write or create unnamed %s", attribs ? "attribute" : "element");
495            if (member == &tmp_zv) {
496                zval_dtor(&tmp_zv);
497            }
498            return FAILURE;
499        }
500    }
501
502    GET_NODE(sxe, node);
503
504    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
505        attribs = 1;
506        elements = 0;
507        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
508        attr = (xmlAttrPtr)node;
509        test = sxe->iter.name != NULL;
510    } else if (sxe->iter.type != SXE_ITER_CHILD) {
511        mynode = node;
512        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
513        attr = node ? node->properties : NULL;
514        test = 0;
515        if (!member && node && node->parent &&
516            node->parent->type == XML_DOCUMENT_NODE) {
517            /* This happens when the user did: $sxe[] = $value
518             * and could also be E_PARSE, but we use this only during parsing
519             * and this is during runtime.
520             */
521            php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot create unnamed attribute");
522            return FAILURE;
523        }
524        if (attribs && !node && sxe->iter.type == SXE_ITER_ELEMENT) {
525            node = xmlNewChild(mynode, mynode->ns, sxe->iter.name, NULL);
526            attr = node->properties;
527        }
528    }
529
530    mynode = node;
531
532    if (value) {
533        switch (Z_TYPE_P(value)) {
534            case IS_LONG:
535            case IS_BOOL:
536            case IS_DOUBLE:
537            case IS_NULL:
538                if (Z_REFCOUNT_P(value) > 1) {
539                    value_copy = *value;
540                    zval_copy_ctor(&value_copy);
541                    value = &value_copy;
542                }
543                convert_to_string(value);
544                break;
545            case IS_STRING:
546                break;
547            case IS_OBJECT:
548                if (Z_OBJCE_P(value) == sxe_class_entry) {
549                    value = sxe_get_value(value TSRMLS_CC);
550                    INIT_PZVAL(value);
551                    new_value = 1;
552                    break;
553                }
554                /* break is missing intentionally */
555            default:
556                if (member == &tmp_zv) {
557                    zval_dtor(&tmp_zv);
558                }
559                zend_error(E_WARNING, "It is not yet possible to assign complex types to %s", attribs ? "attributes" : "properties");
560                return FAILURE;
561        }
562    }
563
564    if (node) {
565        if (attribs) {
566            if (Z_TYPE_P(member) == IS_LONG) {
567                while (attr && nodendx <= Z_LVAL_P(member)) {
568                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
569                        if (nodendx == Z_LVAL_P(member)) {
570                            is_attr = 1;
571                            ++counter;
572                            break;
573                        }
574                        nodendx++;
575                    }
576                    attr = attr->next;
577                }
578            } else {
579                while (attr) {
580                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && !xmlStrcmp(attr->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
581                        is_attr = 1;
582                        ++counter;
583                        break;
584                    }
585                    attr = attr->next;
586                }
587            }
588
589        }
590
591        if (elements) {
592            if (!member || Z_TYPE_P(member) == IS_LONG) {
593                if (node->type == XML_ATTRIBUTE_NODE) {
594                    php_error_docref(NULL TSRMLS_CC, E_ERROR, "Cannot create duplicate attribute");
595                    return FAILURE;
596                }
597
598                if (sxe->iter.type == SXE_ITER_NONE) {
599                    newnode = node;
600                    ++counter;
601                    if (member && Z_LVAL_P(member) > 0) {
602                        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element %s number %ld when only 0 such elements exist", mynode->name, Z_LVAL_P(member));
603                        retval = FAILURE;
604                    }
605                } else if (member) {
606                    newnode = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, &cnt);
607                    if (newnode) {
608                        ++counter;
609                    }
610                }
611            } else {
612                node = node->children;
613                while (node) {
614                    SKIP_TEXT(node);
615
616                    if (!xmlStrcmp(node->name, (xmlChar *)Z_STRVAL_P(member))) {
617                        newnode = node;
618                        ++counter;
619                    }
620
621next_iter:
622                    node = node->next;
623                }
624            }
625        }
626
627        if (counter == 1) {
628            if (is_attr) {
629                newnode = (xmlNodePtr) attr;
630            }
631            if (value) {
632                while ((tempnode = (xmlNodePtr) newnode->children)) {
633                    xmlUnlinkNode(tempnode);
634                    php_libxml_node_free_resource((xmlNodePtr) tempnode TSRMLS_CC);
635                }
636                change_node_zval(newnode, value TSRMLS_CC);
637            }
638        } else if (counter > 1) {
639            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot assign to an array of nodes (duplicate subnodes or attr detected)");
640            retval = FAILURE;
641        } else if (elements) {
642            if (!node) {
643                if (!member || Z_TYPE_P(member) == IS_LONG) {
644                    newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
645                } else {
646                    newnode = xmlNewTextChild(mynode, mynode->ns, (xmlChar *)Z_STRVAL_P(member), value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
647                }
648            } else if (!member || Z_TYPE_P(member) == IS_LONG) {
649                if (member && cnt < Z_LVAL_P(member)) {
650                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element %s number %ld when only %ld such elements exist", mynode->name, Z_LVAL_P(member), cnt);
651                    retval = FAILURE;
652                }
653                newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
654            }
655        } else if (attribs) {
656            if (Z_TYPE_P(member) == IS_LONG) {
657                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot change attribute number %ld when only %d attributes exist", Z_LVAL_P(member), nodendx);
658                retval = FAILURE;
659            } else {
660                newnode = (xmlNodePtr)xmlNewProp(node, (xmlChar *)Z_STRVAL_P(member), value ? (xmlChar *)Z_STRVAL_P(value) : NULL);
661            }
662        }
663    }
664
665    if (member == &tmp_zv) {
666        zval_dtor(&tmp_zv);
667    }
668    if (pnewnode) {
669        *pnewnode = newnode;
670    }
671    if (value && value == &value_copy) {
672        zval_dtor(value);
673    }
674    if (new_value) {
675        zval_ptr_dtor(&value);
676    }
677    return retval;
678}
679/* }}} */
680
681/* {{{ sxe_property_write()
682 */
683static void sxe_property_write(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC)
684{
685    sxe_prop_dim_write(object, member, value, 1, 0, NULL TSRMLS_CC);
686}
687/* }}} */
688
689/* {{{ sxe_dimension_write()
690 */
691static void sxe_dimension_write(zval *object, zval *offset, zval *value TSRMLS_DC)
692{
693    sxe_prop_dim_write(object, offset, value, 0, 1, NULL TSRMLS_CC);
694}
695/* }}} */
696
697static zval** sxe_property_get_adr(zval *object, zval *member, int fetch_type, const zend_literal *key TSRMLS_DC) /* {{{ */
698{
699    php_sxe_object *sxe;
700    xmlNodePtr      node;
701    zval           *return_value;
702    char           *name;
703    SXE_ITER        type;
704
705    sxe = php_sxe_fetch_object(object TSRMLS_CC);
706
707    GET_NODE(sxe, node);
708    convert_to_string(member);
709    name = Z_STRVAL_P(member);
710    node = sxe_get_element_by_name(sxe, node, &name, &type TSRMLS_CC);
711    if (node) {
712        return NULL;
713    }
714    if (sxe_prop_dim_write(object, member, NULL, 1, 0, &node TSRMLS_CC) != SUCCESS) {
715        return NULL;
716    }
717    type = SXE_ITER_NONE;
718    name = NULL;
719
720    MAKE_STD_ZVAL(return_value);
721    _node_as_zval(sxe, node, return_value, type, name, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
722
723    sxe = php_sxe_fetch_object(return_value TSRMLS_CC);
724    if (sxe->tmp) {
725        zval_ptr_dtor(&sxe->tmp);
726    }
727    sxe->tmp = return_value;
728    Z_SET_ISREF_P(return_value);
729
730    return &sxe->tmp;
731}
732/* }}} */
733
734/* {{{ sxe_prop_dim_exists()
735 */
736static int sxe_prop_dim_exists(zval *object, zval *member, int check_empty, zend_bool elements, zend_bool attribs TSRMLS_DC)
737{
738    php_sxe_object *sxe;
739    xmlNodePtr      node;
740    xmlAttrPtr      attr = NULL;
741    int             exists = 0;
742    int             test = 0;
743    zval            tmp_zv;
744
745    if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) {
746        tmp_zv = *member;
747        zval_copy_ctor(&tmp_zv);
748        member = &tmp_zv;
749        convert_to_string(member);
750    }
751
752    sxe = php_sxe_fetch_object(object TSRMLS_CC);
753
754    GET_NODE(sxe, node);
755
756    if (Z_TYPE_P(member) == IS_LONG) {
757        if (sxe->iter.type != SXE_ITER_ATTRLIST) {
758            attribs = 0;
759            elements = 1;
760            if (sxe->iter.type == SXE_ITER_CHILD) {
761                node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
762            }
763        }
764    }
765
766    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
767        attribs = 1;
768        elements = 0;
769        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
770        attr = (xmlAttrPtr)node;
771        test = sxe->iter.name != NULL;
772    } else if (sxe->iter.type != SXE_ITER_CHILD) {
773        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
774        attr = node ? node->properties : NULL;
775        test = 0;
776    }
777
778    if (node) {
779        if (attribs) {
780            if (Z_TYPE_P(member) == IS_LONG) {
781                int nodendx = 0;
782
783                while (attr && nodendx <= Z_LVAL_P(member)) {
784                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
785                        if (nodendx == Z_LVAL_P(member)) {
786                            exists = 1;
787                            break;
788                        }
789                        nodendx++;
790                    }
791                    attr = attr->next;
792                }
793            } else {
794                while (attr) {
795                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && !xmlStrcmp(attr->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
796                        exists = 1;
797                        break;
798                    }
799
800                    attr = attr->next;
801                }
802            }
803            if (exists && check_empty == 1 &&
804                (!attr->children || !attr->children->content || !attr->children->content[0] || !xmlStrcmp(attr->children->content, "0")) ) {
805                /* Attribute with no content in it's text node */
806                exists = 0;
807            }
808        }
809
810        if (elements) {
811            if (Z_TYPE_P(member) == IS_LONG) {
812                if (sxe->iter.type == SXE_ITER_CHILD) {
813                    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
814                }
815                node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
816            }
817            else {
818                node = node->children;
819                while (node) {
820                    xmlNodePtr nnext;
821                    nnext = node->next;
822                    if ((node->type == XML_ELEMENT_NODE) && !xmlStrcmp(node->name, (xmlChar *)Z_STRVAL_P(member))) {
823                        break;
824                    }
825                    node = nnext;
826                }
827            }
828            if (node) {
829                exists = 1;
830                                if (check_empty == 1 &&
831                    (!node->children || (node->children->type == XML_TEXT_NODE && !node->children->next &&
832                        (!node->children->content || !node->children->content[0] || !xmlStrcmp(node->children->content, "0")))) ) {
833                    exists = 0;
834                }
835            }
836        }
837    }
838
839    if (member == &tmp_zv) {
840        zval_dtor(&tmp_zv);
841    }
842
843    return exists;
844}
845/* }}} */
846
847/* {{{ sxe_property_exists()
848 */
849static int sxe_property_exists(zval *object, zval *member, int check_empty, const zend_literal *key TSRMLS_DC)
850{
851    return sxe_prop_dim_exists(object, member, check_empty, 1, 0 TSRMLS_CC);
852}
853/* }}} */
854
855/* {{{ sxe_property_exists()
856 */
857static int sxe_dimension_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
858{
859    return sxe_prop_dim_exists(object, member, check_empty, 0, 1 TSRMLS_CC);
860}
861/* }}} */
862
863/* {{{ sxe_prop_dim_delete()
864 */
865static void sxe_prop_dim_delete(zval *object, zval *member, zend_bool elements, zend_bool attribs TSRMLS_DC)
866{
867    php_sxe_object *sxe;
868    xmlNodePtr      node;
869    xmlNodePtr      nnext;
870    xmlAttrPtr      attr = NULL;
871    xmlAttrPtr      anext;
872    zval            tmp_zv;
873    int             test = 0;
874
875    if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) {
876        tmp_zv = *member;
877        zval_copy_ctor(&tmp_zv);
878        member = &tmp_zv;
879        convert_to_string(member);
880    }
881
882    sxe = php_sxe_fetch_object(object TSRMLS_CC);
883
884    GET_NODE(sxe, node);
885
886    if (Z_TYPE_P(member) == IS_LONG) {
887        if (sxe->iter.type != SXE_ITER_ATTRLIST) {
888            attribs = 0;
889            elements = 1;
890            if (sxe->iter.type == SXE_ITER_CHILD) {
891                node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
892            }
893        }
894    }
895
896    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
897        attribs = 1;
898        elements = 0;
899        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
900        attr = (xmlAttrPtr)node;
901        test = sxe->iter.name != NULL;
902    } else if (sxe->iter.type != SXE_ITER_CHILD) {
903        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
904        attr = node ? node->properties : NULL;
905        test = 0;
906    }
907
908    if (node) {
909        if (attribs) {
910            if (Z_TYPE_P(member) == IS_LONG) {
911                int nodendx = 0;
912
913                while (attr && nodendx <= Z_LVAL_P(member)) {
914                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
915                        if (nodendx == Z_LVAL_P(member)) {
916                            xmlUnlinkNode((xmlNodePtr) attr);
917                            php_libxml_node_free_resource((xmlNodePtr) attr TSRMLS_CC);
918                            break;
919                        }
920                        nodendx++;
921                    }
922                    attr = attr->next;
923                }
924            } else {
925                while (attr) {
926                    anext = attr->next;
927                    if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && !xmlStrcmp(attr->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
928                        xmlUnlinkNode((xmlNodePtr) attr);
929                        php_libxml_node_free_resource((xmlNodePtr) attr TSRMLS_CC);
930                        break;
931                    }
932                    attr = anext;
933                }
934            }
935        }
936
937        if (elements) {
938            if (Z_TYPE_P(member) == IS_LONG) {
939                if (sxe->iter.type == SXE_ITER_CHILD) {
940                    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
941                }
942                node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
943                if (node) {
944                    xmlUnlinkNode(node);
945                    php_libxml_node_free_resource(node TSRMLS_CC);
946                }
947            } else {
948                node = node->children;
949                while (node) {
950                    nnext = node->next;
951
952                    SKIP_TEXT(node);
953
954                    if (!xmlStrcmp(node->name, (xmlChar *)Z_STRVAL_P(member))) {
955                        xmlUnlinkNode(node);
956                        php_libxml_node_free_resource(node TSRMLS_CC);
957                    }
958
959next_iter:
960                    node = nnext;
961                }
962            }
963        }
964    }
965
966    if (member == &tmp_zv) {
967        zval_dtor(&tmp_zv);
968    }
969}
970/* }}} */
971
972/* {{{ sxe_property_delete()
973 */
974static void sxe_property_delete(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
975{
976    sxe_prop_dim_delete(object, member, 1, 0 TSRMLS_CC);
977}
978/* }}} */
979
980/* {{{ sxe_dimension_unset()
981 */
982static void sxe_dimension_delete(zval *object, zval *offset TSRMLS_DC)
983{
984    sxe_prop_dim_delete(object, offset, 0, 1 TSRMLS_CC);
985}
986/* }}} */
987
988static inline char * sxe_xmlNodeListGetString(xmlDocPtr doc, xmlNodePtr list, int inLine) /* {{{ */
989{
990    xmlChar *tmp = xmlNodeListGetString(doc, list, inLine);
991    char    *res;
992
993    if (tmp) {
994        res = estrdup((char*)tmp);
995        xmlFree(tmp);
996    } else {
997        res = STR_EMPTY_ALLOC();
998    }
999
1000    return res;
1001}
1002/* }}} */
1003
1004/* {{{ _get_base_node_value()
1005 */
1006static void _get_base_node_value(php_sxe_object *sxe_ref, xmlNodePtr node, zval **value, xmlChar *nsprefix, int isprefix TSRMLS_DC)
1007{
1008    php_sxe_object *subnode;
1009    xmlChar        *contents;
1010
1011    MAKE_STD_ZVAL(*value);
1012
1013    if (node->children && node->children->type == XML_TEXT_NODE && !xmlIsBlankNode(node->children)) {
1014        contents = xmlNodeListGetString(node->doc, node->children, 1);
1015        if (contents) {
1016            ZVAL_STRING(*value, (char *)contents, 1);
1017            xmlFree(contents);
1018        }
1019    } else {
1020        subnode = php_sxe_object_new(sxe_ref->zo.ce TSRMLS_CC);
1021        subnode->document = sxe_ref->document;
1022        subnode->document->refcount++;
1023        if (nsprefix && *nsprefix) {
1024            subnode->iter.nsprefix = xmlStrdup((xmlChar *)nsprefix);
1025            subnode->iter.isprefix = isprefix;
1026        }
1027        php_libxml_increment_node_ptr((php_libxml_node_object *)subnode, node, NULL TSRMLS_CC);
1028
1029        (*value)->type = IS_OBJECT;
1030        (*value)->value.obj = php_sxe_register_object(subnode TSRMLS_CC);
1031        /*zval_add_ref(value);*/
1032    }
1033}
1034/* }}} */
1035
1036static void sxe_properties_add(HashTable *rv, char *name, int namelen, zval *value TSRMLS_DC) /* {{{ */
1037{
1038    zval  **data_ptr;
1039    zval  *newptr;
1040    ulong h = zend_hash_func(name, namelen);
1041
1042    if (zend_hash_quick_find(rv, name, namelen, h, (void **) &data_ptr) == SUCCESS) {
1043        if (Z_TYPE_PP(data_ptr) == IS_ARRAY) {
1044            zend_hash_next_index_insert(Z_ARRVAL_PP(data_ptr), &value, sizeof(zval *), NULL);
1045        } else {
1046            MAKE_STD_ZVAL(newptr);
1047            array_init(newptr);
1048
1049            zval_add_ref(data_ptr);
1050            zend_hash_next_index_insert(Z_ARRVAL_P(newptr), data_ptr, sizeof(zval *), NULL);
1051            zend_hash_next_index_insert(Z_ARRVAL_P(newptr), &value, sizeof(zval *), NULL);
1052
1053            zend_hash_quick_update(rv, name, namelen, h, &newptr, sizeof(zval *), NULL);
1054        }
1055    } else {
1056        zend_hash_quick_update(rv, name, namelen, h, &value, sizeof(zval *), NULL);
1057    }
1058}
1059/* }}} */
1060
1061static HashTable * sxe_get_prop_hash(zval *object, int is_debug TSRMLS_DC) /* {{{ */
1062{
1063    zval            *value;
1064    zval            *zattr;
1065    HashTable       *rv;
1066    php_sxe_object  *sxe;
1067    char            *name;
1068    xmlNodePtr       node;
1069    xmlAttrPtr       attr;
1070    int              namelen;
1071    int              test;
1072    char         use_iter;
1073    zval            *iter_data = NULL;
1074
1075    use_iter = 0;
1076
1077    sxe = php_sxe_fetch_object(object TSRMLS_CC);
1078
1079    if (is_debug) {
1080        ALLOC_HASHTABLE(rv);
1081        zend_hash_init(rv, 0, NULL, ZVAL_PTR_DTOR, 0);
1082    }
1083    else if (sxe->properties) {
1084        zend_hash_clean(sxe->properties);
1085        rv = sxe->properties;
1086    } else {
1087        ALLOC_HASHTABLE(rv);
1088        zend_hash_init(rv, 0, NULL, ZVAL_PTR_DTOR, 0);
1089        sxe->properties = rv;
1090    }
1091
1092    GET_NODE(sxe, node);
1093    if (!node) {
1094        return rv;
1095    }
1096    if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
1097        if (sxe->iter.type == SXE_ITER_ELEMENT) {
1098            node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1099        }
1100        if (!node || node->type != XML_ENTITY_DECL) {
1101            attr = node ? (xmlAttrPtr)node->properties : NULL;
1102            zattr = NULL;
1103            test = sxe->iter.name && sxe->iter.type == SXE_ITER_ATTRLIST;
1104            while (attr) {
1105                if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
1106                    MAKE_STD_ZVAL(value);
1107                    ZVAL_STRING(value, sxe_xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, attr->children, 1), 0);
1108                    namelen = xmlStrlen(attr->name) + 1;
1109                    if (!zattr) {
1110                        MAKE_STD_ZVAL(zattr);
1111                        array_init(zattr);
1112                        sxe_properties_add(rv, "@attributes", sizeof("@attributes"), zattr TSRMLS_CC);
1113                    }
1114                    add_assoc_zval_ex(zattr, (char*)attr->name, namelen, value);
1115                }
1116                attr = attr->next;
1117            }
1118        }
1119    }
1120
1121    GET_NODE(sxe, node);
1122    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1123
1124    if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
1125        if (node->type == XML_ATTRIBUTE_NODE) {
1126            MAKE_STD_ZVAL(value);
1127            ZVAL_STRING(value, sxe_xmlNodeListGetString(node->doc, node->children, 1), 0);
1128            zend_hash_next_index_insert(rv, &value, sizeof(zval *), NULL);
1129            node = NULL;
1130        } else if (sxe->iter.type != SXE_ITER_CHILD) {
1131
1132            if ( !node->children || !node->parent || !node->next || node->children->next || node->children->children || node->parent->children == node->parent->last ) {
1133                node = node->children;
1134            } else {
1135                iter_data = sxe->iter.data;
1136                sxe->iter.data = NULL;
1137
1138                node = php_sxe_reset_iterator(sxe, 0 TSRMLS_CC);
1139
1140                use_iter = 1;
1141            }
1142        }
1143
1144        while (node) {
1145            if (node->children != NULL || node->prev != NULL || node->next != NULL) {
1146                SKIP_TEXT(node);
1147            } else {
1148                if (node->type == XML_TEXT_NODE) {
1149                    const xmlChar *cur = node->content;
1150
1151                    if (*cur != 0) {
1152                        MAKE_STD_ZVAL(value);
1153                        ZVAL_STRING(value, sxe_xmlNodeListGetString(node->doc, node, 1), 0);
1154                        zend_hash_next_index_insert(rv, &value, sizeof(zval *), NULL);
1155                    }
1156                    goto next_iter;
1157                }
1158            }
1159
1160            if (node->type == XML_ELEMENT_NODE && (! match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix))) {
1161                goto next_iter;
1162            }
1163
1164            name = (char *) node->name;
1165            if (!name) {
1166                goto next_iter;
1167            } else {
1168                namelen = xmlStrlen(node->name) + 1;
1169            }
1170
1171            _get_base_node_value(sxe, node, &value, sxe->iter.nsprefix, sxe->iter.isprefix TSRMLS_CC);
1172
1173            if ( use_iter ) {
1174                zend_hash_next_index_insert(rv, &value, sizeof(zval *), NULL);
1175            } else {
1176                sxe_properties_add(rv, name, namelen, value TSRMLS_CC);
1177            }
1178next_iter:
1179            if ( use_iter ) {
1180                node = php_sxe_iterator_fetch(sxe, node->next, 0 TSRMLS_CC);
1181            } else {
1182                node = node->next;
1183            }
1184        }
1185    }
1186
1187    if ( use_iter ) {
1188        if (sxe->iter.data) {
1189            zval_ptr_dtor(&sxe->iter.data);
1190        }
1191        sxe->iter.data = iter_data;
1192    }
1193
1194    return rv;
1195}
1196/* }}} */
1197
1198static HashTable * sxe_get_gc(zval *object, zval ***table, int *n TSRMLS_DC) /* {{{ */ {
1199    php_sxe_object  *sxe;
1200    sxe = php_sxe_fetch_object(object TSRMLS_CC);
1201
1202    *table = NULL;
1203    *n = 0;
1204    return sxe->properties;
1205}
1206/* }}} */
1207
1208static HashTable * sxe_get_properties(zval *object TSRMLS_DC) /* {{{ */
1209{
1210    return sxe_get_prop_hash(object, 0 TSRMLS_CC);
1211}
1212/* }}} */
1213
1214static HashTable * sxe_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
1215{
1216    *is_temp = 1;
1217    return sxe_get_prop_hash(object, 1 TSRMLS_CC);
1218}
1219/* }}} */
1220
1221static int sxe_objects_compare(zval *object1, zval *object2 TSRMLS_DC) /* {{{ */
1222{
1223    php_sxe_object *sxe1;
1224    php_sxe_object *sxe2;
1225
1226    sxe1 = php_sxe_fetch_object(object1 TSRMLS_CC);
1227    sxe2 = php_sxe_fetch_object(object2 TSRMLS_CC);
1228
1229    if (sxe1->node == NULL) {
1230        if (sxe2->node) {
1231            return 1;
1232        } else if (sxe1->document->ptr == sxe2->document->ptr) {
1233            return 0;
1234        }
1235    } else {
1236        return !(sxe1->node == sxe2->node);
1237    }
1238    return 1;
1239}
1240/* }}} */
1241
1242/* {{{ proto array SimpleXMLElement::xpath(string path)
1243   Runs XPath query on the XML data */
1244SXE_METHOD(xpath)
1245{
1246    php_sxe_object    *sxe;
1247    zval              *value;
1248    char              *query;
1249    int                query_len;
1250    int                i;
1251    int                nsnbr = 0;
1252    xmlNsPtr          *ns = NULL;
1253    xmlXPathObjectPtr  retval;
1254    xmlNodeSetPtr      result;
1255    xmlNodePtr         nodeptr;
1256
1257    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &query, &query_len) == FAILURE) {
1258        return;
1259    }
1260
1261    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1262
1263    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1264        return; /* attributes don't have attributes */
1265    }
1266
1267    if (!sxe->xpath) {
1268        sxe->xpath = xmlXPathNewContext((xmlDocPtr) sxe->document->ptr);
1269    }
1270    if (!sxe->node) {
1271        php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement((xmlDocPtr) sxe->document->ptr), NULL TSRMLS_CC);
1272    }
1273
1274    nodeptr = php_sxe_get_first_node(sxe, sxe->node->node TSRMLS_CC);
1275
1276    sxe->xpath->node = nodeptr;
1277
1278    ns = xmlGetNsList((xmlDocPtr) sxe->document->ptr, nodeptr);
1279    if (ns != NULL) {
1280        while (ns[nsnbr] != NULL) {
1281            nsnbr++;
1282        }
1283    }
1284
1285    sxe->xpath->namespaces = ns;
1286    sxe->xpath->nsNr = nsnbr;
1287
1288    retval = xmlXPathEval((xmlChar *)query, sxe->xpath);
1289    if (ns != NULL) {
1290        xmlFree(ns);
1291        sxe->xpath->namespaces = NULL;
1292        sxe->xpath->nsNr = 0;
1293    }
1294
1295    if (!retval) {
1296        RETURN_FALSE;
1297    }
1298
1299    result = retval->nodesetval;
1300
1301    array_init(return_value);
1302
1303    if (result != NULL) {
1304        for (i = 0; i < result->nodeNr; ++i) {
1305            nodeptr = result->nodeTab[i];
1306            if (nodeptr->type == XML_TEXT_NODE || nodeptr->type == XML_ELEMENT_NODE || nodeptr->type == XML_ATTRIBUTE_NODE) {
1307                MAKE_STD_ZVAL(value);
1308                /**
1309                 * Detect the case where the last selector is text(), simplexml
1310                 * always accesses the text() child by default, therefore we assign
1311                 * to the parent node.
1312                 */
1313                if (nodeptr->type == XML_TEXT_NODE) {
1314                    _node_as_zval(sxe, nodeptr->parent, value, SXE_ITER_NONE, NULL, NULL, 0 TSRMLS_CC);
1315                } else if (nodeptr->type == XML_ATTRIBUTE_NODE) {
1316                    _node_as_zval(sxe, nodeptr->parent, value, SXE_ITER_ATTRLIST, (char*)nodeptr->name, nodeptr->ns ? (xmlChar *)nodeptr->ns->href : NULL, 0 TSRMLS_CC);
1317                } else {
1318                    _node_as_zval(sxe, nodeptr, value, SXE_ITER_NONE, NULL, NULL, 0 TSRMLS_CC);
1319                }
1320
1321                add_next_index_zval(return_value, value);
1322            }
1323        }
1324    }
1325
1326    xmlXPathFreeObject(retval);
1327}
1328/* }}} */
1329
1330/* {{{ proto bool SimpleXMLElement::registerXPathNamespace(string prefix, string ns)
1331   Creates a prefix/ns context for the next XPath query */
1332SXE_METHOD(registerXPathNamespace)
1333{
1334    php_sxe_object    *sxe;
1335    int prefix_len, ns_uri_len;
1336    char *prefix, *ns_uri;
1337
1338    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) {
1339        return;
1340    }
1341
1342    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1343    if (!sxe->xpath) {
1344        sxe->xpath = xmlXPathNewContext((xmlDocPtr) sxe->document->ptr);
1345    }
1346
1347    if (xmlXPathRegisterNs(sxe->xpath, (xmlChar *)prefix, (xmlChar *)ns_uri) != 0) {
1348        RETURN_FALSE
1349    }
1350    RETURN_TRUE;
1351}
1352
1353/* }}} */
1354
1355/* {{{ proto string SimpleXMLElement::asXML([string filename])
1356   Return a well-formed XML string based on SimpleXML element */
1357SXE_METHOD(asXML)
1358{
1359    php_sxe_object     *sxe;
1360    xmlNodePtr          node;
1361    xmlOutputBufferPtr  outbuf;
1362    xmlChar            *strval;
1363    int                 strval_len;
1364    char               *filename;
1365    int                 filename_len;
1366
1367    if (ZEND_NUM_ARGS() > 1) {
1368        RETURN_FALSE;
1369    }
1370
1371    if (ZEND_NUM_ARGS() == 1) {
1372        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
1373            RETURN_FALSE;
1374        }
1375
1376        sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1377        GET_NODE(sxe, node);
1378        node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1379
1380        if (node) {
1381            if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
1382                int bytes;
1383                bytes = xmlSaveFile(filename, (xmlDocPtr) sxe->document->ptr);
1384                if (bytes == -1) {
1385                    RETURN_FALSE;
1386                } else {
1387                    RETURN_TRUE;
1388                }
1389            } else {
1390                outbuf = xmlOutputBufferCreateFilename(filename, NULL, 0);
1391
1392                if (outbuf == NULL) {
1393                    RETURN_FALSE;
1394                }
1395
1396                xmlNodeDumpOutput(outbuf, (xmlDocPtr) sxe->document->ptr, node, 0, 0, NULL);
1397                xmlOutputBufferClose(outbuf);
1398                RETURN_TRUE;
1399            }
1400        } else {
1401            RETURN_FALSE;
1402        }
1403    }
1404
1405    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1406    GET_NODE(sxe, node);
1407    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1408
1409    if (node) {
1410        if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
1411            xmlDocDumpMemoryEnc((xmlDocPtr) sxe->document->ptr, &strval, &strval_len, ((xmlDocPtr) sxe->document->ptr)->encoding);
1412            RETVAL_STRINGL((char *)strval, strval_len, 1);
1413            xmlFree(strval);
1414        } else {
1415            /* Should we be passing encoding information instead of NULL? */
1416            outbuf = xmlAllocOutputBuffer(NULL);
1417
1418            if (outbuf == NULL) {
1419                RETURN_FALSE;
1420            }
1421
1422            xmlNodeDumpOutput(outbuf, (xmlDocPtr) sxe->document->ptr, node, 0, 0, ((xmlDocPtr) sxe->document->ptr)->encoding);
1423            xmlOutputBufferFlush(outbuf);
1424#ifdef LIBXML2_NEW_BUFFER
1425            RETVAL_STRINGL((char *)xmlOutputBufferGetContent(outbuf), xmlOutputBufferGetSize(outbuf), 1);
1426#else
1427            RETVAL_STRINGL((char *)outbuf->buffer->content, outbuf->buffer->use, 1);
1428#endif
1429            xmlOutputBufferClose(outbuf);
1430        }
1431    } else {
1432        RETVAL_FALSE;
1433    }
1434}
1435/* }}} */
1436
1437#define SXE_NS_PREFIX(ns) (ns->prefix ? (char*)ns->prefix : "")
1438
1439static inline void sxe_add_namespace_name(zval *return_value, xmlNsPtr ns) /* {{{ */
1440{
1441    char *prefix = SXE_NS_PREFIX(ns);
1442    if (zend_hash_exists(Z_ARRVAL_P(return_value), prefix, strlen(prefix) + 1) == 0) {
1443        add_assoc_string(return_value, prefix, (char*)ns->href, 1);
1444    }
1445}
1446/* }}} */
1447
1448static void sxe_add_namespaces(php_sxe_object *sxe, xmlNodePtr node, zend_bool recursive, zval *return_value TSRMLS_DC) /* {{{ */
1449{
1450    xmlAttrPtr  attr;
1451
1452    if (node->ns) {
1453        sxe_add_namespace_name(return_value, node->ns);
1454    }
1455
1456    attr = node->properties;
1457    while (attr) {
1458        if (attr->ns) {
1459            sxe_add_namespace_name(return_value, attr->ns);
1460        }
1461        attr = attr->next;
1462    }
1463
1464    if (recursive) {
1465        node = node->children;
1466        while (node) {
1467            if (node->type == XML_ELEMENT_NODE) {
1468                sxe_add_namespaces(sxe, node, recursive, return_value TSRMLS_CC);
1469            }
1470            node = node->next;
1471        }
1472    }
1473} /* }}} */
1474
1475/* {{{ proto string SimpleXMLElement::getNamespaces([bool recursve])
1476   Return all namespaces in use */
1477SXE_METHOD(getNamespaces)
1478{
1479    zend_bool           recursive = 0;
1480    php_sxe_object     *sxe;
1481    xmlNodePtr          node;
1482
1483    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &recursive) == FAILURE) {
1484        return;
1485    }
1486
1487    array_init(return_value);
1488
1489    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1490    GET_NODE(sxe, node);
1491    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1492
1493    if (node) {
1494        if (node->type == XML_ELEMENT_NODE) {
1495            sxe_add_namespaces(sxe, node, recursive, return_value TSRMLS_CC);
1496        } else if (node->type == XML_ATTRIBUTE_NODE && node->ns) {
1497            sxe_add_namespace_name(return_value, node->ns);
1498        }
1499    }
1500}
1501/* }}} */
1502
1503static void sxe_add_registered_namespaces(php_sxe_object *sxe, xmlNodePtr node, zend_bool recursive, zval *return_value TSRMLS_DC) /* {{{ */
1504{
1505    xmlNsPtr ns;
1506
1507    if (node->type == XML_ELEMENT_NODE) {
1508        ns = node->nsDef;
1509        while (ns != NULL) {
1510            sxe_add_namespace_name(return_value, ns);
1511            ns = ns->next;
1512        }
1513        if (recursive) {
1514            node = node->children;
1515            while (node) {
1516                sxe_add_registered_namespaces(sxe, node, recursive, return_value TSRMLS_CC);
1517                node = node->next;
1518            }
1519        }
1520    }
1521}
1522/* }}} */
1523
1524/* {{{ proto string SimpleXMLElement::getDocNamespaces([bool recursive [, bool from_root])
1525   Return all namespaces registered with document */
1526SXE_METHOD(getDocNamespaces)
1527{
1528    zend_bool           recursive = 0, from_root = 1;
1529    php_sxe_object     *sxe;
1530    xmlNodePtr          node;
1531
1532    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &recursive, &from_root) == FAILURE) {
1533        return;
1534    }
1535
1536    array_init(return_value);
1537
1538    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1539    if(from_root){
1540        node = xmlDocGetRootElement((xmlDocPtr)sxe->document->ptr);
1541    }else{
1542        GET_NODE(sxe, node);
1543    }
1544
1545    sxe_add_registered_namespaces(sxe, node, recursive, return_value TSRMLS_CC);
1546}
1547/* }}} */
1548
1549/* {{{ proto object SimpleXMLElement::children([string ns [, bool is_prefix]])
1550   Finds children of given node */
1551SXE_METHOD(children)
1552{
1553    php_sxe_object *sxe;
1554    char           *nsprefix = NULL;
1555    int             nsprefix_len = 0;
1556    xmlNodePtr      node;
1557    zend_bool       isprefix = 0;
1558
1559    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &nsprefix, &nsprefix_len, &isprefix) == FAILURE) {
1560        return;
1561    }
1562
1563    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1564
1565    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1566        return; /* attributes don't have attributes */
1567    }
1568
1569    GET_NODE(sxe, node);
1570    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1571
1572    _node_as_zval(sxe, node, return_value, SXE_ITER_CHILD, NULL, (xmlChar *)nsprefix, isprefix TSRMLS_CC);
1573
1574}
1575/* }}} */
1576
1577/* {{{ proto object SimpleXMLElement::getName()
1578   Finds children of given node */
1579SXE_METHOD(getName)
1580{
1581    php_sxe_object *sxe;
1582    xmlNodePtr      node;
1583    int             namelen;
1584
1585    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1586
1587    GET_NODE(sxe, node);
1588    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1589    if (node) {
1590        namelen = xmlStrlen(node->name);
1591        RETURN_STRINGL((char*)node->name, namelen, 1);
1592    } else {
1593        RETURN_EMPTY_STRING();
1594    }
1595}
1596/* }}} */
1597
1598/* {{{ proto array SimpleXMLElement::attributes([string ns [, bool is_prefix]])
1599   Identifies an element's attributes */
1600SXE_METHOD(attributes)
1601{
1602    php_sxe_object *sxe;
1603    char           *nsprefix = NULL;
1604    int             nsprefix_len = 0;
1605    xmlNodePtr      node;
1606    zend_bool       isprefix = 0;
1607
1608    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!b", &nsprefix, &nsprefix_len, &isprefix) == FAILURE) {
1609        return;
1610    }
1611
1612    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1613    GET_NODE(sxe, node);
1614
1615    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1616        return; /* attributes don't have attributes */
1617    }
1618
1619    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1620
1621    _node_as_zval(sxe, node, return_value, SXE_ITER_ATTRLIST, NULL, (xmlChar *)nsprefix, isprefix TSRMLS_CC);
1622}
1623/* }}} */
1624
1625/* {{{ proto void SimpleXMLElement::addChild(string qName [, string value [, string ns]])
1626   Add Element with optional namespace information */
1627SXE_METHOD(addChild)
1628{
1629    php_sxe_object *sxe;
1630    char           *qname, *value = NULL, *nsuri = NULL;
1631    int             qname_len, value_len = 0, nsuri_len = 0;
1632    xmlNodePtr      node, newnode;
1633    xmlNsPtr        nsptr = NULL;
1634    xmlChar        *localname, *prefix = NULL;
1635
1636    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s!s!",
1637        &qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
1638        return;
1639    }
1640
1641    if (qname_len == 0) {
1642        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Element name is required");
1643        return;
1644    }
1645
1646    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1647    GET_NODE(sxe, node);
1648
1649    if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1650        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add element to attributes");
1651        return;
1652    }
1653
1654    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1655
1656    if (node == NULL) {
1657        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot add child. Parent is not a permanent member of the XML tree");
1658        return;
1659    }
1660
1661    localname = xmlSplitQName2((xmlChar *)qname, &prefix);
1662    if (localname == NULL) {
1663        localname = xmlStrdup((xmlChar *)qname);
1664    }
1665
1666    newnode = xmlNewChild(node, NULL, localname, (xmlChar *)value);
1667
1668    if (nsuri != NULL) {
1669        if (nsuri_len == 0) {
1670            newnode->ns = NULL;
1671            nsptr = xmlNewNs(newnode, (xmlChar *)nsuri, prefix);
1672        } else {
1673            nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar *)nsuri);
1674            if (nsptr == NULL) {
1675                nsptr = xmlNewNs(newnode, (xmlChar *)nsuri, prefix);
1676            }
1677            newnode->ns = nsptr;
1678        }
1679    }
1680
1681    _node_as_zval(sxe, newnode, return_value, SXE_ITER_NONE, (char *)localname, prefix, 0 TSRMLS_CC);
1682
1683    xmlFree(localname);
1684    if (prefix != NULL) {
1685        xmlFree(prefix);
1686    }
1687}
1688/* }}} */
1689
1690/* {{{ proto void SimpleXMLElement::addAttribute(string qName, string value [,string ns])
1691   Add Attribute with optional namespace information */
1692SXE_METHOD(addAttribute)
1693{
1694    php_sxe_object *sxe;
1695    char           *qname, *value = NULL, *nsuri = NULL;
1696    int             qname_len, value_len = 0, nsuri_len = 0;
1697    xmlNodePtr      node;
1698    xmlAttrPtr      attrp = NULL;
1699    xmlNsPtr        nsptr = NULL;
1700    xmlChar        *localname, *prefix = NULL;
1701
1702    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|s!",
1703        &qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
1704        return;
1705    }
1706
1707    if (qname_len == 0) {
1708        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attribute name is required");
1709        return;
1710    }
1711
1712    sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1713    GET_NODE(sxe, node);
1714
1715    node = php_sxe_get_first_node(sxe, node TSRMLS_CC);
1716
1717    if (node && node->type != XML_ELEMENT_NODE) {
1718        node = node->parent;
1719    }
1720
1721    if (node == NULL) {
1722        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to locate parent Element");
1723        return;
1724    }
1725
1726    localname = xmlSplitQName2((xmlChar *)qname, &prefix);
1727    if (localname == NULL) {
1728        if (nsuri_len > 0) {
1729            if (prefix != NULL) {
1730                xmlFree(prefix);
1731            }
1732            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attribute requires prefix for namespace");
1733            return;
1734        }
1735        localname = xmlStrdup((xmlChar *)qname);
1736    }
1737
1738    attrp = xmlHasNsProp(node, localname, (xmlChar *)nsuri);
1739    if (attrp != NULL && attrp->type != XML_ATTRIBUTE_DECL) {
1740        xmlFree(localname);
1741        if (prefix != NULL) {
1742            xmlFree(prefix);
1743        }
1744        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Attribute already exists");
1745        return;
1746    }
1747
1748    if (nsuri != NULL) {
1749        nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar *)nsuri);
1750        if (nsptr == NULL) {
1751            nsptr = xmlNewNs(node, (xmlChar *)nsuri, prefix);
1752        }
1753    }
1754
1755    attrp = xmlNewNsProp(node, nsptr, localname, (xmlChar *)value);
1756
1757    xmlFree(localname);
1758    if (prefix != NULL) {
1759        xmlFree(prefix);
1760    }
1761}
1762/* }}} */
1763
1764/* {{{ cast_object()
1765 */
1766static int cast_object(zval *object, int type, char *contents TSRMLS_DC)
1767{
1768    if (contents) {
1769        ZVAL_STRINGL(object, contents, strlen(contents), 1);
1770    } else {
1771        ZVAL_NULL(object);
1772    }
1773    Z_SET_REFCOUNT_P(object, 1);
1774    Z_UNSET_ISREF_P(object);
1775
1776    switch (type) {
1777        case IS_STRING:
1778            convert_to_string(object);
1779            break;
1780        case IS_BOOL:
1781            convert_to_boolean(object);
1782            break;
1783        case IS_LONG:
1784            convert_to_long(object);
1785            break;
1786        case IS_DOUBLE:
1787            convert_to_double(object);
1788            break;
1789        default:
1790            return FAILURE;
1791    }
1792    return SUCCESS;
1793}
1794/* }}} */
1795
1796/* {{{ sxe_object_cast()
1797 */
1798static int sxe_object_cast(zval *readobj, zval *writeobj, int type TSRMLS_DC)
1799{
1800    php_sxe_object *sxe;
1801    xmlChar           *contents = NULL;
1802    xmlNodePtr      node;
1803    int rv;
1804    HashTable      *prop_hash;
1805
1806    sxe = php_sxe_fetch_object(readobj TSRMLS_CC);
1807
1808    if (type == IS_BOOL) {
1809        node = php_sxe_get_first_node(sxe, NULL TSRMLS_CC);
1810        prop_hash = sxe_get_prop_hash(readobj, 1 TSRMLS_CC);
1811        INIT_PZVAL(writeobj);
1812        ZVAL_BOOL(writeobj, node != NULL || zend_hash_num_elements(prop_hash) > 0);
1813        zend_hash_destroy(prop_hash);
1814        efree(prop_hash);
1815        return SUCCESS;
1816    }
1817
1818    if (sxe->iter.type != SXE_ITER_NONE) {
1819        node = php_sxe_get_first_node(sxe, NULL TSRMLS_CC);
1820        if (node) {
1821            contents = xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, node->children, 1);
1822        }
1823    } else {
1824        if (!sxe->node) {
1825            if (sxe->document) {
1826                php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement((xmlDocPtr) sxe->document->ptr), NULL TSRMLS_CC);
1827            }
1828        }
1829
1830        if (sxe->node && sxe->node->node) {
1831            if (sxe->node->node->children) {
1832                contents = xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, sxe->node->node->children, 1);
1833            }
1834        }
1835    }
1836
1837    if (readobj == writeobj) {
1838        INIT_PZVAL(writeobj);
1839        zval_dtor(readobj);
1840    }
1841
1842    rv = cast_object(writeobj, type, (char *)contents TSRMLS_CC);
1843
1844    if (contents) {
1845        xmlFree(contents);
1846    }
1847    return rv;
1848}
1849/* }}} */
1850
1851/* {{{ proto object SimpleXMLElement::__toString() U
1852   Returns the string content */
1853SXE_METHOD(__toString)
1854{
1855    zval           *result;
1856
1857    ALLOC_INIT_ZVAL(result);
1858
1859    if (sxe_object_cast(getThis(), result, IS_STRING TSRMLS_CC) == SUCCESS) {
1860        RETURN_ZVAL(result, 1, 1);
1861    } else {
1862        zval_ptr_dtor(&result);
1863        RETURN_EMPTY_STRING();
1864    }
1865}
1866/* }}} */
1867
1868static int php_sxe_count_elements_helper(php_sxe_object *sxe, long *count TSRMLS_DC) /* {{{ */
1869{
1870    xmlNodePtr       node;
1871    zval            *data;
1872
1873    *count = 0;
1874
1875    data = sxe->iter.data;
1876    sxe->iter.data = NULL;
1877
1878    node = php_sxe_reset_iterator(sxe, 0 TSRMLS_CC);
1879
1880    while (node)
1881    {
1882        (*count)++;
1883        node = php_sxe_iterator_fetch(sxe, node->next, 0 TSRMLS_CC);
1884    }
1885
1886    if (sxe->iter.data) {
1887        zval_ptr_dtor(&sxe->iter.data);
1888    }
1889    sxe->iter.data = data;
1890
1891    return SUCCESS;
1892}
1893/* }}} */
1894
1895static int sxe_count_elements(zval *object, long *count TSRMLS_DC) /* {{{ */
1896{
1897    php_sxe_object  *intern;
1898    intern = php_sxe_fetch_object(object TSRMLS_CC);
1899    if (intern->fptr_count) {
1900        zval *rv;
1901        zend_call_method_with_0_params(&object, intern->zo.ce, &intern->fptr_count, "count", &rv);
1902        if (rv) {
1903            if (intern->tmp) {
1904                zval_ptr_dtor(&intern->tmp);
1905            }
1906            MAKE_STD_ZVAL(intern->tmp);
1907            ZVAL_ZVAL(intern->tmp, rv, 1, 1);
1908            convert_to_long(intern->tmp);
1909            *count = (long) Z_LVAL_P(intern->tmp);
1910            return SUCCESS;
1911        }
1912        return FAILURE;
1913    }
1914    return php_sxe_count_elements_helper(intern, count TSRMLS_CC);
1915}
1916/* }}} */
1917
1918/* {{{ proto int SimpleXMLElement::count()
1919 Get number of child elements */
1920SXE_METHOD(count)
1921{
1922    long count = 0;
1923    php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
1924
1925    if (zend_parse_parameters_none() == FAILURE) {
1926        return;
1927    }
1928
1929    php_sxe_count_elements_helper(sxe, &count TSRMLS_CC);
1930
1931    RETURN_LONG(count);
1932}
1933/* }}} */
1934
1935static zval *sxe_get_value(zval *z TSRMLS_DC) /* {{{ */
1936{
1937    zval *retval;
1938
1939    MAKE_STD_ZVAL(retval);
1940
1941    if (sxe_object_cast(z, retval, IS_STRING TSRMLS_CC)==FAILURE) {
1942        zend_error(E_ERROR, "Unable to cast node to string");
1943        /* FIXME: Should not be fatal */
1944    }
1945
1946    Z_SET_REFCOUNT_P(retval, 0);
1947    return retval;
1948}
1949/* }}} */
1950
1951static zend_object_handlers sxe_object_handlers = { /* {{{ */
1952    ZEND_OBJECTS_STORE_HANDLERS,
1953    sxe_property_read,
1954    sxe_property_write,
1955    sxe_dimension_read,
1956    sxe_dimension_write,
1957    sxe_property_get_adr,
1958    sxe_get_value,          /* get */
1959    NULL,
1960    sxe_property_exists,
1961    sxe_property_delete,
1962    sxe_dimension_exists,
1963    sxe_dimension_delete,
1964    sxe_get_properties,
1965    NULL, /* zend_get_std_object_handlers()->get_method,*/
1966    NULL, /* zend_get_std_object_handlers()->call_method,*/
1967    NULL, /* zend_get_std_object_handlers()->get_constructor, */
1968    NULL, /* zend_get_std_object_handlers()->get_class_entry,*/
1969    NULL, /* zend_get_std_object_handlers()->get_class_name,*/
1970    sxe_objects_compare,
1971    sxe_object_cast,
1972    sxe_count_elements,
1973    sxe_get_debug_info,
1974    NULL,
1975    sxe_get_gc
1976};
1977/* }}} */
1978
1979/* {{{ sxe_object_clone()
1980 */
1981static void
1982sxe_object_clone(void *object, void **clone_ptr TSRMLS_DC)
1983{
1984    php_sxe_object *sxe = (php_sxe_object *) object;
1985    php_sxe_object *clone;
1986    xmlNodePtr nodep = NULL;
1987    xmlDocPtr docp = NULL;
1988
1989    clone = php_sxe_object_new(sxe->zo.ce TSRMLS_CC);
1990    clone->document = sxe->document;
1991    if (clone->document) {
1992        clone->document->refcount++;
1993        docp = clone->document->ptr;
1994    }
1995
1996    clone->iter.isprefix = sxe->iter.isprefix;
1997    if (sxe->iter.name != NULL) {
1998        clone->iter.name = xmlStrdup((xmlChar *)sxe->iter.name);
1999    }
2000    if (sxe->iter.nsprefix != NULL) {
2001        clone->iter.nsprefix = xmlStrdup((xmlChar *)sxe->iter.nsprefix);
2002    }
2003    clone->iter.type = sxe->iter.type;
2004
2005    if (sxe->node) {
2006        nodep = xmlDocCopyNode(sxe->node->node, docp, 1);
2007    }
2008
2009    php_libxml_increment_node_ptr((php_libxml_node_object *)clone, nodep, NULL TSRMLS_CC);
2010
2011    *clone_ptr = (void *) clone;
2012}
2013/* }}} */
2014
2015/* {{{ sxe_object_dtor()
2016 */
2017static void sxe_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
2018{
2019    /* dtor required to cleanup iterator related data properly */
2020
2021    php_sxe_object *sxe;
2022
2023    sxe = (php_sxe_object *) object;
2024
2025    if (sxe->iter.data) {
2026        zval_ptr_dtor(&sxe->iter.data);
2027        sxe->iter.data = NULL;
2028    }
2029
2030    if (sxe->iter.name) {
2031        xmlFree(sxe->iter.name);
2032        sxe->iter.name = NULL;
2033    }
2034    if (sxe->iter.nsprefix) {
2035        xmlFree(sxe->iter.nsprefix);
2036        sxe->iter.nsprefix = NULL;
2037    }
2038    if (sxe->tmp) {
2039        zval_ptr_dtor(&sxe->tmp);
2040        sxe->tmp = NULL;
2041    }
2042}
2043/* }}} */
2044
2045/* {{{ sxe_object_free_storage()
2046 */
2047static void sxe_object_free_storage(void *object TSRMLS_DC)
2048{
2049    php_sxe_object *sxe;
2050
2051    sxe = (php_sxe_object *) object;
2052
2053#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 1 && PHP_RELEASE_VERSION > 2) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 1) || (PHP_MAJOR_VERSION > 5)
2054    zend_object_std_dtor(&sxe->zo TSRMLS_CC);
2055#else
2056    if (sxe->zo.guards) {
2057        zend_hash_destroy(sxe->zo.guards);
2058        FREE_HASHTABLE(sxe->zo.guards);
2059    }
2060
2061    if (sxe->zo.properties) {
2062        zend_hash_destroy(sxe->zo.properties);
2063        FREE_HASHTABLE(sxe->zo.properties);
2064    }
2065#endif
2066
2067    php_libxml_node_decrement_resource((php_libxml_node_object *)sxe TSRMLS_CC);
2068
2069    if (sxe->xpath) {
2070        xmlXPathFreeContext(sxe->xpath);
2071    }
2072
2073    if (sxe->properties) {
2074        zend_hash_destroy(sxe->properties);
2075        FREE_HASHTABLE(sxe->properties);
2076    }
2077
2078    efree(object);
2079}
2080/* }}} */
2081
2082/* {{{ php_sxe_object_new()
2083 */
2084static php_sxe_object* php_sxe_object_new(zend_class_entry *ce TSRMLS_DC)
2085{
2086    php_sxe_object *intern;
2087    zend_class_entry     *parent = ce;
2088    int inherited = 0;
2089
2090    intern = ecalloc(1, sizeof(php_sxe_object));
2091
2092    intern->iter.type = SXE_ITER_NONE;
2093    intern->iter.nsprefix = NULL;
2094    intern->iter.name = NULL;
2095    intern->fptr_count = NULL;
2096
2097#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION == 1 && PHP_RELEASE_VERSION > 2) || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 1) || (PHP_MAJOR_VERSION > 5)
2098    zend_object_std_init(&intern->zo, ce TSRMLS_CC);
2099#else
2100    ALLOC_HASHTABLE(intern->zo.properties);
2101    zend_hash_init(intern->zo.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
2102
2103    intern->zo.ce = ce;
2104    intern->zo.guards = NULL;
2105#endif
2106
2107    while (parent) {
2108        if (parent == sxe_class_entry) {
2109            break;
2110        }
2111
2112        parent = parent->parent;
2113        inherited = 1;
2114    }
2115
2116    if (inherited) {
2117        zend_hash_find(&ce->function_table, "count", sizeof("count"),(void **) &intern->fptr_count);
2118        if (intern->fptr_count->common.scope == parent) {
2119            intern->fptr_count = NULL;
2120        }
2121    }
2122
2123    return intern;
2124}
2125/* }}} */
2126
2127/* {{{ php_sxe_register_object
2128 */
2129static zend_object_value
2130php_sxe_register_object(php_sxe_object *intern TSRMLS_DC)
2131{
2132    zend_object_value rv;
2133
2134    rv.handle = zend_objects_store_put(intern, sxe_object_dtor, (zend_objects_free_object_storage_t)sxe_object_free_storage, sxe_object_clone TSRMLS_CC);
2135    rv.handlers = (zend_object_handlers *) &sxe_object_handlers;
2136
2137    return rv;
2138}
2139/* }}} */
2140
2141/* {{{ sxe_object_new()
2142 */
2143PHP_SXE_API zend_object_value
2144sxe_object_new(zend_class_entry *ce TSRMLS_DC)
2145{
2146    php_sxe_object    *intern;
2147
2148    intern = php_sxe_object_new(ce TSRMLS_CC);
2149    return php_sxe_register_object(intern TSRMLS_CC);
2150}
2151/* }}} */
2152
2153/* {{{ proto simplemxml_element simplexml_load_file(string filename [, string class_name [, int options [, string ns [, bool is_prefix]]]])
2154   Load a filename and return a simplexml_element object to allow for processing */
2155PHP_FUNCTION(simplexml_load_file)
2156{
2157    php_sxe_object *sxe;
2158    char           *filename;
2159    int             filename_len;
2160    xmlDocPtr       docp;
2161    char           *ns = NULL;
2162    int             ns_len = 0;
2163    long            options = 0;
2164    zend_class_entry *ce= sxe_class_entry;
2165    zend_bool       isprefix = 0;
2166
2167    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|C!lsb", &filename, &filename_len, &ce, &options, &ns, &ns_len, &isprefix) == FAILURE) {
2168        return;
2169    }
2170
2171    docp = xmlReadFile(filename, NULL, options);
2172
2173    if (! docp) {
2174        RETURN_FALSE;
2175    }
2176
2177    if (!ce) {
2178        ce = sxe_class_entry;
2179    }
2180    sxe = php_sxe_object_new(ce TSRMLS_CC);
2181    sxe->iter.nsprefix = ns_len ? xmlStrdup((xmlChar *)ns) : NULL;
2182    sxe->iter.isprefix = isprefix;
2183    php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp TSRMLS_CC);
2184    php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL TSRMLS_CC);
2185
2186    return_value->type = IS_OBJECT;
2187    return_value->value.obj = php_sxe_register_object(sxe TSRMLS_CC);
2188}
2189/* }}} */
2190
2191/* {{{ proto simplemxml_element simplexml_load_string(string data [, string class_name [, int options [, string ns [, bool is_prefix]]]])
2192   Load a string and return a simplexml_element object to allow for processing */
2193PHP_FUNCTION(simplexml_load_string)
2194{
2195    php_sxe_object *sxe;
2196    char           *data;
2197    int             data_len;
2198    xmlDocPtr       docp;
2199    char           *ns = NULL;
2200    int             ns_len = 0;
2201    long            options = 0;
2202    zend_class_entry *ce= sxe_class_entry;
2203    zend_bool       isprefix = 0;
2204
2205    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|C!lsb", &data, &data_len, &ce, &options, &ns, &ns_len, &isprefix) == FAILURE) {
2206        return;
2207    }
2208
2209    docp = xmlReadMemory(data, data_len, NULL, NULL, options);
2210
2211    if (! docp) {
2212        RETURN_FALSE;
2213    }
2214
2215    if (!ce) {
2216        ce = sxe_class_entry;
2217    }
2218    sxe = php_sxe_object_new(ce TSRMLS_CC);
2219    sxe->iter.nsprefix = ns_len ? xmlStrdup((xmlChar *)ns) : NULL;
2220    sxe->iter.isprefix = isprefix;
2221    php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp TSRMLS_CC);
2222    php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL TSRMLS_CC);
2223
2224    return_value->type = IS_OBJECT;
2225    return_value->value.obj = php_sxe_register_object(sxe TSRMLS_CC);
2226}
2227/* }}} */
2228
2229/* {{{ proto SimpleXMLElement::__construct(string data [, int options [, bool data_is_url [, string ns [, bool is_prefix]]]])
2230   SimpleXMLElement constructor */
2231SXE_METHOD(__construct)
2232{
2233    php_sxe_object *sxe = php_sxe_fetch_object(getThis() TSRMLS_CC);
2234    char           *data, *ns = NULL;
2235    int             data_len, ns_len = 0;
2236    xmlDocPtr       docp;
2237    long            options = 0;
2238    zend_bool       is_url = 0, isprefix = 0;
2239    zend_error_handling error_handling;
2240
2241    zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC);
2242    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lbsb", &data, &data_len, &options, &is_url, &ns, &ns_len, &isprefix) == FAILURE) {
2243        zend_restore_error_handling(&error_handling TSRMLS_CC);
2244        return;
2245    }
2246
2247    zend_restore_error_handling(&error_handling TSRMLS_CC);
2248
2249    docp = is_url ? xmlReadFile(data, NULL, options) : xmlReadMemory(data, data_len, NULL, NULL, options);
2250
2251    if (!docp) {
2252        ((php_libxml_node_object *)sxe)->document = NULL;
2253        zend_throw_exception(zend_exception_get_default(TSRMLS_C), "String could not be parsed as XML", 0 TSRMLS_CC);
2254        return;
2255    }
2256
2257    sxe->iter.nsprefix = ns_len ? xmlStrdup((xmlChar *)ns) : NULL;
2258    sxe->iter.isprefix = isprefix;
2259    php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp TSRMLS_CC);
2260    php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL TSRMLS_CC);
2261}
2262/* }}} */
2263
2264zend_object_iterator_funcs php_sxe_iterator_funcs = { /* {{{ */
2265    php_sxe_iterator_dtor,
2266    php_sxe_iterator_valid,
2267    php_sxe_iterator_current_data,
2268    php_sxe_iterator_current_key,
2269    php_sxe_iterator_move_forward,
2270    php_sxe_iterator_rewind,
2271};
2272/* }}} */
2273
2274static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data TSRMLS_DC) /* {{{ */
2275{
2276    xmlChar *prefix  = sxe->iter.nsprefix;
2277    int isprefix  = sxe->iter.isprefix;
2278    int test_elem = sxe->iter.type == SXE_ITER_ELEMENT  && sxe->iter.name;
2279    int test_attr = sxe->iter.type == SXE_ITER_ATTRLIST && sxe->iter.name;
2280
2281    while (node) {
2282        SKIP_TEXT(node);
2283        if (sxe->iter.type != SXE_ITER_ATTRLIST && node->type == XML_ELEMENT_NODE) {
2284            if ((!test_elem || !xmlStrcmp(node->name, sxe->iter.name)) && match_ns(sxe, node, prefix, isprefix)) {
2285                break;
2286            }
2287        } else if (node->type == XML_ATTRIBUTE_NODE) {
2288            if ((!test_attr || !xmlStrcmp(node->name, sxe->iter.name)) && match_ns(sxe, node, prefix, isprefix)) {
2289                break;
2290            }
2291        }
2292next_iter:
2293        node = node->next;
2294    }
2295
2296    if (node && use_data) {
2297        ALLOC_INIT_ZVAL(sxe->iter.data);
2298        _node_as_zval(sxe, node, sxe->iter.data, SXE_ITER_NONE, NULL, prefix, isprefix TSRMLS_CC);
2299    }
2300
2301    return node;
2302}
2303/* }}} */
2304
2305static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data TSRMLS_DC) /* {{{ */
2306{
2307    xmlNodePtr node;
2308
2309    if (sxe->iter.data) {
2310        zval_ptr_dtor(&sxe->iter.data);
2311        sxe->iter.data = NULL;
2312    }
2313
2314    GET_NODE(sxe, node)
2315
2316    if (node) {
2317        switch (sxe->iter.type) {
2318            case SXE_ITER_ELEMENT:
2319            case SXE_ITER_CHILD:
2320            case SXE_ITER_NONE:
2321                node = node->children;
2322                break;
2323            case SXE_ITER_ATTRLIST:
2324                node = (xmlNodePtr) node->properties;
2325        }
2326        return php_sxe_iterator_fetch(sxe, node, use_data TSRMLS_CC);
2327    }
2328    return NULL;
2329}
2330/* }}} */
2331
2332zend_object_iterator *php_sxe_get_iterator(zend_class_entry *ce, zval *object, int by_ref TSRMLS_DC) /* {{{ */
2333{
2334    php_sxe_iterator *iterator;
2335
2336    if (by_ref) {
2337        zend_error(E_ERROR, "An iterator cannot be used with foreach by reference");
2338    }
2339    iterator = emalloc(sizeof(php_sxe_iterator));
2340
2341    Z_ADDREF_P(object);
2342    iterator->intern.data = (void*)object;
2343    iterator->intern.funcs = &php_sxe_iterator_funcs;
2344    iterator->sxe = php_sxe_fetch_object(object TSRMLS_CC);
2345
2346    return (zend_object_iterator*)iterator;
2347}
2348/* }}} */
2349
2350static void php_sxe_iterator_dtor(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
2351{
2352    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2353
2354    /* cleanup handled in sxe_object_dtor as we dont always have an iterator wrapper */
2355    if (iterator->intern.data) {
2356        zval_ptr_dtor((zval**)&iterator->intern.data);
2357    }
2358
2359    efree(iterator);
2360}
2361/* }}} */
2362
2363static int php_sxe_iterator_valid(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
2364{
2365    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2366
2367    return iterator->sxe->iter.data ? SUCCESS : FAILURE;
2368}
2369/* }}} */
2370
2371static void php_sxe_iterator_current_data(zend_object_iterator *iter, zval ***data TSRMLS_DC) /* {{{ */
2372{
2373    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2374
2375    *data = &iterator->sxe->iter.data;
2376}
2377/* }}} */
2378
2379static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key TSRMLS_DC) /* {{{ */
2380{
2381    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2382    zval *curobj = iterator->sxe->iter.data;
2383    php_sxe_object *intern = (php_sxe_object *)zend_object_store_get_object(curobj TSRMLS_CC);
2384
2385    xmlNodePtr curnode = NULL;
2386    if (intern != NULL && intern->node != NULL) {
2387        curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node;
2388    }
2389
2390    if (curnode) {
2391        ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name), 1);
2392    } else {
2393        ZVAL_NULL(key);
2394    }
2395}
2396/* }}} */
2397
2398PHP_SXE_API void php_sxe_move_forward_iterator(php_sxe_object *sxe TSRMLS_DC) /* {{{ */
2399{
2400    xmlNodePtr      node = NULL;
2401    php_sxe_object  *intern;
2402
2403    if (sxe->iter.data) {
2404        intern = (php_sxe_object *)zend_object_store_get_object(sxe->iter.data TSRMLS_CC);
2405        GET_NODE(intern, node)
2406        zval_ptr_dtor(&sxe->iter.data);
2407        sxe->iter.data = NULL;
2408    }
2409
2410    if (node) {
2411        php_sxe_iterator_fetch(sxe, node->next, 1 TSRMLS_CC);
2412    }
2413}
2414/* }}} */
2415
2416static void php_sxe_iterator_move_forward(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
2417{
2418    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2419    php_sxe_move_forward_iterator(iterator->sxe TSRMLS_CC);
2420}
2421/* }}} */
2422
2423static void php_sxe_iterator_rewind(zend_object_iterator *iter TSRMLS_DC) /* {{{ */
2424{
2425    php_sxe_object  *sxe;
2426
2427    php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2428    sxe = iterator->sxe;
2429
2430    php_sxe_reset_iterator(sxe, 1 TSRMLS_CC);
2431}
2432/* }}} */
2433
2434void *simplexml_export_node(zval *object TSRMLS_DC) /* {{{ */
2435{
2436    php_sxe_object *sxe;
2437    xmlNodePtr node;
2438
2439    sxe = php_sxe_fetch_object(object TSRMLS_CC);
2440    GET_NODE(sxe, node);
2441    return php_sxe_get_first_node(sxe, node TSRMLS_CC);
2442}
2443/* }}} */
2444
2445/* {{{ proto simplemxml_element simplexml_import_dom(domNode node [, string class_name])
2446   Get a simplexml_element object from dom to allow for processing */
2447PHP_FUNCTION(simplexml_import_dom)
2448{
2449    php_sxe_object *sxe;
2450    zval *node;
2451    php_libxml_node_object *object;
2452    xmlNodePtr      nodep = NULL;
2453    zend_class_entry *ce= sxe_class_entry;
2454
2455    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|C!", &node, &ce) == FAILURE) {
2456        return;
2457    }
2458
2459    object = (php_libxml_node_object *)zend_object_store_get_object(node TSRMLS_CC);
2460
2461    nodep = php_libxml_import_node(node TSRMLS_CC);
2462
2463    if (nodep) {
2464        if (nodep->doc == NULL) {
2465            php_error_docref(NULL TSRMLS_CC, E_WARNING, "Imported Node must have associated Document");
2466            RETURN_NULL();
2467        }
2468        if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
2469            nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
2470        }
2471    }
2472
2473    if (nodep && nodep->type == XML_ELEMENT_NODE) {
2474        if (!ce) {
2475            ce = sxe_class_entry;
2476        }
2477        sxe = php_sxe_object_new(ce TSRMLS_CC);
2478        sxe->document = object->document;
2479        php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, nodep->doc TSRMLS_CC);
2480        php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, nodep, NULL TSRMLS_CC);
2481
2482        return_value->type = IS_OBJECT;
2483        return_value->value.obj = php_sxe_register_object(sxe TSRMLS_CC);
2484    } else {
2485        php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Nodetype to import");
2486        RETVAL_NULL();
2487    }
2488}
2489/* }}} */
2490
2491/* {{{ arginfo */
2492ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexml_load_file, 0, 0, 1)
2493    ZEND_ARG_INFO(0, filename)
2494    ZEND_ARG_INFO(0, class_name)
2495    ZEND_ARG_INFO(0, options)
2496    ZEND_ARG_INFO(0, ns)
2497    ZEND_ARG_INFO(0, is_prefix)
2498ZEND_END_ARG_INFO()
2499
2500ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexml_load_string, 0, 0, 1)
2501    ZEND_ARG_INFO(0, data)
2502    ZEND_ARG_INFO(0, class_name)
2503    ZEND_ARG_INFO(0, options)
2504    ZEND_ARG_INFO(0, ns)
2505    ZEND_ARG_INFO(0, is_prefix)
2506ZEND_END_ARG_INFO()
2507
2508ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexml_import_dom, 0, 0, 1)
2509    ZEND_ARG_INFO(0, node)
2510    ZEND_ARG_INFO(0, class_name)
2511ZEND_END_ARG_INFO()
2512
2513ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_xpath, 0, 0, 1)
2514    ZEND_ARG_INFO(0, path)
2515ZEND_END_ARG_INFO()
2516
2517ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_registerxpathnamespace, 0, 0, 2)
2518    ZEND_ARG_INFO(0, prefix)
2519    ZEND_ARG_INFO(0, ns)
2520ZEND_END_ARG_INFO()
2521
2522ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_asxml, 0, 0, 0)
2523    ZEND_ARG_INFO(0, filename)
2524ZEND_END_ARG_INFO()
2525
2526ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_getnamespaces, 0, 0, 0)
2527    ZEND_ARG_INFO(0, recursve)
2528ZEND_END_ARG_INFO()
2529
2530ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_getdocnamespaces, 0, 0, 0)
2531    ZEND_ARG_INFO(0, recursve)
2532    ZEND_ARG_INFO(0, from_root)
2533ZEND_END_ARG_INFO()
2534
2535ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_children, 0, 0, 0)
2536    ZEND_ARG_INFO(0, ns)
2537    ZEND_ARG_INFO(0, is_prefix)
2538ZEND_END_ARG_INFO()
2539
2540ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement__construct, 0, 0, 1)
2541    ZEND_ARG_INFO(0, data)
2542    ZEND_ARG_INFO(0, options)
2543    ZEND_ARG_INFO(0, data_is_url)
2544    ZEND_ARG_INFO(0, ns)
2545    ZEND_ARG_INFO(0, is_prefix)
2546ZEND_END_ARG_INFO()
2547
2548ZEND_BEGIN_ARG_INFO(arginfo_simplexmlelement__void, 0)
2549ZEND_END_ARG_INFO()
2550
2551ZEND_BEGIN_ARG_INFO_EX(arginfo_simplexmlelement_addchild, 0, 0, 1)
2552    ZEND_ARG_INFO(0, name)
2553    ZEND_ARG_INFO(0, value)
2554    ZEND_ARG_INFO(0, ns)
2555ZEND_END_ARG_INFO()
2556/* }}} */
2557
2558const zend_function_entry simplexml_functions[] = { /* {{{ */
2559    PHP_FE(simplexml_load_file,     arginfo_simplexml_load_file)
2560    PHP_FE(simplexml_load_string,   arginfo_simplexml_load_string)
2561    PHP_FE(simplexml_import_dom,    arginfo_simplexml_import_dom)
2562    PHP_FE_END
2563};
2564/* }}} */
2565
2566static const zend_module_dep simplexml_deps[] = { /* {{{ */
2567    ZEND_MOD_REQUIRED("libxml")
2568    ZEND_MOD_REQUIRED("spl")
2569    ZEND_MOD_END
2570};
2571/* }}} */
2572
2573zend_module_entry simplexml_module_entry = { /* {{{ */
2574    STANDARD_MODULE_HEADER_EX, NULL,
2575    simplexml_deps,
2576    "SimpleXML",
2577    simplexml_functions,
2578    PHP_MINIT(simplexml),
2579    PHP_MSHUTDOWN(simplexml),
2580    NULL,
2581    NULL,
2582    PHP_MINFO(simplexml),
2583    "0.1",
2584    STANDARD_MODULE_PROPERTIES
2585};
2586/* }}} */
2587
2588#ifdef COMPILE_DL_SIMPLEXML
2589ZEND_GET_MODULE(simplexml)
2590#endif
2591
2592/* the method table */
2593/* each method can have its own parameters and visibility */
2594static const zend_function_entry sxe_functions[] = { /* {{{ */
2595    SXE_ME(__construct,            arginfo_simplexmlelement__construct, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) /* must be called */
2596    SXE_ME(asXML,                  arginfo_simplexmlelement_asxml, ZEND_ACC_PUBLIC)
2597    SXE_MALIAS(saveXML, asXML,     arginfo_simplexmlelement_asxml, ZEND_ACC_PUBLIC)
2598    SXE_ME(xpath,                  arginfo_simplexmlelement_xpath, ZEND_ACC_PUBLIC)
2599    SXE_ME(registerXPathNamespace, arginfo_simplexmlelement_registerxpathnamespace, ZEND_ACC_PUBLIC)
2600    SXE_ME(attributes,             arginfo_simplexmlelement_children, ZEND_ACC_PUBLIC)
2601    SXE_ME(children,               arginfo_simplexmlelement_children, ZEND_ACC_PUBLIC)
2602    SXE_ME(getNamespaces,          arginfo_simplexmlelement_getnamespaces, ZEND_ACC_PUBLIC)
2603    SXE_ME(getDocNamespaces,       arginfo_simplexmlelement_getdocnamespaces, ZEND_ACC_PUBLIC)
2604    SXE_ME(getName,                arginfo_simplexmlelement__void, ZEND_ACC_PUBLIC)
2605    SXE_ME(addChild,               arginfo_simplexmlelement_addchild, ZEND_ACC_PUBLIC)
2606    SXE_ME(addAttribute,           arginfo_simplexmlelement_addchild, ZEND_ACC_PUBLIC)
2607    SXE_ME(__toString,             arginfo_simplexmlelement__void, ZEND_ACC_PUBLIC)
2608    SXE_ME(count,                  arginfo_simplexmlelement__void, ZEND_ACC_PUBLIC)
2609    PHP_FE_END
2610};
2611/* }}} */
2612
2613/* {{{ PHP_MINIT_FUNCTION(simplexml)
2614 */
2615PHP_MINIT_FUNCTION(simplexml)
2616{
2617    zend_class_entry sxe;
2618
2619    INIT_CLASS_ENTRY(sxe, "SimpleXMLElement", sxe_functions);
2620    sxe.create_object = sxe_object_new;
2621    sxe_class_entry = zend_register_internal_class(&sxe TSRMLS_CC);
2622    sxe_class_entry->get_iterator = php_sxe_get_iterator;
2623    sxe_class_entry->iterator_funcs.funcs = &php_sxe_iterator_funcs;
2624    zend_class_implements(sxe_class_entry TSRMLS_CC, 1, zend_ce_traversable);
2625    sxe_object_handlers.get_method = zend_get_std_object_handlers()->get_method;
2626    sxe_object_handlers.get_constructor = zend_get_std_object_handlers()->get_constructor;
2627    sxe_object_handlers.get_class_entry = zend_get_std_object_handlers()->get_class_entry;
2628    sxe_object_handlers.get_class_name = zend_get_std_object_handlers()->get_class_name;
2629    sxe_class_entry->serialize = zend_class_serialize_deny;
2630    sxe_class_entry->unserialize = zend_class_unserialize_deny;
2631
2632    php_libxml_register_export(sxe_class_entry, simplexml_export_node);
2633
2634    PHP_MINIT(sxe)(INIT_FUNC_ARGS_PASSTHRU);
2635
2636    return SUCCESS;
2637}
2638/* }}} */
2639
2640/* {{{ PHP_MSHUTDOWN_FUNCTION(simplexml)
2641 */
2642PHP_MSHUTDOWN_FUNCTION(simplexml)
2643{
2644    sxe_class_entry = NULL;
2645    return SUCCESS;
2646}
2647/* }}} */
2648
2649/* {{{ PHP_MINFO_FUNCTION(simplexml)
2650 */
2651PHP_MINFO_FUNCTION(simplexml)
2652{
2653    php_info_print_table_start();
2654    php_info_print_table_header(2, "Simplexml support", "enabled");
2655    php_info_print_table_row(2, "Revision", "$Id: f84e1e014d888b0412910755edb035e56483fcf8 $");
2656    php_info_print_table_row(2, "Schema support",
2657#ifdef LIBXML_SCHEMAS_ENABLED
2658        "enabled");
2659#else
2660        "not available");
2661#endif
2662    php_info_print_table_end();
2663}
2664/* }}} */
2665
2666#endif
2667
2668/**
2669 * Local Variables:
2670 * c-basic-offset: 4
2671 * tab-width: 4
2672 * indent-tabs-mode: t
2673 * End:
2674 * vim600: fdm=marker
2675 * vim: noet sw=4 ts=4
2676 */
2677