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