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