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