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