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