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