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