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