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