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