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