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