xref: /PHP-8.2/ext/simplexml/simplexml.c (revision b5834c12)
1 /*
2   +----------------------------------------------------------------------+
3   | Copyright (c) The PHP Group                                          |
4   +----------------------------------------------------------------------+
5   | This source file is subject to version 3.01 of the PHP license,      |
6   | that is bundled with this package in the file LICENSE, and is        |
7   | available through the world-wide-web at the following url:           |
8   | https://www.php.net/license/3_01.txt                                 |
9   | If you did not receive a copy of the PHP license and are unable to   |
10   | obtain it through the world-wide-web, please send a note to          |
11   | license@php.net so we can mail you a copy immediately.               |
12   +----------------------------------------------------------------------+
13   | Authors: Sterling Hughes <sterling@php.net>                          |
14   |          Marcus Boerger <helly@php.net>                              |
15   |          Rob Richards <rrichards@php.net>                            |
16   +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #if defined(HAVE_LIBXML) && defined(HAVE_SIMPLEXML)
25 
26 #include "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 zend_result 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 	if (UNEXPECTED(!node)) {
640 		return &EG(error_zval);
641 	}
642 	name = ZSTR_VAL(zname);
643 	node = sxe_get_element_by_name(sxe, node, &name, &type);
644 	if (node) {
645 		return NULL;
646 	}
647 	ZVAL_STR(&member, zname);
648 	if (sxe_prop_dim_write(object, &member, NULL, 1, 0, &node) == &EG(error_zval)) {
649 		return &EG(error_zval);
650 	}
651 	type = SXE_ITER_NONE;
652 	name = NULL;
653 
654 	_node_as_zval(sxe, node, &ret, type, name, sxe->iter.nsprefix, sxe->iter.isprefix);
655 
656 	if (!Z_ISUNDEF(sxe->tmp)) {
657 		zval_ptr_dtor(&sxe->tmp);
658 	}
659 
660 	ZVAL_COPY_VALUE(&sxe->tmp, &ret);
661 
662 	return &sxe->tmp;
663 }
664 /* }}} */
665 
666 /* {{{ sxe_prop_dim_exists() */
sxe_prop_dim_exists(zend_object * object,zval * member,int check_empty,bool elements,bool attribs)667 static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empty, bool elements, bool attribs)
668 {
669 	php_sxe_object *sxe;
670 	xmlNodePtr      node;
671 	xmlAttrPtr      attr = NULL;
672 	int				exists = 0;
673 	int             test = 0;
674 	zval            tmp_zv;
675 
676 	if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) {
677 		zend_string *str = zval_try_get_string_func(member);
678 		if (UNEXPECTED(!str)) {
679 			return 0;
680 		}
681 		ZVAL_STR(&tmp_zv, str);
682 		member = &tmp_zv;
683 	}
684 
685 	sxe = php_sxe_fetch_object(object);
686 
687 	GET_NODE(sxe, node);
688 
689 	if (Z_TYPE_P(member) == IS_LONG) {
690 		if (sxe->iter.type != SXE_ITER_ATTRLIST) {
691 			attribs = 0;
692 			elements = 1;
693 			if (sxe->iter.type == SXE_ITER_CHILD) {
694 				node = php_sxe_get_first_node(sxe, node);
695 			}
696 		}
697 	}
698 
699 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
700 		attribs = 1;
701 		elements = 0;
702 		node = php_sxe_get_first_node(sxe, node);
703 		attr = (xmlAttrPtr)node;
704 		test = sxe->iter.name != NULL;
705 	} else if (sxe->iter.type != SXE_ITER_CHILD) {
706 		node = php_sxe_get_first_node(sxe, node);
707 		attr = node ? node->properties : NULL;
708 		test = 0;
709 	}
710 
711 	if (node) {
712 		if (attribs) {
713 			if (Z_TYPE_P(member) == IS_LONG) {
714 				int	nodendx = 0;
715 
716 				while (attr && nodendx <= Z_LVAL_P(member)) {
717 					if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
718 						if (nodendx == Z_LVAL_P(member)) {
719 							exists = 1;
720 							break;
721 						}
722 						nodendx++;
723 					}
724 					attr = attr->next;
725 				}
726 			} else {
727 				while (attr) {
728 					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)) {
729 						exists = 1;
730 						break;
731 					}
732 
733 					attr = attr->next;
734 				}
735 			}
736 			if (exists && check_empty == 1 &&
737 				(!attr->children || !attr->children->content || !attr->children->content[0] || !xmlStrcmp(attr->children->content, (const xmlChar *) "0")) ) {
738 				/* Attribute with no content in it's text node */
739 				exists = 0;
740 			}
741 		}
742 
743 		if (elements) {
744 			if (Z_TYPE_P(member) == IS_LONG) {
745 				if (sxe->iter.type == SXE_ITER_CHILD) {
746 					node = php_sxe_get_first_node(sxe, node);
747 				}
748 				node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
749 			} else {
750 				node = sxe_find_element_by_name(sxe, node->children, (xmlChar *)Z_STRVAL_P(member));
751 			}
752 			if (node) {
753 				exists = 1;
754 				if (check_empty == 1 &&
755 					(!node->children || (node->children->type == XML_TEXT_NODE && !node->children->next &&
756 					 (!node->children->content || !node->children->content[0] || !xmlStrcmp(node->children->content, (const xmlChar *) "0")))) ) {
757 					exists = 0;
758 				}
759 			}
760 		}
761 	}
762 
763 	if (member == &tmp_zv) {
764 		zval_ptr_dtor_str(&tmp_zv);
765 	}
766 
767 	return exists;
768 }
769 /* }}} */
770 
771 /* {{{ sxe_property_exists() */
sxe_property_exists(zend_object * object,zend_string * name,int check_empty,void ** cache_slot)772 static int sxe_property_exists(zend_object *object, zend_string *name, int check_empty, void **cache_slot)
773 {
774 	zval member;
775 	ZVAL_STR(&member, name);
776 	return sxe_prop_dim_exists(object, &member, check_empty, 1, 0);
777 }
778 /* }}} */
779 
780 /* {{{ sxe_dimension_exists() */
sxe_dimension_exists(zend_object * object,zval * member,int check_empty)781 static int sxe_dimension_exists(zend_object *object, zval *member, int check_empty)
782 {
783 	return sxe_prop_dim_exists(object, member, check_empty, 0, 1);
784 }
785 /* }}} */
786 
787 /* {{{ sxe_prop_dim_delete() */
sxe_prop_dim_delete(zend_object * object,zval * member,bool elements,bool attribs)788 static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements, bool attribs)
789 {
790 	php_sxe_object *sxe;
791 	xmlNodePtr      node;
792 	xmlNodePtr      nnext;
793 	xmlAttrPtr      attr = NULL;
794 	xmlAttrPtr      anext;
795 	zval            tmp_zv;
796 	int             test = 0;
797 
798 	if (Z_TYPE_P(member) != IS_STRING && Z_TYPE_P(member) != IS_LONG) {
799 		zend_string *str = zval_try_get_string_func(member);
800 		if (UNEXPECTED(!str)) {
801 			return;
802 		}
803 		ZVAL_STR(&tmp_zv, str);
804 		member = &tmp_zv;
805 	}
806 
807 	sxe = php_sxe_fetch_object(object);
808 
809 	GET_NODE(sxe, node);
810 
811 	if (Z_TYPE_P(member) == IS_LONG) {
812 		if (sxe->iter.type != SXE_ITER_ATTRLIST) {
813 			attribs = 0;
814 			elements = 1;
815 			if (sxe->iter.type == SXE_ITER_CHILD) {
816 				node = php_sxe_get_first_node(sxe, node);
817 			}
818 		}
819 	}
820 
821 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
822 		attribs = 1;
823 		elements = 0;
824 		node = php_sxe_get_first_node(sxe, node);
825 		attr = (xmlAttrPtr)node;
826 		test = sxe->iter.name != NULL;
827 	} else if (sxe->iter.type != SXE_ITER_CHILD) {
828 		node = php_sxe_get_first_node(sxe, node);
829 		attr = node ? node->properties : NULL;
830 		test = 0;
831 	}
832 
833 	if (node) {
834 		if (attribs) {
835 			if (Z_TYPE_P(member) == IS_LONG) {
836 				int	nodendx = 0;
837 
838 				while (attr && nodendx <= Z_LVAL_P(member)) {
839 					if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr) attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
840 						if (nodendx == Z_LVAL_P(member)) {
841 							xmlUnlinkNode((xmlNodePtr) attr);
842 							php_libxml_node_free_resource((xmlNodePtr) attr);
843 							break;
844 						}
845 						nodendx++;
846 					}
847 					attr = attr->next;
848 				}
849 			} else {
850 				while (attr) {
851 					anext = attr->next;
852 					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)) {
853 						xmlUnlinkNode((xmlNodePtr) attr);
854 						php_libxml_node_free_resource((xmlNodePtr) attr);
855 						break;
856 					}
857 					attr = anext;
858 				}
859 			}
860 		}
861 
862 		if (elements) {
863 			if (Z_TYPE_P(member) == IS_LONG) {
864 				if (sxe->iter.type == SXE_ITER_CHILD) {
865 					node = php_sxe_get_first_node(sxe, node);
866 				}
867 				node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
868 				if (node) {
869 					xmlUnlinkNode(node);
870 					php_libxml_node_free_resource(node);
871 				}
872 			} else {
873 				node = node->children;
874 				while (node) {
875 					nnext = node->next;
876 
877 					SKIP_TEXT(node);
878 
879 					if (!xmlStrcmp(node->name, (xmlChar *)Z_STRVAL_P(member)) && match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
880 						xmlUnlinkNode(node);
881 						php_libxml_node_free_resource(node);
882 					}
883 
884 next_iter:
885 					node = nnext;
886 				}
887 			}
888 		}
889 	}
890 
891 	if (member == &tmp_zv) {
892 		zval_ptr_dtor_str(&tmp_zv);
893 	}
894 }
895 /* }}} */
896 
897 /* {{{ sxe_property_delete() */
sxe_property_delete(zend_object * object,zend_string * name,void ** cache_slot)898 static void sxe_property_delete(zend_object *object, zend_string *name, void **cache_slot)
899 {
900 	zval member;
901 	ZVAL_STR(&member, name);
902 	sxe_prop_dim_delete(object, &member, 1, 0);
903 }
904 /* }}} */
905 
906 /* {{{ sxe_dimension_unset() */
sxe_dimension_delete(zend_object * object,zval * offset)907 static void sxe_dimension_delete(zend_object *object, zval *offset)
908 {
909 	sxe_prop_dim_delete(object, offset, 0, 1);
910 }
911 /* }}} */
912 
sxe_xmlNodeListGetString(xmlDocPtr doc,xmlNodePtr list,int inLine)913 static inline zend_string *sxe_xmlNodeListGetString(xmlDocPtr doc, xmlNodePtr list, int inLine) /* {{{ */
914 {
915 	xmlChar *tmp = xmlNodeListGetString(doc, list, inLine);
916 	zend_string *res;
917 
918 	if (tmp) {
919 		res = zend_string_init((char*)tmp, strlen((char *)tmp), 0);
920 		xmlFree(tmp);
921 	} else {
922 		res = ZSTR_EMPTY_ALLOC();
923 	}
924 
925 	return res;
926 }
927 /* }}} */
928 
929 /* {{{ _get_base_node_value() */
_get_base_node_value(php_sxe_object * sxe_ref,xmlNodePtr node,zval * value,xmlChar * nsprefix,int isprefix)930 static void _get_base_node_value(php_sxe_object *sxe_ref, xmlNodePtr node, zval *value, xmlChar *nsprefix, int isprefix)
931 {
932 	php_sxe_object *subnode;
933 	xmlChar        *contents;
934 
935 	if (node->children && node->children->type == XML_TEXT_NODE && !xmlIsBlankNode(node->children)) {
936 		contents = xmlNodeListGetString(node->doc, node->children, 1);
937 		if (contents) {
938 			ZVAL_STRING(value, (char *)contents);
939 			xmlFree(contents);
940 		}
941 	} else {
942 		subnode = php_sxe_object_new(sxe_ref->zo.ce, sxe_ref->fptr_count);
943 		subnode->document = sxe_ref->document;
944 		subnode->document->refcount++;
945 		if (nsprefix && *nsprefix) {
946 			subnode->iter.nsprefix = (xmlChar*)estrdup((char *)nsprefix);
947 			subnode->iter.isprefix = isprefix;
948 		}
949 		php_libxml_increment_node_ptr((php_libxml_node_object *)subnode, node, NULL);
950 
951 		ZVAL_OBJ(value, &subnode->zo);
952 		/*zval_add_ref(value);*/
953 	}
954 }
955 /* }}} */
956 
sxe_properties_add(HashTable * rv,char * name,int namelen,zval * value)957 static void sxe_properties_add(HashTable *rv, char *name, int namelen, zval *value) /* {{{ */
958 {
959 	zend_string *key;
960 	zval  *data_ptr;
961 	zval  newptr;
962 
963 	key = zend_string_init(name, namelen, 0);
964 	if ((data_ptr = zend_hash_find(rv, key)) != NULL) {
965 		if (Z_TYPE_P(data_ptr) == IS_ARRAY) {
966 			zend_hash_next_index_insert_new(Z_ARRVAL_P(data_ptr), value);
967 		} else {
968 			array_init(&newptr);
969 			zend_hash_next_index_insert_new(Z_ARRVAL(newptr), data_ptr);
970 			zend_hash_next_index_insert_new(Z_ARRVAL(newptr), value);
971 			ZVAL_ARR(data_ptr, Z_ARR(newptr));
972 		}
973 	} else {
974 		zend_hash_add_new(rv, key, value);
975 	}
976 	zend_string_release_ex(key, 0);
977 }
978 /* }}} */
979 
sxe_prop_is_empty(zend_object * object)980 static int sxe_prop_is_empty(zend_object *object) /* {{{ */
981 {
982 	php_sxe_object  *sxe;
983 	xmlNodePtr       node;
984 	xmlAttrPtr       attr;
985 	zval             iter_data;
986 	int              test;
987 	int              is_empty;
988 	int              use_iter = 0;
989 
990 	sxe = php_sxe_fetch_object(object);
991 
992 	GET_NODE(sxe, node);
993 	if (!node) {
994 		return 1;
995 	}
996 
997 	if (sxe->iter.type == SXE_ITER_ELEMENT) {
998 		node = php_sxe_get_first_node(sxe, node);
999 	}
1000 	if (!node || node->type != XML_ENTITY_DECL) {
1001 		attr = node ? (xmlAttrPtr)node->properties : NULL;
1002 		test = sxe->iter.name && sxe->iter.type == SXE_ITER_ATTRLIST;
1003 		while (attr) {
1004 			if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
1005 				return 0;
1006 			}
1007 			attr = attr->next;
1008 		}
1009 	}
1010 
1011 	GET_NODE(sxe, node);
1012 	node = php_sxe_get_first_node(sxe, node);
1013 	is_empty = 1;
1014 	ZVAL_UNDEF(&iter_data);
1015 	if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
1016 		if (node->type == XML_ATTRIBUTE_NODE) {
1017 			return 0;
1018 		} else if (sxe->iter.type != SXE_ITER_CHILD) {
1019 			if (sxe->iter.type == SXE_ITER_NONE || !node->children || !node->parent || node->children->next || node->children->children || node->parent->children == node->parent->last) {
1020 				node = node->children;
1021 			} else {
1022 				ZVAL_COPY_VALUE(&iter_data, &sxe->iter.data);
1023 				ZVAL_UNDEF(&sxe->iter.data);
1024 				node = php_sxe_reset_iterator(sxe, 0);
1025 				use_iter = 1;
1026 			}
1027 		}
1028 
1029 		while (node) {
1030 			if (node->children != NULL || node->prev != NULL || node->next != NULL) {
1031 				SKIP_TEXT(node);
1032 			} else {
1033 				if (node->type == XML_TEXT_NODE) {
1034 					const xmlChar *cur = node->content;
1035 					if (*cur != 0) {
1036 						is_empty = 0;
1037 						break;
1038 					}
1039 					goto next_iter;
1040 				}
1041 			}
1042 
1043 			if (node->type == XML_ELEMENT_NODE && (! match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix))) {
1044 				goto next_iter;
1045 			}
1046 
1047 			if (!node->name) {
1048 				goto next_iter;
1049 			}
1050 
1051 			is_empty = 0;
1052 			break;
1053 next_iter:
1054 			if (use_iter) {
1055 				node = php_sxe_iterator_fetch(sxe, node->next, 0);
1056 			} else {
1057 				node = node->next;
1058 			}
1059 		}
1060 	}
1061 
1062 	if (use_iter) {
1063 		if (!Z_ISUNDEF(sxe->iter.data)) {
1064 			zval_ptr_dtor(&sxe->iter.data);
1065 		}
1066 		ZVAL_COPY_VALUE(&sxe->iter.data, &iter_data);
1067 	}
1068 
1069 	return is_empty;
1070 }
1071 /* }}} */
1072 
sxe_get_prop_hash(zend_object * object,int is_debug)1073 static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
1074 {
1075 	zval            value;
1076 	zval            zattr;
1077 	HashTable       *rv;
1078 	php_sxe_object  *sxe;
1079 	char            *name;
1080 	xmlNodePtr       node;
1081 	xmlAttrPtr       attr;
1082 	int              namelen;
1083 	int              test;
1084 	char 		 	 use_iter;
1085 	zval             iter_data;
1086 
1087 	use_iter = 0;
1088 
1089 	sxe = php_sxe_fetch_object(object);
1090 
1091 	if (is_debug) {
1092 		rv = zend_new_array(0);
1093 	} else if (sxe->properties) {
1094 		zend_hash_clean(sxe->properties);
1095 		rv = sxe->properties;
1096 	} else {
1097 		rv = zend_new_array(0);
1098 		sxe->properties = rv;
1099 	}
1100 
1101 	GET_NODE(sxe, node);
1102 	if (!node) {
1103 		return rv;
1104 	}
1105 	if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
1106 		if (sxe->iter.type == SXE_ITER_ELEMENT) {
1107 			node = php_sxe_get_first_node(sxe, node);
1108 		}
1109 		if (!node || node->type != XML_ENTITY_DECL) {
1110 			attr = node ? (xmlAttrPtr)node->properties : NULL;
1111 			ZVAL_UNDEF(&zattr);
1112 			test = sxe->iter.name && sxe->iter.type == SXE_ITER_ATTRLIST;
1113 			while (attr) {
1114 				if ((!test || !xmlStrcmp(attr->name, sxe->iter.name)) && match_ns(sxe, (xmlNodePtr)attr, sxe->iter.nsprefix, sxe->iter.isprefix)) {
1115 					ZVAL_STR(&value, sxe_xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, attr->children, 1));
1116 					namelen = xmlStrlen(attr->name);
1117 					if (Z_ISUNDEF(zattr)) {
1118 						array_init(&zattr);
1119 						sxe_properties_add(rv, "@attributes", sizeof("@attributes") - 1, &zattr);
1120 					}
1121 					add_assoc_zval_ex(&zattr, (char*)attr->name, namelen, &value);
1122 				}
1123 				attr = attr->next;
1124 			}
1125 		}
1126 	}
1127 
1128 	GET_NODE(sxe, node);
1129 	node = php_sxe_get_first_node(sxe, node);
1130 
1131 	if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
1132 		if (node->type == XML_ATTRIBUTE_NODE) {
1133 			ZVAL_STR(&value, sxe_xmlNodeListGetString(node->doc, node->children, 1));
1134 			zend_hash_next_index_insert(rv, &value);
1135 			node = NULL;
1136 		} else if (sxe->iter.type != SXE_ITER_CHILD) {
1137 
1138 			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 ) {
1139 				node = node->children;
1140 			} else {
1141 				ZVAL_COPY_VALUE(&iter_data, &sxe->iter.data);
1142 				ZVAL_UNDEF(&sxe->iter.data);
1143 
1144 				node = php_sxe_reset_iterator(sxe, 0);
1145 
1146 				use_iter = 1;
1147 			}
1148 		}
1149 
1150 		while (node) {
1151 			if (node->children != NULL || node->prev != NULL || node->next != NULL || xmlIsBlankNode(node)) {
1152 				SKIP_TEXT(node);
1153 			} else {
1154 				if (node->type == XML_TEXT_NODE) {
1155 					const xmlChar *cur = node->content;
1156 
1157 					if (*cur != 0) {
1158 						ZVAL_STR(&value, sxe_xmlNodeListGetString(node->doc, node, 1));
1159 						zend_hash_next_index_insert(rv, &value);
1160 					}
1161 					goto next_iter;
1162 				}
1163 			}
1164 
1165 			if (node->type == XML_ELEMENT_NODE && (! match_ns(sxe, node, sxe->iter.nsprefix, sxe->iter.isprefix))) {
1166 				goto next_iter;
1167 			}
1168 
1169 			name = (char *) node->name;
1170 			if (!name) {
1171 				goto next_iter;
1172 			} else {
1173 				namelen = xmlStrlen(node->name);
1174 			}
1175 
1176 			_get_base_node_value(sxe, node, &value, sxe->iter.nsprefix, sxe->iter.isprefix);
1177 
1178 			if ( use_iter ) {
1179 				zend_hash_next_index_insert(rv, &value);
1180 			} else {
1181 				sxe_properties_add(rv, name, namelen, &value);
1182 			}
1183 next_iter:
1184 			if (UNEXPECTED(node->type == XML_ENTITY_DECL)) {
1185 				/* Entity decls are linked together via the next pointer.
1186 				 * The only way to get to an entity decl is via an entity reference in the document.
1187 				 * If we then continue iterating, we'll end up in the DTD. Even worse, if the entities reference each other we'll infinite loop. */
1188 				break;
1189 			}
1190 			if (use_iter) {
1191 				node = php_sxe_iterator_fetch(sxe, node->next, 0);
1192 			} else {
1193 				node = node->next;
1194 			}
1195 		}
1196 	}
1197 
1198 	if (use_iter) {
1199 		if (!Z_ISUNDEF(sxe->iter.data)) {
1200 			zval_ptr_dtor(&sxe->iter.data);
1201 		}
1202 		ZVAL_COPY_VALUE(&sxe->iter.data, &iter_data);
1203 	}
1204 
1205 	return rv;
1206 }
1207 /* }}} */
1208 
sxe_get_gc(zend_object * object,zval ** table,int * n)1209 static HashTable *sxe_get_gc(zend_object *object, zval **table, int *n) /* {{{ */ {
1210 	php_sxe_object *sxe;
1211 	sxe = php_sxe_fetch_object(object);
1212 
1213 	*table = NULL;
1214 	*n = 0;
1215 	return sxe->properties;
1216 }
1217 /* }}} */
1218 
sxe_get_properties(zend_object * object)1219 static HashTable *sxe_get_properties(zend_object *object) /* {{{ */
1220 {
1221 	return sxe_get_prop_hash(object, 0);
1222 }
1223 /* }}} */
1224 
sxe_get_debug_info(zend_object * object,int * is_temp)1225 static HashTable * sxe_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
1226 {
1227 	*is_temp = 1;
1228 	return sxe_get_prop_hash(object, 1);
1229 }
1230 /* }}} */
1231 
sxe_objects_compare(zval * object1,zval * object2)1232 static int sxe_objects_compare(zval *object1, zval *object2) /* {{{ */
1233 {
1234 	php_sxe_object *sxe1;
1235 	php_sxe_object *sxe2;
1236 
1237 	ZEND_COMPARE_OBJECTS_FALLBACK(object1, object2);
1238 
1239 	sxe1 = Z_SXEOBJ_P(object1);
1240 	sxe2 = Z_SXEOBJ_P(object2);
1241 
1242 	if (sxe1->node != NULL && sxe2->node != NULL) {
1243 		/* Both nodes set: Only support equality comparison between nodes. */
1244 		if (sxe1->node == sxe2->node) {
1245 			return 0;
1246 		}
1247 		return ZEND_UNCOMPARABLE;
1248 	}
1249 
1250 	if (sxe1->node == NULL && sxe2->node == NULL) {
1251 		/* Both nodes not set: Only support equality comparison between documents. */
1252 		if (sxe1->document->ptr == sxe2->document->ptr) {
1253 			return 0;
1254 		}
1255 		return ZEND_UNCOMPARABLE;
1256 	}
1257 
1258 	/* Only one of the nodes set: Cannot compare. */
1259 	return ZEND_UNCOMPARABLE;
1260 }
1261 /* }}} */
1262 
1263 /* {{{ Runs XPath query on the XML data */
PHP_METHOD(SimpleXMLElement,xpath)1264 PHP_METHOD(SimpleXMLElement, xpath)
1265 {
1266 	php_sxe_object    *sxe;
1267 	zval               value;
1268 	char              *query;
1269 	size_t                query_len;
1270 	int                i;
1271 	int                nsnbr = 0;
1272 	xmlNsPtr          *ns = NULL;
1273 	xmlXPathObjectPtr  retval;
1274 	xmlNodeSetPtr      result;
1275 	xmlNodePtr		   nodeptr;
1276 
1277 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &query, &query_len) == FAILURE) {
1278 		RETURN_THROWS();
1279 	}
1280 
1281 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1282 
1283 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1284 		return; /* attributes don't have attributes */
1285 	}
1286 
1287 	GET_NODE(sxe, nodeptr);
1288 	nodeptr = php_sxe_get_first_node(sxe, nodeptr);
1289 	if (!nodeptr) {
1290 		return;
1291 	}
1292 
1293 	if (!sxe->xpath) {
1294 		sxe->xpath = xmlXPathNewContext((xmlDocPtr) sxe->document->ptr);
1295 	}
1296 	sxe->xpath->node = nodeptr;
1297 
1298 	ns = xmlGetNsList((xmlDocPtr) sxe->document->ptr, nodeptr);
1299 	if (ns != NULL) {
1300 		while (ns[nsnbr] != NULL) {
1301 			nsnbr++;
1302 		}
1303 	}
1304 
1305 	sxe->xpath->namespaces = ns;
1306 	sxe->xpath->nsNr = nsnbr;
1307 
1308 	retval = xmlXPathEval((xmlChar *)query, sxe->xpath);
1309 	if (ns != NULL) {
1310 		xmlFree(ns);
1311 		sxe->xpath->namespaces = NULL;
1312 		sxe->xpath->nsNr = 0;
1313 	}
1314 
1315 	if (!retval) {
1316 		RETURN_FALSE;
1317 	}
1318 
1319 	result = retval->nodesetval;
1320 
1321 	if (result != NULL) {
1322 		array_init(return_value);
1323 
1324 		for (i = 0; i < result->nodeNr; ++i) {
1325 			nodeptr = result->nodeTab[i];
1326 			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) {
1327 				/**
1328 				 * Detect the case where the last selector is text(), simplexml
1329 				 * always accesses the text() child by default, therefore we assign
1330 				 * to the parent node.
1331 				 */
1332 				if (nodeptr->type == XML_TEXT_NODE) {
1333 					_node_as_zval(sxe, nodeptr->parent, &value, SXE_ITER_NONE, NULL, NULL, 0);
1334 				} else if (nodeptr->type == XML_ATTRIBUTE_NODE) {
1335 					_node_as_zval(sxe, nodeptr->parent, &value, SXE_ITER_ATTRLIST, (char*)nodeptr->name, nodeptr->ns ? (xmlChar *)nodeptr->ns->href : NULL, 0);
1336 				} else {
1337 					_node_as_zval(sxe, nodeptr, &value, SXE_ITER_NONE, NULL, NULL, 0);
1338 				}
1339 
1340 				add_next_index_zval(return_value, &value);
1341 			}
1342 		}
1343 	} else {
1344 		RETVAL_EMPTY_ARRAY();
1345 	}
1346 
1347 	xmlXPathFreeObject(retval);
1348 }
1349 /* }}} */
1350 
1351 /* {{{ Creates a prefix/ns context for the next XPath query */
PHP_METHOD(SimpleXMLElement,registerXPathNamespace)1352 PHP_METHOD(SimpleXMLElement, registerXPathNamespace)
1353 {
1354 	php_sxe_object    *sxe;
1355 	size_t prefix_len, ns_uri_len;
1356 	char *prefix, *ns_uri;
1357 
1358 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &prefix, &prefix_len, &ns_uri, &ns_uri_len) == FAILURE) {
1359 		RETURN_THROWS();
1360 	}
1361 
1362 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1363 	if (!sxe->document) {
1364 		zend_throw_error(NULL, "SimpleXMLElement is not properly initialized");
1365 		RETURN_THROWS();
1366 	}
1367 
1368 	if (!sxe->xpath) {
1369 		sxe->xpath = xmlXPathNewContext((xmlDocPtr) sxe->document->ptr);
1370 	}
1371 
1372 	if (xmlXPathRegisterNs(sxe->xpath, (xmlChar *)prefix, (xmlChar *)ns_uri) != 0) {
1373 		RETURN_FALSE;
1374 	}
1375 	RETURN_TRUE;
1376 }
1377 
1378 /* }}} */
1379 
1380 /* {{{ Return a well-formed XML string based on SimpleXML element */
PHP_METHOD(SimpleXMLElement,asXML)1381 PHP_METHOD(SimpleXMLElement, asXML)
1382 {
1383 	php_sxe_object     *sxe;
1384 	xmlNodePtr          node;
1385 	xmlOutputBufferPtr  outbuf;
1386 	xmlChar            *strval;
1387 	int                 strval_len;
1388 	char               *filename = NULL;
1389 	size_t                 filename_len;
1390 
1391 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!", &filename, &filename_len) == FAILURE) {
1392 		RETURN_THROWS();
1393 	}
1394 
1395 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1396 	GET_NODE(sxe, node);
1397 	node = php_sxe_get_first_node(sxe, node);
1398 
1399 	if (!node) {
1400 		RETURN_FALSE;
1401 	}
1402 
1403 	if (filename) {
1404 		if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
1405 			int bytes;
1406 			bytes = xmlSaveFile(filename, (xmlDocPtr) sxe->document->ptr);
1407 			if (bytes == -1) {
1408 				RETURN_FALSE;
1409 			} else {
1410 				RETURN_TRUE;
1411 			}
1412 		} else {
1413 			outbuf = xmlOutputBufferCreateFilename(filename, NULL, 0);
1414 
1415 			if (outbuf == NULL) {
1416 				RETURN_FALSE;
1417 			}
1418 
1419 			xmlNodeDumpOutput(outbuf, (xmlDocPtr) sxe->document->ptr, node, 0, 0, NULL);
1420 			xmlOutputBufferClose(outbuf);
1421 			RETURN_TRUE;
1422 		}
1423 	}
1424 
1425 	if (node->parent && (XML_DOCUMENT_NODE == node->parent->type)) {
1426 		xmlDocDumpMemoryEnc((xmlDocPtr) sxe->document->ptr, &strval, &strval_len, (const char *) ((xmlDocPtr) sxe->document->ptr)->encoding);
1427 		if (!strval) {
1428 			RETVAL_FALSE;
1429 		} else {
1430 			RETVAL_STRINGL((char *)strval, strval_len);
1431 		}
1432 		xmlFree(strval);
1433 	} else {
1434 		char *return_content;
1435 		size_t return_len;
1436 		/* Should we be passing encoding information instead of NULL? */
1437 		outbuf = xmlAllocOutputBuffer(NULL);
1438 
1439 		if (outbuf == NULL) {
1440 			RETURN_FALSE;
1441 		}
1442 
1443 		xmlNodeDumpOutput(outbuf, (xmlDocPtr) sxe->document->ptr, node, 0, 0, (const char *) ((xmlDocPtr) sxe->document->ptr)->encoding);
1444 		xmlOutputBufferFlush(outbuf);
1445 #ifdef LIBXML2_NEW_BUFFER
1446 		return_content = (char *)xmlOutputBufferGetContent(outbuf);
1447 		return_len = xmlOutputBufferGetSize(outbuf);
1448 #else
1449 		return_content = (char *)outbuf->buffer->content;
1450 		return_len = outbuf->buffer->use;
1451 #endif
1452 		if (!return_content) {
1453 			RETVAL_FALSE;
1454 		} else {
1455 			RETVAL_STRINGL(return_content, return_len);
1456 		}
1457 		xmlOutputBufferClose(outbuf);
1458 	}
1459 }
1460 /* }}} */
1461 
1462 #define SXE_NS_PREFIX(ns) (ns->prefix ? (char*)ns->prefix : "")
1463 
sxe_add_namespace_name(zval * return_value,xmlNsPtr ns)1464 static inline void sxe_add_namespace_name(zval *return_value, xmlNsPtr ns) /* {{{ */
1465 {
1466 	char *prefix = SXE_NS_PREFIX(ns);
1467 	zend_string *key = zend_string_init(prefix, strlen(prefix), 0);
1468 	zval zv;
1469 
1470 	if (!zend_hash_exists(Z_ARRVAL_P(return_value), key)) {
1471 		ZVAL_STRING(&zv, (char*)ns->href);
1472 		zend_hash_add_new(Z_ARRVAL_P(return_value), key, &zv);
1473 	}
1474 	zend_string_release_ex(key, 0);
1475 }
1476 /* }}} */
1477 
sxe_add_namespaces(php_sxe_object * sxe,xmlNodePtr node,bool recursive,zval * return_value)1478 static void sxe_add_namespaces(php_sxe_object *sxe, xmlNodePtr node, bool recursive, zval *return_value) /* {{{ */
1479 {
1480 	xmlAttrPtr  attr;
1481 
1482 	if (node->ns) {
1483 		sxe_add_namespace_name(return_value, node->ns);
1484 	}
1485 
1486 	attr = node->properties;
1487 	while (attr) {
1488 		if (attr->ns) {
1489 			sxe_add_namespace_name(return_value, attr->ns);
1490 		}
1491 		attr = attr->next;
1492 	}
1493 
1494 	if (recursive) {
1495 		node = node->children;
1496 		while (node) {
1497 			if (node->type == XML_ELEMENT_NODE) {
1498 				sxe_add_namespaces(sxe, node, recursive, return_value);
1499 			}
1500 			node = node->next;
1501 		}
1502 	}
1503 } /* }}} */
1504 
sxe_object_free_iterxpath(php_sxe_object * sxe)1505 static inline void sxe_object_free_iterxpath(php_sxe_object *sxe)
1506 {
1507 	if (!Z_ISUNDEF(sxe->iter.data)) {
1508 		zval_ptr_dtor(&sxe->iter.data);
1509 		ZVAL_UNDEF(&sxe->iter.data);
1510 	}
1511 
1512 	if (sxe->iter.name) {
1513 		efree(sxe->iter.name);
1514 		sxe->iter.name = NULL;
1515 	}
1516 	if (sxe->iter.nsprefix) {
1517 		efree(sxe->iter.nsprefix);
1518 		sxe->iter.nsprefix = NULL;
1519 	}
1520 	if (!Z_ISUNDEF(sxe->tmp)) {
1521 		zval_ptr_dtor(&sxe->tmp);
1522 		ZVAL_UNDEF(&sxe->tmp);
1523 	}
1524 
1525 	php_libxml_node_decrement_resource((php_libxml_node_object *)sxe);
1526 
1527 	if (sxe->xpath) {
1528 		xmlXPathFreeContext(sxe->xpath);
1529 		sxe->xpath = NULL;
1530 	}
1531 }
1532 
1533 
1534 /* {{{ Return all namespaces in use */
PHP_METHOD(SimpleXMLElement,getNamespaces)1535 PHP_METHOD(SimpleXMLElement, getNamespaces)
1536 {
1537 	bool           recursive = 0;
1538 	php_sxe_object     *sxe;
1539 	xmlNodePtr          node;
1540 
1541 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &recursive) == FAILURE) {
1542 		RETURN_THROWS();
1543 	}
1544 
1545 	array_init(return_value);
1546 
1547 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1548 	GET_NODE(sxe, node);
1549 	node = php_sxe_get_first_node(sxe, node);
1550 
1551 	if (node) {
1552 		if (node->type == XML_ELEMENT_NODE) {
1553 			sxe_add_namespaces(sxe, node, recursive, return_value);
1554 		} else if (node->type == XML_ATTRIBUTE_NODE && node->ns) {
1555 			sxe_add_namespace_name(return_value, node->ns);
1556 		}
1557 	}
1558 }
1559 /* }}} */
1560 
sxe_add_registered_namespaces(php_sxe_object * sxe,xmlNodePtr node,bool recursive,zval * return_value)1561 static void sxe_add_registered_namespaces(php_sxe_object *sxe, xmlNodePtr node, bool recursive, zval *return_value) /* {{{ */
1562 {
1563 	xmlNsPtr ns;
1564 
1565 	if (node->type == XML_ELEMENT_NODE) {
1566 		ns = node->nsDef;
1567 		while (ns != NULL) {
1568 			sxe_add_namespace_name(return_value, ns);
1569 			ns = ns->next;
1570 		}
1571 		if (recursive) {
1572 			node = node->children;
1573 			while (node) {
1574 				sxe_add_registered_namespaces(sxe, node, recursive, return_value);
1575 				node = node->next;
1576 			}
1577 		}
1578 	}
1579 }
1580 /* }}} */
1581 
1582 /* {{{ Return all namespaces registered with document */
PHP_METHOD(SimpleXMLElement,getDocNamespaces)1583 PHP_METHOD(SimpleXMLElement, getDocNamespaces)
1584 {
1585 	bool           recursive = 0, from_root = 1;
1586 	php_sxe_object     *sxe;
1587 	xmlNodePtr          node;
1588 
1589 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|bb", &recursive, &from_root) == FAILURE) {
1590 		RETURN_THROWS();
1591 	}
1592 
1593 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1594 	if (from_root) {
1595 		if (!sxe->document) {
1596 			zend_throw_error(NULL, "SimpleXMLElement is not properly initialized");
1597 			RETURN_THROWS();
1598 		}
1599 
1600 		node = xmlDocGetRootElement((xmlDocPtr)sxe->document->ptr);
1601 	} else {
1602 		GET_NODE(sxe, node);
1603 	}
1604 
1605 	if (node == NULL) {
1606 		RETURN_FALSE;
1607 	}
1608 
1609 	array_init(return_value);
1610 	sxe_add_registered_namespaces(sxe, node, recursive, return_value);
1611 }
1612 /* }}} */
1613 
1614 /* {{{ Finds children of given node */
PHP_METHOD(SimpleXMLElement,children)1615 PHP_METHOD(SimpleXMLElement, children)
1616 {
1617 	php_sxe_object *sxe;
1618 	char           *nsprefix = NULL;
1619 	size_t             nsprefix_len = 0;
1620 	xmlNodePtr      node;
1621 	bool       isprefix = 0;
1622 
1623 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!b", &nsprefix, &nsprefix_len, &isprefix) == FAILURE) {
1624 		RETURN_THROWS();
1625 	}
1626 
1627 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1628 
1629 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1630 		return; /* attributes don't have attributes */
1631 	}
1632 
1633 	GET_NODE(sxe, node);
1634 	node = php_sxe_get_first_node(sxe, node);
1635 	if (!node) {
1636 		return;
1637 	}
1638 
1639 	_node_as_zval(sxe, node, return_value, SXE_ITER_CHILD, NULL, (xmlChar *)nsprefix, isprefix);
1640 
1641 }
1642 /* }}} */
1643 
1644 /* {{{ Finds children of given node */
PHP_METHOD(SimpleXMLElement,getName)1645 PHP_METHOD(SimpleXMLElement, getName)
1646 {
1647 	php_sxe_object *sxe;
1648 	xmlNodePtr      node;
1649 	int             namelen;
1650 
1651 	if (zend_parse_parameters_none() == FAILURE) {
1652 		RETURN_THROWS();
1653 	}
1654 
1655 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1656 
1657 	GET_NODE(sxe, node);
1658 	node = php_sxe_get_first_node(sxe, node);
1659 	if (node) {
1660 		namelen = xmlStrlen(node->name);
1661 		RETURN_STRINGL((char*)node->name, namelen);
1662 	} else {
1663 		RETURN_EMPTY_STRING();
1664 	}
1665 }
1666 /* }}} */
1667 
1668 /* {{{ Identifies an element's attributes */
PHP_METHOD(SimpleXMLElement,attributes)1669 PHP_METHOD(SimpleXMLElement, attributes)
1670 {
1671 	php_sxe_object *sxe;
1672 	char           *nsprefix = NULL;
1673 	size_t             nsprefix_len = 0;
1674 	xmlNodePtr      node;
1675 	bool       isprefix = 0;
1676 
1677 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!b", &nsprefix, &nsprefix_len, &isprefix) == FAILURE) {
1678 		RETURN_THROWS();
1679 	}
1680 
1681 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1682 	GET_NODE(sxe, node);
1683 	node = php_sxe_get_first_node(sxe, node);
1684 	if (!node) {
1685 		return;
1686 	}
1687 
1688 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1689 		return; /* attributes don't have attributes */
1690 	}
1691 
1692 	_node_as_zval(sxe, node, return_value, SXE_ITER_ATTRLIST, NULL, (xmlChar *)nsprefix, isprefix);
1693 }
1694 /* }}} */
1695 
1696 /* {{{ Add Element with optional namespace information */
PHP_METHOD(SimpleXMLElement,addChild)1697 PHP_METHOD(SimpleXMLElement, addChild)
1698 {
1699 	php_sxe_object *sxe;
1700 	char           *qname, *value = NULL, *nsuri = NULL;
1701 	size_t             qname_len, value_len = 0, nsuri_len = 0;
1702 	xmlNodePtr      node, newnode;
1703 	xmlNsPtr        nsptr = NULL;
1704 	xmlChar        *localname, *prefix = NULL;
1705 
1706 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s!",
1707 		&qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
1708 		RETURN_THROWS();
1709 	}
1710 
1711 	if (qname_len == 0) {
1712 		zend_argument_value_error(1, "cannot be empty");
1713 		RETURN_THROWS();
1714 	}
1715 
1716 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1717 	GET_NODE(sxe, node);
1718 
1719 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
1720 		php_error_docref(NULL, E_WARNING, "Cannot add element to attributes");
1721 		return;
1722 	}
1723 
1724 	node = php_sxe_get_first_node(sxe, node);
1725 
1726 	if (node == NULL) {
1727 		php_error_docref(NULL, E_WARNING, "Cannot add child. Parent is not a permanent member of the XML tree");
1728 		return;
1729 	}
1730 
1731 	localname = xmlSplitQName2((xmlChar *)qname, &prefix);
1732 	if (localname == NULL) {
1733 		localname = xmlStrdup((xmlChar *)qname);
1734 	}
1735 
1736 	newnode = xmlNewChild(node, NULL, localname, (xmlChar *)value);
1737 
1738 	if (nsuri != NULL) {
1739 		if (nsuri_len == 0) {
1740 			newnode->ns = NULL;
1741 			nsptr = xmlNewNs(newnode, (xmlChar *)nsuri, prefix);
1742 		} else {
1743 			nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar *)nsuri);
1744 			if (nsptr == NULL) {
1745 				nsptr = xmlNewNs(newnode, (xmlChar *)nsuri, prefix);
1746 			}
1747 			newnode->ns = nsptr;
1748 		}
1749 	}
1750 
1751 	_node_as_zval(sxe, newnode, return_value, SXE_ITER_NONE, (char *)localname, prefix, 0);
1752 
1753 	xmlFree(localname);
1754 	if (prefix != NULL) {
1755 		xmlFree(prefix);
1756 	}
1757 }
1758 /* }}} */
1759 
1760 /* {{{ Add Attribute with optional namespace information */
PHP_METHOD(SimpleXMLElement,addAttribute)1761 PHP_METHOD(SimpleXMLElement, addAttribute)
1762 {
1763 	php_sxe_object *sxe;
1764 	char           *qname, *value = NULL, *nsuri = NULL;
1765 	size_t             qname_len, value_len = 0, nsuri_len = 0;
1766 	xmlNodePtr      node;
1767 	xmlAttrPtr      attrp = NULL;
1768 	xmlNsPtr        nsptr = NULL;
1769 	xmlChar        *localname, *prefix = NULL;
1770 
1771 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss|s!",
1772 		&qname, &qname_len, &value, &value_len, &nsuri, &nsuri_len) == FAILURE) {
1773 		RETURN_THROWS();
1774 	}
1775 
1776 	if (qname_len == 0) {
1777 		zend_argument_value_error(1, "cannot be empty");
1778 		RETURN_THROWS();
1779 	}
1780 
1781 	sxe = Z_SXEOBJ_P(ZEND_THIS);
1782 	GET_NODE(sxe, node);
1783 
1784 	node = php_sxe_get_first_node(sxe, node);
1785 
1786 	if (node && node->type != XML_ELEMENT_NODE) {
1787 		node = node->parent;
1788 	}
1789 
1790 	if (node == NULL) {
1791 		php_error_docref(NULL, E_WARNING, "Unable to locate parent Element");
1792 		return;
1793 	}
1794 
1795 	localname = xmlSplitQName2((xmlChar *)qname, &prefix);
1796 	if (localname == NULL) {
1797 		if (nsuri_len > 0) {
1798 			if (prefix != NULL) {
1799 				xmlFree(prefix);
1800 			}
1801 			php_error_docref(NULL, E_WARNING, "Attribute requires prefix for namespace");
1802 			return;
1803 		}
1804 		localname = xmlStrdup((xmlChar *)qname);
1805 	}
1806 
1807 	attrp = xmlHasNsProp(node, localname, (xmlChar *)nsuri);
1808 	if (attrp != NULL && attrp->type != XML_ATTRIBUTE_DECL) {
1809 		xmlFree(localname);
1810 		if (prefix != NULL) {
1811 			xmlFree(prefix);
1812 		}
1813 		php_error_docref(NULL, E_WARNING, "Attribute already exists");
1814 		return;
1815 	}
1816 
1817 	if (nsuri != NULL) {
1818 		nsptr = xmlSearchNsByHref(node->doc, node, (xmlChar *)nsuri);
1819 		if (nsptr == NULL) {
1820 			nsptr = xmlNewNs(node, (xmlChar *)nsuri, prefix);
1821 		}
1822 	}
1823 
1824 	attrp = xmlNewNsProp(node, nsptr, localname, (xmlChar *)value);
1825 
1826 	xmlFree(localname);
1827 	if (prefix != NULL) {
1828 		xmlFree(prefix);
1829 	}
1830 }
1831 /* }}} */
1832 
1833 /* {{{ cast_object() */
cast_object(zval * object,int type,char * contents)1834 static zend_result cast_object(zval *object, int type, char *contents)
1835 {
1836 	if (contents) {
1837 		ZVAL_STRINGL(object, contents, strlen(contents));
1838 	} else {
1839 		ZVAL_NULL(object);
1840 	}
1841 
1842 	switch (type) {
1843 		case IS_STRING:
1844 			convert_to_string(object);
1845 			break;
1846 		case _IS_BOOL:
1847 			convert_to_boolean(object);
1848 			break;
1849 		case IS_LONG:
1850 			convert_to_long(object);
1851 			break;
1852 		case IS_DOUBLE:
1853 			convert_to_double(object);
1854 			break;
1855 		case _IS_NUMBER:
1856 			convert_scalar_to_number(object);
1857 			break;
1858 		default:
1859 			return FAILURE;
1860 	}
1861 	return SUCCESS;
1862 }
1863 /* }}} */
1864 
1865 /* {{{ sxe_object_cast() */
sxe_object_cast_ex(zend_object * readobj,zval * writeobj,int type)1866 static zend_result sxe_object_cast_ex(zend_object *readobj, zval *writeobj, int type)
1867 {
1868 	php_sxe_object *sxe;
1869 	xmlChar        *contents = NULL;
1870 	bool            free_contents = true;
1871 	xmlNodePtr	    node;
1872 	zend_result rv;
1873 
1874 	sxe = php_sxe_fetch_object(readobj);
1875 
1876 	if (type == _IS_BOOL) {
1877 		node = php_sxe_get_first_node(sxe, NULL);
1878 		if (node) {
1879 			ZVAL_TRUE(writeobj);
1880 		} else {
1881 			ZVAL_BOOL(writeobj, !sxe_prop_is_empty(readobj));
1882 		}
1883 		return SUCCESS;
1884 	}
1885 
1886 	if (sxe->iter.type != SXE_ITER_NONE) {
1887 		node = php_sxe_get_first_node(sxe, NULL);
1888 		if (node) {
1889 			contents = xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, node->children, 1);
1890 		}
1891 	} else {
1892 		if (!sxe->node) {
1893 			if (sxe->document) {
1894 				php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement((xmlDocPtr) sxe->document->ptr), NULL);
1895 			}
1896 		}
1897 
1898 		if (sxe->node && sxe->node->node) {
1899 			if (sxe->node->node->children) {
1900 				contents = xmlNodeListGetString((xmlDocPtr) sxe->document->ptr, sxe->node->node->children, 1);
1901 			} else if (sxe->node->node->type == XML_COMMENT_NODE || sxe->node->node->type == XML_PI_NODE) {
1902 				contents = sxe->node->node->content;
1903 				free_contents = false;
1904 			}
1905 		}
1906 	}
1907 
1908 	rv = cast_object(writeobj, type, (char *)contents);
1909 
1910 	if (contents && free_contents) {
1911 		xmlFree(contents);
1912 	}
1913 
1914 	return rv;
1915 }
1916 /* }}} */
1917 
1918 /*  {{{ Variant of sxe_object_cast_ex that handles overwritten __toString() method */
sxe_object_cast(zend_object * readobj,zval * writeobj,int type)1919 static zend_result sxe_object_cast(zend_object *readobj, zval *writeobj, int type)
1920 {
1921 	if (type == IS_STRING
1922 		&& zend_std_cast_object_tostring(readobj, writeobj, IS_STRING) == SUCCESS
1923 	) {
1924 		return SUCCESS;
1925 	}
1926 
1927 	return sxe_object_cast_ex(readobj, writeobj, type);
1928 }
1929 /* }}} */
1930 
1931 /* {{{ Returns the string content */
PHP_METHOD(SimpleXMLElement,__toString)1932 PHP_METHOD(SimpleXMLElement, __toString)
1933 {
1934 	if (zend_parse_parameters_none() == FAILURE) {
1935 		RETURN_THROWS();
1936 	}
1937 
1938 	if (sxe_object_cast_ex(Z_OBJ_P(ZEND_THIS), return_value, IS_STRING) != SUCCESS) {
1939 		zval_ptr_dtor(return_value);
1940 		RETURN_EMPTY_STRING();
1941 	}
1942 }
1943 /* }}} */
1944 
php_sxe_count_elements_helper(php_sxe_object * sxe,zend_long * count)1945 static zend_result php_sxe_count_elements_helper(php_sxe_object *sxe, zend_long *count) /* {{{ */
1946 {
1947 	xmlNodePtr      node;
1948 	zval            data;
1949 
1950 	*count = 0;
1951 
1952 	ZVAL_COPY_VALUE(&data, &sxe->iter.data);
1953 	ZVAL_UNDEF(&sxe->iter.data);
1954 
1955 	node = php_sxe_reset_iterator(sxe, 0);
1956 
1957 	while (node)
1958 	{
1959 		(*count)++;
1960 		node = php_sxe_iterator_fetch(sxe, node->next, 0);
1961 	}
1962 
1963 	if (!Z_ISUNDEF(sxe->iter.data)) {
1964 		zval_ptr_dtor(&sxe->iter.data);
1965 	}
1966 	ZVAL_COPY_VALUE(&sxe->iter.data, &data);
1967 
1968 	return SUCCESS;
1969 }
1970 /* }}} */
1971 
sxe_count_elements(zend_object * object,zend_long * count)1972 static zend_result sxe_count_elements(zend_object *object, zend_long *count) /* {{{ */
1973 {
1974 	php_sxe_object  *intern;
1975 	intern = php_sxe_fetch_object(object);
1976 	if (intern->fptr_count) {
1977 		zval rv;
1978 		zend_call_method_with_0_params(object, intern->zo.ce, &intern->fptr_count, "count", &rv);
1979 		if (!Z_ISUNDEF(rv)) {
1980 			*count = zval_get_long(&rv);
1981 			zval_ptr_dtor(&rv);
1982 			return SUCCESS;
1983 		}
1984 		return FAILURE;
1985 	}
1986 	return php_sxe_count_elements_helper(intern, count);
1987 }
1988 /* }}} */
1989 
1990 /* {{{ Get number of child elements */
PHP_METHOD(SimpleXMLElement,count)1991 PHP_METHOD(SimpleXMLElement, count)
1992 {
1993 	zend_long count = 0;
1994 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
1995 
1996 	if (zend_parse_parameters_none() == FAILURE) {
1997 		RETURN_THROWS();
1998 	}
1999 
2000 	php_sxe_count_elements_helper(sxe, &count);
2001 
2002 	RETURN_LONG(count);
2003 }
2004 /* }}} */
2005 
2006 
2007 /* {{{ Rewind to first element */
PHP_METHOD(SimpleXMLElement,rewind)2008 PHP_METHOD(SimpleXMLElement, rewind)
2009 {
2010 	if (zend_parse_parameters_none() == FAILURE) {
2011 		RETURN_THROWS();
2012 	}
2013 
2014 	php_sxe_rewind_iterator(Z_SXEOBJ_P(ZEND_THIS));
2015 }
2016 /* }}} */
2017 
2018 /* {{{ Check whether iteration is valid */
PHP_METHOD(SimpleXMLElement,valid)2019 PHP_METHOD(SimpleXMLElement, valid)
2020 {
2021 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2022 
2023 	if (zend_parse_parameters_none() == FAILURE) {
2024 		RETURN_THROWS();
2025 	}
2026 
2027 	RETURN_BOOL(!Z_ISUNDEF(sxe->iter.data));
2028 }
2029 /* }}} */
2030 
2031 /* {{{ Get current element */
PHP_METHOD(SimpleXMLElement,current)2032 PHP_METHOD(SimpleXMLElement, current)
2033 {
2034 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2035 
2036 	if (zend_parse_parameters_none() == FAILURE) {
2037 		RETURN_THROWS();
2038 	}
2039 
2040 	if (Z_ISUNDEF(sxe->iter.data)) {
2041 		zend_throw_error(NULL, "Iterator not initialized or already consumed");
2042 		RETURN_THROWS();
2043 	}
2044 
2045 	RETURN_COPY_DEREF(&sxe->iter.data);
2046 }
2047 /* }}} */
2048 
2049 /* {{{ Get name of current child element */
PHP_METHOD(SimpleXMLElement,key)2050 PHP_METHOD(SimpleXMLElement, key)
2051 {
2052 	xmlNodePtr curnode;
2053 	php_sxe_object *intern;
2054 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2055 
2056 	if (zend_parse_parameters_none() == FAILURE) {
2057 		RETURN_THROWS();
2058 	}
2059 
2060 	if (Z_ISUNDEF(sxe->iter.data)) {
2061 		zend_throw_error(NULL, "Iterator not initialized or already consumed");
2062 		RETURN_THROWS();
2063 	}
2064 
2065 	intern = Z_SXEOBJ_P(&sxe->iter.data);
2066 	if (intern == NULL || intern->node == NULL) {
2067 		zend_throw_error(NULL, "Iterator not initialized or already consumed");
2068 		RETURN_THROWS();
2069 	}
2070 
2071 	curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node;
2072 	RETURN_STRINGL((char*)curnode->name, xmlStrlen(curnode->name));
2073 }
2074 /* }}} */
2075 
2076 /* {{{ Move to next element */
PHP_METHOD(SimpleXMLElement,next)2077 PHP_METHOD(SimpleXMLElement, next)
2078 {
2079 	if (zend_parse_parameters_none() == FAILURE) {
2080 		RETURN_THROWS();
2081 	}
2082 
2083 	php_sxe_move_forward_iterator(Z_SXEOBJ_P(ZEND_THIS));
2084 }
2085 /* }}} */
2086 
2087 /* {{{ Check whether element has children (elements) */
PHP_METHOD(SimpleXMLElement,hasChildren)2088 PHP_METHOD(SimpleXMLElement, hasChildren)
2089 {
2090 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2091 	php_sxe_object *child;
2092 	xmlNodePtr      node;
2093 
2094 	if (zend_parse_parameters_none() == FAILURE) {
2095 		RETURN_THROWS();
2096 	}
2097 
2098 	if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
2099 		RETURN_FALSE;
2100 	}
2101 	child = Z_SXEOBJ_P(&sxe->iter.data);
2102 
2103 	GET_NODE(child, node);
2104 	if (node) {
2105 		node = node->children;
2106 	}
2107 	while (node && node->type != XML_ELEMENT_NODE) {
2108 		node = node->next;
2109 	}
2110 	RETURN_BOOL(node ? 1 : 0);
2111 }
2112 /* }}} */
2113 
2114 /* {{{ Get child element iterator */
PHP_METHOD(SimpleXMLElement,getChildren)2115 PHP_METHOD(SimpleXMLElement, getChildren)
2116 {
2117 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2118 
2119 	if (zend_parse_parameters_none() == FAILURE) {
2120 		RETURN_THROWS();
2121 	}
2122 
2123 	if (Z_ISUNDEF(sxe->iter.data) || sxe->iter.type == SXE_ITER_ATTRLIST) {
2124 		return; /* return NULL */
2125 	}
2126 
2127 	RETURN_COPY_DEREF(&sxe->iter.data);
2128 }
2129 
2130 static zend_object_handlers sxe_object_handlers;
2131 
2132 /* {{{ sxe_object_clone() */
2133 static zend_object *
sxe_object_clone(zend_object * object)2134 sxe_object_clone(zend_object *object)
2135 {
2136 	php_sxe_object *sxe = php_sxe_fetch_object(object);
2137 	php_sxe_object *clone;
2138 	xmlNodePtr nodep = NULL;
2139 	xmlDocPtr docp = NULL;
2140 	bool is_root_element = sxe->node && sxe->node->node && sxe->node->node->parent
2141 		&& (sxe->node->node->parent->type == XML_DOCUMENT_NODE || sxe->node->node->parent->type == XML_HTML_DOCUMENT_NODE);
2142 
2143 	clone = php_sxe_object_new(sxe->zo.ce, sxe->fptr_count);
2144 
2145 	if (is_root_element) {
2146 		docp = xmlCopyDoc(sxe->document->ptr, 1);
2147 		php_libxml_increment_doc_ref((php_libxml_node_object *)clone, docp);
2148 	} else {
2149 		clone->document = sxe->document;
2150 		if (clone->document) {
2151 			clone->document->refcount++;
2152 			docp = clone->document->ptr;
2153 		}
2154 	}
2155 
2156 	clone->iter.isprefix = sxe->iter.isprefix;
2157 	if (sxe->iter.name != NULL) {
2158 		clone->iter.name = (xmlChar*)estrdup((char*)sxe->iter.name);
2159 	}
2160 	if (sxe->iter.nsprefix != NULL) {
2161 		clone->iter.nsprefix = (xmlChar*)estrdup((char*)sxe->iter.nsprefix);
2162 	}
2163 	clone->iter.type = sxe->iter.type;
2164 
2165 	if (sxe->node) {
2166 		if (is_root_element) {
2167 			nodep = xmlDocGetRootElement(docp);
2168 		} else {
2169 			nodep = xmlDocCopyNode(sxe->node->node, docp, 1);
2170 		}
2171 	}
2172 
2173 	php_libxml_increment_node_ptr((php_libxml_node_object *)clone, nodep, NULL);
2174 
2175 	return &clone->zo;
2176 }
2177 /* }}} */
2178 
2179 /* {{{ sxe_object_free_storage() */
sxe_object_free_storage(zend_object * object)2180 static void sxe_object_free_storage(zend_object *object)
2181 {
2182 	php_sxe_object *sxe;
2183 
2184 	sxe = php_sxe_fetch_object(object);
2185 
2186 	zend_object_std_dtor(&sxe->zo);
2187 
2188 	sxe_object_free_iterxpath(sxe);
2189 
2190 	if (sxe->properties) {
2191 		zend_hash_destroy(sxe->properties);
2192 		FREE_HASHTABLE(sxe->properties);
2193 	}
2194 }
2195 /* }}} */
2196 
2197 /* {{{ php_sxe_find_fptr_count() */
php_sxe_find_fptr_count(zend_class_entry * ce)2198 static zend_function* php_sxe_find_fptr_count(zend_class_entry *ce)
2199 {
2200 	zend_function *fptr_count = NULL;
2201 	zend_class_entry *parent = ce;
2202 	int inherited = 0;
2203 
2204 	while (parent) {
2205 		if (parent == sxe_class_entry) {
2206 			break;
2207 		}
2208 		parent = parent->parent;
2209 		inherited = 1;
2210 	}
2211 
2212 	if (inherited) {
2213 		fptr_count = zend_hash_str_find_ptr(&ce->function_table, "count", sizeof("count") - 1);
2214 		if (fptr_count->common.scope == parent) {
2215 			fptr_count = NULL;
2216 		}
2217 	}
2218 
2219 	return fptr_count;
2220 }
2221 /* }}} */
2222 
2223 /* {{{ php_sxe_object_new() */
php_sxe_object_new(zend_class_entry * ce,zend_function * fptr_count)2224 static php_sxe_object* php_sxe_object_new(zend_class_entry *ce, zend_function *fptr_count)
2225 {
2226 	php_sxe_object *intern;
2227 
2228 	intern = zend_object_alloc(sizeof(php_sxe_object), ce);
2229 
2230 	intern->iter.type = SXE_ITER_NONE;
2231 	intern->iter.nsprefix = NULL;
2232 	intern->iter.name = NULL;
2233 	intern->fptr_count = fptr_count;
2234 
2235 	zend_object_std_init(&intern->zo, ce);
2236 	object_properties_init(&intern->zo, ce);
2237 	intern->zo.handlers = &sxe_object_handlers;
2238 
2239 	return intern;
2240 }
2241 /* }}} */
2242 
2243 /* {{{ sxe_object_new() */
2244 PHP_SXE_API zend_object *
sxe_object_new(zend_class_entry * ce)2245 sxe_object_new(zend_class_entry *ce)
2246 {
2247 	php_sxe_object    *intern;
2248 
2249 	intern = php_sxe_object_new(ce, php_sxe_find_fptr_count(ce));
2250 	return &intern->zo;
2251 }
2252 /* }}} */
2253 
2254 /* {{{ Load a filename and return a simplexml_element object to allow for processing */
PHP_FUNCTION(simplexml_load_file)2255 PHP_FUNCTION(simplexml_load_file)
2256 {
2257 	php_sxe_object *sxe;
2258 	char           *filename;
2259 	size_t             filename_len;
2260 	xmlDocPtr       docp;
2261 	char           *ns = NULL;
2262 	size_t             ns_len = 0;
2263 	zend_long            options = 0;
2264 	zend_class_entry *ce= sxe_class_entry;
2265 	zend_function    *fptr_count;
2266 	bool       isprefix = 0;
2267 
2268 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|C!lsb", &filename, &filename_len, &ce, &options, &ns, &ns_len, &isprefix) == FAILURE) {
2269 		RETURN_THROWS();
2270 	}
2271 
2272 	if (ZEND_LONG_EXCEEDS_INT(options)) {
2273 		zend_argument_value_error(3, "is too large");
2274 		RETURN_THROWS();
2275 	}
2276 
2277 	PHP_LIBXML_SANITIZE_GLOBALS(read_file);
2278 	docp = xmlReadFile(filename, NULL, (int)options);
2279 	PHP_LIBXML_RESTORE_GLOBALS(read_file);
2280 
2281 	if (!docp) {
2282 		RETURN_FALSE;
2283 	}
2284 
2285 	if (!ce) {
2286 		ce = sxe_class_entry;
2287 		fptr_count = NULL;
2288 	} else {
2289 		fptr_count = php_sxe_find_fptr_count(ce);
2290 	}
2291 	sxe = php_sxe_object_new(ce, fptr_count);
2292 	sxe->iter.nsprefix = ns_len ? (xmlChar*)estrdup(ns) : NULL;
2293 	sxe->iter.isprefix = isprefix;
2294 	php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2295 	php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
2296 
2297 	RETURN_OBJ(&sxe->zo);
2298 }
2299 /* }}} */
2300 
2301 /* {{{ Load a string and return a simplexml_element object to allow for processing */
PHP_FUNCTION(simplexml_load_string)2302 PHP_FUNCTION(simplexml_load_string)
2303 {
2304 	php_sxe_object *sxe;
2305 	char           *data;
2306 	size_t             data_len;
2307 	xmlDocPtr       docp;
2308 	char           *ns = NULL;
2309 	size_t             ns_len = 0;
2310 	zend_long            options = 0;
2311 	zend_class_entry *ce= sxe_class_entry;
2312 	zend_function    *fptr_count;
2313 	bool       isprefix = 0;
2314 
2315 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|C!lsb", &data, &data_len, &ce, &options, &ns, &ns_len, &isprefix) == FAILURE) {
2316 		RETURN_THROWS();
2317 	}
2318 
2319 	if (ZEND_SIZE_T_INT_OVFL(data_len)) {
2320 		zend_argument_value_error(1, "is too long");
2321 		RETURN_THROWS();
2322 	}
2323 	if (ZEND_SIZE_T_INT_OVFL(ns_len)) {
2324 		zend_argument_value_error(4, "is too long");
2325 		RETURN_THROWS();
2326 	}
2327 	if (ZEND_LONG_EXCEEDS_INT(options)) {
2328 		zend_argument_value_error(3, "is too large");
2329 		RETURN_THROWS();
2330 	}
2331 
2332 	PHP_LIBXML_SANITIZE_GLOBALS(read_memory);
2333 	docp = xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options);
2334 	PHP_LIBXML_RESTORE_GLOBALS(read_memory);
2335 
2336 	if (!docp) {
2337 		RETURN_FALSE;
2338 	}
2339 
2340 	if (!ce) {
2341 		ce = sxe_class_entry;
2342 		fptr_count = NULL;
2343 	} else {
2344 		fptr_count = php_sxe_find_fptr_count(ce);
2345 	}
2346 	sxe = php_sxe_object_new(ce, fptr_count);
2347 	sxe->iter.nsprefix = ns_len ? (xmlChar*)estrdup(ns) : NULL;
2348 	sxe->iter.isprefix = isprefix;
2349 	php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2350 	php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
2351 
2352 	RETURN_OBJ(&sxe->zo);
2353 }
2354 /* }}} */
2355 
2356 /* {{{ SimpleXMLElement constructor */
PHP_METHOD(SimpleXMLElement,__construct)2357 PHP_METHOD(SimpleXMLElement, __construct)
2358 {
2359 	php_sxe_object *sxe = Z_SXEOBJ_P(ZEND_THIS);
2360 	char           *data, *ns = NULL;
2361 	size_t             data_len, ns_len = 0;
2362 	xmlDocPtr       docp;
2363 	zend_long            options = 0;
2364 	bool       is_url = 0, isprefix = 0;
2365 
2366 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|lbsb", &data, &data_len, &options, &is_url, &ns, &ns_len, &isprefix) == FAILURE) {
2367 		RETURN_THROWS();
2368 	}
2369 
2370 	if (ZEND_SIZE_T_INT_OVFL(data_len)) {
2371 		zend_argument_error(zend_ce_exception, 1, "is too long");
2372 		RETURN_THROWS();
2373 	}
2374 	if (ZEND_SIZE_T_INT_OVFL(ns_len)) {
2375 		zend_argument_error(zend_ce_exception, 4, "is too long");
2376 		RETURN_THROWS();
2377 	}
2378 	if (ZEND_LONG_EXCEEDS_INT(options)) {
2379 		zend_argument_error(zend_ce_exception, 2, "is invalid");
2380 		RETURN_THROWS();
2381 	}
2382 
2383 	PHP_LIBXML_SANITIZE_GLOBALS(read_file_or_memory);
2384 	docp = is_url ? xmlReadFile(data, NULL, (int)options) : xmlReadMemory(data, (int)data_len, NULL, NULL, (int)options);
2385 	PHP_LIBXML_RESTORE_GLOBALS(read_file_or_memory);
2386 
2387 	if (!docp) {
2388 		zend_throw_exception(zend_ce_exception, "String could not be parsed as XML", 0);
2389 		RETURN_THROWS();
2390 	}
2391 
2392 	sxe_object_free_iterxpath(sxe);
2393 
2394 	sxe->iter.nsprefix = ns_len ? (xmlChar*)estrdup(ns) : NULL;
2395 	sxe->iter.isprefix = isprefix;
2396 	php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, docp);
2397 	php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, xmlDocGetRootElement(docp), NULL);
2398 }
2399 /* }}} */
2400 
2401 static const zend_object_iterator_funcs php_sxe_iterator_funcs = { /* {{{ */
2402 	php_sxe_iterator_dtor,
2403 	php_sxe_iterator_valid,
2404 	php_sxe_iterator_current_data,
2405 	php_sxe_iterator_current_key,
2406 	php_sxe_iterator_move_forward,
2407 	php_sxe_iterator_rewind,
2408 	NULL,
2409 	NULL, /* get_gc */
2410 };
2411 /* }}} */
2412 
php_sxe_iterator_fetch(php_sxe_object * sxe,xmlNodePtr node,int use_data)2413 static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data) /* {{{ */
2414 {
2415 	xmlChar *prefix  = sxe->iter.nsprefix;
2416 	int isprefix  = sxe->iter.isprefix;
2417 
2418 	if (sxe->iter.type == SXE_ITER_ATTRLIST) {
2419 		if (sxe->iter.name) {
2420 			while (node) {
2421 				if (node->type == XML_ATTRIBUTE_NODE) {
2422 					if (!xmlStrcmp(node->name, sxe->iter.name) && match_ns(sxe, node, prefix, isprefix)) {
2423 						break;
2424 					}
2425 				}
2426 				node = node->next;
2427 			}
2428 		} else {
2429 			while (node) {
2430 				if (node->type == XML_ATTRIBUTE_NODE) {
2431 					if (match_ns(sxe, node, prefix, isprefix)) {
2432 						break;
2433 					}
2434 				}
2435 				node = node->next;
2436 			}
2437 		}
2438 	} else if (sxe->iter.type == SXE_ITER_ELEMENT && sxe->iter.name) {
2439 		while (node) {
2440 			if (node->type == XML_ELEMENT_NODE) {
2441 				if (!xmlStrcmp(node->name, sxe->iter.name) && match_ns(sxe, node, prefix, isprefix)) {
2442 					break;
2443 				}
2444 			}
2445 			node = node->next;
2446 		}
2447 	} else {
2448 		while (node) {
2449 			if (node->type == XML_ELEMENT_NODE) {
2450 				if (match_ns(sxe, node, prefix, isprefix)) {
2451 					break;
2452 				}
2453 			}
2454 			node = node->next;
2455 		}
2456 	}
2457 
2458 	if (node && use_data) {
2459 		_node_as_zval(sxe, node, &sxe->iter.data, SXE_ITER_NONE, NULL, prefix, isprefix);
2460 	}
2461 
2462 	return node;
2463 }
2464 /* }}} */
2465 
php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object * sxe,int use_data)2466 static xmlNodePtr php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object *sxe, int use_data)
2467 {
2468 	xmlNodePtr node;
2469 	GET_NODE(sxe, node)
2470 
2471 	if (node) {
2472 		switch (sxe->iter.type) {
2473 			case SXE_ITER_ELEMENT:
2474 			case SXE_ITER_CHILD:
2475 			case SXE_ITER_NONE:
2476 				node = node->children;
2477 				break;
2478 			case SXE_ITER_ATTRLIST:
2479 				node = (xmlNodePtr) node->properties;
2480 		}
2481 		if (use_data) {
2482 			ZEND_ASSERT(Z_ISUNDEF(sxe->iter.data));
2483 		}
2484 		return php_sxe_iterator_fetch(sxe, node, use_data);
2485 	}
2486 	return NULL;
2487 }
2488 
php_sxe_reset_iterator(php_sxe_object * sxe,int use_data)2489 static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {{{ */
2490 {
2491 	if (!Z_ISUNDEF(sxe->iter.data)) {
2492 		zval_ptr_dtor(&sxe->iter.data);
2493 		ZVAL_UNDEF(&sxe->iter.data);
2494 	}
2495 
2496 	return php_sxe_reset_iterator_no_clear_iter_data(sxe, use_data);
2497 }
2498 /* }}} */
2499 
php_sxe_get_iterator(zend_class_entry * ce,zval * object,int by_ref)2500 zend_object_iterator *php_sxe_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
2501 {
2502 	php_sxe_iterator *iterator;
2503 
2504 	if (by_ref) {
2505 		zend_throw_error(NULL, "An iterator cannot be used with foreach by reference");
2506 		return NULL;
2507 	}
2508 	iterator = emalloc(sizeof(php_sxe_iterator));
2509 	zend_iterator_init(&iterator->intern);
2510 
2511 	ZVAL_OBJ_COPY(&iterator->intern.data, Z_OBJ_P(object));
2512 	iterator->intern.funcs = &php_sxe_iterator_funcs;
2513 	iterator->sxe = Z_SXEOBJ_P(object);
2514 
2515 	return (zend_object_iterator*)iterator;
2516 }
2517 /* }}} */
2518 
php_sxe_iterator_dtor(zend_object_iterator * iter)2519 static void php_sxe_iterator_dtor(zend_object_iterator *iter) /* {{{ */
2520 {
2521 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2522 
2523 	/* cleanup handled in sxe_object_dtor as we don't always have an iterator wrapper */
2524 	if (!Z_ISUNDEF(iterator->intern.data)) {
2525 		zval_ptr_dtor(&iterator->intern.data);
2526 	}
2527 }
2528 /* }}} */
2529 
php_sxe_iterator_valid(zend_object_iterator * iter)2530 static int php_sxe_iterator_valid(zend_object_iterator *iter) /* {{{ */
2531 {
2532 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2533 
2534 	return Z_ISUNDEF(iterator->sxe->iter.data) ? FAILURE : SUCCESS;
2535 }
2536 /* }}} */
2537 
php_sxe_iterator_current_data(zend_object_iterator * iter)2538 static zval *php_sxe_iterator_current_data(zend_object_iterator *iter) /* {{{ */
2539 {
2540 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2541 
2542 	return &iterator->sxe->iter.data;
2543 }
2544 /* }}} */
2545 
php_sxe_iterator_current_key(zend_object_iterator * iter,zval * key)2546 static void php_sxe_iterator_current_key(zend_object_iterator *iter, zval *key) /* {{{ */
2547 {
2548 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2549 	zval *curobj = &iterator->sxe->iter.data;
2550 	if (Z_ISUNDEF_P(curobj)) {
2551 		ZVAL_NULL(key);
2552 		return;
2553 	}
2554 
2555 	php_sxe_object *intern = Z_SXEOBJ_P(curobj);
2556 
2557 	xmlNodePtr curnode = NULL;
2558 	if (intern != NULL && intern->node != NULL) {
2559 		curnode = (xmlNodePtr)((php_libxml_node_ptr *)intern->node)->node;
2560 	}
2561 
2562 	if (curnode) {
2563 		ZVAL_STRINGL(key, (char *) curnode->name, xmlStrlen(curnode->name));
2564 	} else {
2565 		ZVAL_NULL(key);
2566 	}
2567 }
2568 /* }}} */
2569 
php_sxe_move_forward_iterator(php_sxe_object * sxe)2570 PHP_SXE_API void php_sxe_move_forward_iterator(php_sxe_object *sxe) /* {{{ */
2571 {
2572 	xmlNodePtr      node = NULL;
2573 	php_sxe_object  *intern;
2574 
2575 	if (!Z_ISUNDEF(sxe->iter.data)) {
2576 		intern = Z_SXEOBJ_P(&sxe->iter.data);
2577 		GET_NODE(intern, node)
2578 		zval_ptr_dtor(&sxe->iter.data);
2579 		ZVAL_UNDEF(&sxe->iter.data);
2580 	}
2581 
2582 	if (node) {
2583 		php_sxe_iterator_fetch(sxe, node->next, 1);
2584 	}
2585 }
2586 /* }}} */
2587 
php_sxe_iterator_move_forward(zend_object_iterator * iter)2588 static void php_sxe_iterator_move_forward(zend_object_iterator *iter) /* {{{ */
2589 {
2590 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2591 	php_sxe_move_forward_iterator(iterator->sxe);
2592 }
2593 /* }}} */
2594 
php_sxe_rewind_iterator(php_sxe_object * sxe)2595 PHP_SXE_API void php_sxe_rewind_iterator(php_sxe_object *sxe) /* {{{ */
2596 {
2597 	php_sxe_reset_iterator(sxe, 1);
2598 }
2599 /* }}} */
2600 
php_sxe_iterator_rewind(zend_object_iterator * iter)2601 static void php_sxe_iterator_rewind(zend_object_iterator *iter) /* {{{ */
2602 {
2603 	php_sxe_object	*sxe;
2604 
2605 	php_sxe_iterator *iterator = (php_sxe_iterator *)iter;
2606 	sxe = iterator->sxe;
2607 
2608 	php_sxe_reset_iterator(sxe, 1);
2609 }
2610 /* }}} */
2611 
simplexml_export_node(zval * object)2612 void *simplexml_export_node(zval *object) /* {{{ */
2613 {
2614 	php_sxe_object *sxe;
2615 	xmlNodePtr node;
2616 
2617 	sxe = Z_SXEOBJ_P(object);
2618 	GET_NODE(sxe, node);
2619 	return php_sxe_get_first_node(sxe, node);
2620 }
2621 /* }}} */
2622 
2623 /* {{{ Get a simplexml_element object from dom to allow for processing */
PHP_FUNCTION(simplexml_import_dom)2624 PHP_FUNCTION(simplexml_import_dom)
2625 {
2626 	php_sxe_object *sxe;
2627 	zval *node;
2628 	php_libxml_node_object *object;
2629 	xmlNodePtr		nodep = NULL;
2630 	zend_class_entry *ce = sxe_class_entry;
2631 	zend_function    *fptr_count;
2632 
2633 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o|C!", &node, &ce) == FAILURE) {
2634 		RETURN_THROWS();
2635 	}
2636 
2637 	nodep = php_libxml_import_node(node);
2638 
2639 	if (!nodep) {
2640 		zend_argument_type_error(1, "must be of type SimpleXMLElement|DOMNode, %s given", zend_zval_type_name(node));
2641 		RETURN_THROWS();
2642 	}
2643 
2644 	if (nodep->doc == NULL) {
2645 		php_error_docref(NULL, E_WARNING, "Imported Node must have associated Document");
2646 		RETURN_NULL();
2647 	}
2648 
2649 	if (nodep->type == XML_DOCUMENT_NODE || nodep->type == XML_HTML_DOCUMENT_NODE) {
2650 		nodep = xmlDocGetRootElement((xmlDocPtr) nodep);
2651 	}
2652 
2653 	if (nodep && nodep->type == XML_ELEMENT_NODE) {
2654 		if (!ce) {
2655 			ce = sxe_class_entry;
2656 			fptr_count = NULL;
2657 		} else {
2658 			fptr_count = php_sxe_find_fptr_count(ce);
2659 		}
2660 
2661 		object = Z_LIBXML_NODE_P(node);
2662 
2663 		sxe = php_sxe_object_new(ce, fptr_count);
2664 		sxe->document = object->document;
2665 		php_libxml_increment_doc_ref((php_libxml_node_object *)sxe, nodep->doc);
2666 		php_libxml_increment_node_ptr((php_libxml_node_object *)sxe, nodep, NULL);
2667 
2668 		RETURN_OBJ(&sxe->zo);
2669 	} else {
2670 		php_error_docref(NULL, E_WARNING, "Invalid Nodetype to import");
2671 		RETVAL_NULL();
2672 	}
2673 }
2674 /* }}} */
2675 
2676 static const zend_module_dep simplexml_deps[] = { /* {{{ */
2677 	ZEND_MOD_REQUIRED("libxml")
2678 	ZEND_MOD_REQUIRED("spl")
2679 	ZEND_MOD_END
2680 };
2681 /* }}} */
2682 
2683 zend_module_entry simplexml_module_entry = { /* {{{ */
2684 	STANDARD_MODULE_HEADER_EX, NULL,
2685 	simplexml_deps,
2686 	"SimpleXML",
2687 	ext_functions,
2688 	PHP_MINIT(simplexml),
2689 	PHP_MSHUTDOWN(simplexml),
2690 	NULL,
2691 	NULL,
2692 	PHP_MINFO(simplexml),
2693 	PHP_SIMPLEXML_VERSION,
2694 	STANDARD_MODULE_PROPERTIES
2695 };
2696 /* }}} */
2697 
2698 #ifdef COMPILE_DL_SIMPLEXML
2699 ZEND_GET_MODULE(simplexml)
2700 #endif
2701 
2702 /* {{{ PHP_MINIT_FUNCTION(simplexml) */
PHP_MINIT_FUNCTION(simplexml)2703 PHP_MINIT_FUNCTION(simplexml)
2704 {
2705 	sxe_class_entry = register_class_SimpleXMLElement(zend_ce_stringable, zend_ce_countable, spl_ce_RecursiveIterator);
2706 	sxe_class_entry->create_object = sxe_object_new;
2707 	sxe_class_entry->get_iterator = php_sxe_get_iterator;
2708 
2709 	memcpy(&sxe_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
2710 	sxe_object_handlers.offset = XtOffsetOf(php_sxe_object, zo);
2711 	sxe_object_handlers.free_obj = sxe_object_free_storage;
2712 	sxe_object_handlers.clone_obj = sxe_object_clone;
2713 	sxe_object_handlers.read_property = sxe_property_read;
2714 	sxe_object_handlers.write_property = sxe_property_write;
2715 	sxe_object_handlers.read_dimension = sxe_dimension_read;
2716 	sxe_object_handlers.write_dimension = sxe_dimension_write;
2717 	sxe_object_handlers.get_property_ptr_ptr = sxe_property_get_adr;
2718 	sxe_object_handlers.has_property = sxe_property_exists;
2719 	sxe_object_handlers.unset_property = sxe_property_delete;
2720 	sxe_object_handlers.has_dimension = sxe_dimension_exists;
2721 	sxe_object_handlers.unset_dimension = sxe_dimension_delete;
2722 	sxe_object_handlers.get_properties = sxe_get_properties;
2723 	sxe_object_handlers.compare = sxe_objects_compare;
2724 	sxe_object_handlers.cast_object = sxe_object_cast;
2725 	sxe_object_handlers.count_elements = sxe_count_elements;
2726 	sxe_object_handlers.get_debug_info = sxe_get_debug_info;
2727 	sxe_object_handlers.get_closure = NULL;
2728 	sxe_object_handlers.get_gc = sxe_get_gc;
2729 
2730 	/* TODO: Why do we have two variables for this? */
2731 	ce_SimpleXMLElement = sxe_class_entry;
2732 
2733 	ce_SimpleXMLIterator = register_class_SimpleXMLIterator(ce_SimpleXMLElement);
2734 
2735 	php_libxml_register_export(sxe_class_entry, simplexml_export_node);
2736 
2737 	return SUCCESS;
2738 }
2739 /* }}} */
2740 
2741 /* {{{ PHP_MSHUTDOWN_FUNCTION(simplexml) */
PHP_MSHUTDOWN_FUNCTION(simplexml)2742 PHP_MSHUTDOWN_FUNCTION(simplexml)
2743 {
2744 	sxe_class_entry = NULL;
2745 	return SUCCESS;
2746 }
2747 /* }}} */
2748 
2749 /* {{{ PHP_MINFO_FUNCTION(simplexml) */
PHP_MINFO_FUNCTION(simplexml)2750 PHP_MINFO_FUNCTION(simplexml)
2751 {
2752 	php_info_print_table_start();
2753 	php_info_print_table_row(2, "SimpleXML support", "enabled");
2754 	php_info_print_table_row(2, "Schema support",
2755 #ifdef LIBXML_SCHEMAS_ENABLED
2756 		"enabled");
2757 #else
2758 		"not available");
2759 #endif
2760 	php_info_print_table_end();
2761 }
2762 /* }}} */
2763 
2764 #endif
2765