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