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