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