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