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