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