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: Christian Stocker <chregu@php.net> |
14 | Rob Richards <rrichards@php.net> |
15 +----------------------------------------------------------------------+
16 */
17
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21
22 #include "php.h"
23 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
24 #include "php_dom.h"
25
26 /*
27 * class DOMElement extends DOMNode
28 *
29 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-745549614
30 * Since:
31 */
32
33 /* {{{ */
PHP_METHOD(DOMElement,__construct)34 PHP_METHOD(DOMElement, __construct)
35 {
36 xmlNodePtr nodep = NULL, oldnode = NULL;
37 dom_object *intern;
38 char *name, *value = NULL, *uri = NULL;
39 char *localname = NULL, *prefix = NULL;
40 int errorcode = 0;
41 size_t name_len, value_len = 0, uri_len = 0;
42 int name_valid;
43 xmlNsPtr nsptr = NULL;
44
45 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s", &name, &name_len, &value, &value_len, &uri, &uri_len) == FAILURE) {
46 RETURN_THROWS();
47 }
48
49 name_valid = xmlValidateName((xmlChar *) name, 0);
50 if (name_valid != 0) {
51 php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
52 RETURN_THROWS();
53 }
54
55 /* Namespace logic is separate and only when uri passed in to insure no BC breakage */
56 if (uri_len > 0) {
57 errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
58 if (errorcode == 0) {
59 nodep = xmlNewNode (NULL, (xmlChar *)localname);
60 if (nodep != NULL && uri != NULL) {
61 nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
62 xmlSetNs(nodep, nsptr);
63 }
64 }
65 xmlFree(localname);
66 if (prefix != NULL) {
67 xmlFree(prefix);
68 }
69 if (errorcode != 0) {
70 if (nodep != NULL) {
71 xmlFreeNode(nodep);
72 }
73 php_dom_throw_error(errorcode, 1);
74 RETURN_THROWS();
75 }
76 } else {
77 /* If you don't pass a namespace uri, then you can't set a prefix */
78 localname = (char *) xmlSplitQName2((xmlChar *) name, (xmlChar **) &prefix);
79 if (prefix != NULL) {
80 xmlFree(localname);
81 xmlFree(prefix);
82 php_dom_throw_error(NAMESPACE_ERR, 1);
83 RETURN_THROWS();
84 }
85 nodep = xmlNewNode(NULL, (xmlChar *) name);
86 }
87
88 if (!nodep) {
89 php_dom_throw_error(INVALID_STATE_ERR, 1);
90 RETURN_THROWS();
91 }
92
93 if (value_len > 0) {
94 xmlNodeSetContentLen(nodep, (xmlChar *) value, value_len);
95 }
96
97 intern = Z_DOMOBJ_P(ZEND_THIS);
98 oldnode = dom_object_get_node(intern);
99 if (oldnode != NULL) {
100 php_libxml_node_decrement_resource((php_libxml_node_object *)intern);
101 }
102 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, nodep, (void *)intern);
103 }
104 /* }}} end DOMElement::__construct */
105
106 /* {{{ tagName string
107 readonly=yes
108 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-104682815
109 Since:
110 */
dom_element_tag_name_read(dom_object * obj,zval * retval)111 int dom_element_tag_name_read(dom_object *obj, zval *retval)
112 {
113 xmlNodePtr nodep;
114 xmlNsPtr ns;
115 xmlChar *qname;
116
117 nodep = dom_object_get_node(obj);
118
119 if (nodep == NULL) {
120 php_dom_throw_error(INVALID_STATE_ERR, 1);
121 return FAILURE;
122 }
123
124 ns = nodep->ns;
125 if (ns != NULL && ns->prefix) {
126 qname = xmlStrdup(ns->prefix);
127 qname = xmlStrcat(qname, (xmlChar *)":");
128 qname = xmlStrcat(qname, nodep->name);
129 ZVAL_STRING(retval, (char *)qname);
130 xmlFree(qname);
131 } else {
132 ZVAL_STRING(retval, (char *) nodep->name);
133 }
134
135 return SUCCESS;
136 }
137
138 /* }}} */
139
dom_element_reflected_attribute_read(dom_object * obj,zval * retval,const char * name)140 static int dom_element_reflected_attribute_read(dom_object *obj, zval *retval, const char *name)
141 {
142 xmlNodePtr nodep = dom_object_get_node(obj);
143
144 if (nodep == NULL) {
145 php_dom_throw_error(INVALID_STATE_ERR, 1);
146 return FAILURE;
147 }
148
149 xmlChar *content = xmlGetNoNsProp(nodep, (const xmlChar *) name);
150 if (content == NULL) {
151 ZVAL_EMPTY_STRING(retval);
152 return SUCCESS;
153 }
154
155 ZVAL_STRING(retval, (const char *) content);
156 xmlFree(content);
157
158 return SUCCESS;
159 }
160
dom_element_reflected_attribute_write(dom_object * obj,zval * newval,const char * name)161 static xmlAttrPtr dom_element_reflected_attribute_write(dom_object *obj, zval *newval, const char *name)
162 {
163 xmlNode *nodep = dom_object_get_node(obj);
164
165 if (nodep == NULL) {
166 php_dom_throw_error(INVALID_STATE_ERR, 1);
167 return NULL;
168 }
169
170 /* Typed property, so it is a string already */
171 ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
172 return xmlSetNsProp(nodep, NULL, (const xmlChar *) name, (const xmlChar *) Z_STRVAL_P(newval));
173 }
174
175 /* {{{ className string
176 URL: https://dom.spec.whatwg.org/#dom-element-classname
177 Since:
178 */
dom_element_class_name_read(dom_object * obj,zval * retval)179 int dom_element_class_name_read(dom_object *obj, zval *retval)
180 {
181 return dom_element_reflected_attribute_read(obj, retval, "class");
182 }
183
dom_element_class_name_write(dom_object * obj,zval * newval)184 int dom_element_class_name_write(dom_object *obj, zval *newval)
185 {
186 if (dom_element_reflected_attribute_write(obj, newval, "class")) {
187 return SUCCESS;
188 }
189 return FAILURE;
190 }
191 /* }}} */
192
193 /* {{{ id string
194 URL: https://dom.spec.whatwg.org/#dom-element-id
195 Since:
196 */
dom_element_id_read(dom_object * obj,zval * retval)197 int dom_element_id_read(dom_object *obj, zval *retval)
198 {
199 return dom_element_reflected_attribute_read(obj, retval, "id");
200 }
201
202 static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id);
203
dom_element_id_write(dom_object * obj,zval * newval)204 int dom_element_id_write(dom_object *obj, zval *newval)
205 {
206 xmlAttrPtr attr = dom_element_reflected_attribute_write(obj, newval, "id");
207 if (!attr) {
208 return FAILURE;
209 }
210 php_set_attribute_id(attr, true);
211 return SUCCESS;
212 }
213 /* }}} */
214
215 /* {{{ schemaTypeInfo typeinfo
216 readonly=yes
217 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Element-schemaTypeInfo
218 Since: DOM Level 3
219 */
dom_element_schema_type_info_read(dom_object * obj,zval * retval)220 int dom_element_schema_type_info_read(dom_object *obj, zval *retval)
221 {
222 ZVAL_NULL(retval);
223 return SUCCESS;
224 }
225
226 /* }}} */
227
228 /* Note: the object returned is not necessarily a node, but can be an attribute or a namespace declaration. */
dom_get_dom1_attribute(xmlNodePtr elem,xmlChar * name)229 static xmlNodePtr dom_get_dom1_attribute(xmlNodePtr elem, xmlChar *name) /* {{{ */
230 {
231 int len;
232 const xmlChar *nqname;
233
234 nqname = xmlSplitQName3(name, &len);
235 if (nqname != NULL) {
236 xmlNsPtr ns;
237 if (strncmp((const char *) name, "xmlns:", len + 1) == 0) {
238 ns = elem->nsDef;
239 while (ns) {
240 if (xmlStrEqual(ns->prefix, nqname)) {
241 break;
242 }
243 ns = ns->next;
244 }
245 return (xmlNodePtr)ns;
246 }
247 xmlChar *prefix = xmlStrndup(name, len);
248 ns = xmlSearchNs(elem->doc, elem, prefix);
249 if (prefix != NULL) {
250 xmlFree(prefix);
251 }
252 if (ns != NULL) {
253 return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href);
254 }
255 } else {
256 if (xmlStrEqual(name, (xmlChar *)"xmlns")) {
257 xmlNsPtr nsPtr = elem->nsDef;
258 while (nsPtr) {
259 if (nsPtr->prefix == NULL) {
260 return (xmlNodePtr)nsPtr;
261 }
262 nsPtr = nsPtr->next;
263 }
264 return NULL;
265 }
266 }
267 return (xmlNodePtr)xmlHasNsProp(elem, name, NULL);
268 }
269 /* }}} */
270
271 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9
272 Since:
273 */
PHP_METHOD(DOMElement,getAttribute)274 PHP_METHOD(DOMElement, getAttribute)
275 {
276 zval *id;
277 xmlNode *nodep;
278 char *name;
279 xmlChar *value = NULL;
280 dom_object *intern;
281 xmlNodePtr attr;
282 size_t name_len;
283 bool should_free;
284
285 id = ZEND_THIS;
286 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
287 RETURN_THROWS();
288 }
289
290 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
291
292 attr = dom_get_dom1_attribute(nodep, (xmlChar *)name);
293 if (attr) {
294 switch (attr->type) {
295 case XML_ATTRIBUTE_NODE:
296 value = xmlNodeListGetString(attr->doc, attr->children, 1);
297 should_free = true;
298 break;
299 case XML_NAMESPACE_DECL:
300 value = (xmlChar *) ((xmlNsPtr)attr)->href;
301 should_free = false;
302 break;
303 default:
304 value = (xmlChar *) ((xmlAttributePtr)attr)->defaultValue;
305 should_free = false;
306 }
307 }
308
309 if (value == NULL) {
310 RETURN_EMPTY_STRING();
311 } else {
312 RETVAL_STRING((char *)value);
313 if (should_free) {
314 xmlFree(value);
315 }
316 }
317 }
318 /* }}} end dom_element_get_attribute */
319
320 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-getattributenames
321 Since:
322 */
PHP_METHOD(DOMElement,getAttributeNames)323 PHP_METHOD(DOMElement, getAttributeNames)
324 {
325 zval *id;
326 xmlNode *nodep;
327 dom_object *unused_intern;
328 zval tmp;
329
330 if (zend_parse_parameters_none() == FAILURE) {
331 RETURN_THROWS();
332 }
333
334 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, unused_intern);
335
336 array_init(return_value);
337 HashTable *ht = Z_ARRVAL_P(return_value);
338 zend_hash_real_init_packed(ht);
339
340 for (xmlNsPtr nsptr = nodep->nsDef; nsptr; nsptr = nsptr->next) {
341 const char *prefix = (const char *) nsptr->prefix;
342 if (prefix == NULL) {
343 ZVAL_STRING(&tmp, "xmlns");
344 } else {
345 ZVAL_STR(&tmp, dom_node_concatenated_name_helper(strlen(prefix), prefix, strlen("xmlns"), (const char *) "xmlns"));
346 }
347 zend_hash_next_index_insert(ht, &tmp);
348 }
349
350 for (xmlAttrPtr attr = nodep->properties; attr; attr = attr->next) {
351 ZVAL_STR(&tmp, dom_node_get_node_name_attribute_or_element((const xmlNode *) attr));
352 zend_hash_next_index_insert(ht, &tmp);
353 }
354 }
355 /* }}} end DOMElement::getAttributeNames() */
356
dom_create_attribute(xmlNodePtr nodep,const char * name,const char * value)357 static xmlNodePtr dom_create_attribute(xmlNodePtr nodep, const char *name, const char* value)
358 {
359 if (xmlStrEqual((xmlChar *)name, (xmlChar *)"xmlns")) {
360 return (xmlNodePtr) xmlNewNs(nodep, (xmlChar *)value, NULL);
361 } else {
362 return (xmlNodePtr) xmlSetProp(nodep, (xmlChar *) name, (xmlChar *)value);
363 }
364 }
365
366 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082
367 Since:
368 */
PHP_METHOD(DOMElement,setAttribute)369 PHP_METHOD(DOMElement, setAttribute)
370 {
371 zval *id;
372 xmlNode *nodep;
373 xmlNodePtr attr = NULL;
374 int ret, name_valid;
375 size_t name_len, value_len;
376 dom_object *intern;
377 char *name, *value;
378
379 id = ZEND_THIS;
380 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &value, &value_len) == FAILURE) {
381 RETURN_THROWS();
382 }
383
384 if (name_len == 0) {
385 zend_argument_value_error(1, "cannot be empty");
386 RETURN_THROWS();
387 }
388
389 name_valid = xmlValidateName((xmlChar *) name, 0);
390 if (name_valid != 0) {
391 php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
392 RETURN_THROWS();
393 }
394
395 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
396
397 attr = dom_get_dom1_attribute(nodep, (xmlChar *)name);
398 if (attr != NULL) {
399 switch (attr->type) {
400 case XML_ATTRIBUTE_NODE:
401 node_list_unlink(attr->children);
402 break;
403 case XML_NAMESPACE_DECL:
404 RETURN_FALSE;
405 default:
406 break;
407 }
408
409 }
410
411 attr = dom_create_attribute(nodep, name, value);
412 if (!attr) {
413 zend_argument_value_error(1, "must be a valid XML attribute");
414 RETURN_THROWS();
415 }
416 if (attr->type == XML_NAMESPACE_DECL) {
417 RETURN_TRUE;
418 }
419
420 DOM_RET_OBJ(attr, &ret, intern);
421
422 }
423 /* }}} end dom_element_set_attribute */
424
425 typedef struct {
426 xmlNodePtr current_node;
427 xmlNsPtr defined_ns;
428 } dom_deep_ns_redef_item;
429
430 /* Reconciliation for a *single* namespace, but reconciles *closest* to the subtree needing it. */
dom_deep_ns_redef(xmlNodePtr node,xmlNsPtr ns_to_redefine)431 static void dom_deep_ns_redef(xmlNodePtr node, xmlNsPtr ns_to_redefine)
432 {
433 size_t worklist_capacity = 128;
434 dom_deep_ns_redef_item *worklist = emalloc(sizeof(dom_deep_ns_redef_item) * worklist_capacity);
435 worklist[0].current_node = node;
436 worklist[0].defined_ns = NULL;
437 size_t worklist_size = 1;
438
439 while (worklist_size > 0) {
440 worklist_size--;
441 dom_deep_ns_redef_item *current_worklist_item = &worklist[worklist_size];
442 ZEND_ASSERT(current_worklist_item->current_node->type == XML_ELEMENT_NODE);
443 xmlNsPtr defined_ns = current_worklist_item->defined_ns;
444
445 if (current_worklist_item->current_node->ns == ns_to_redefine) {
446 if (defined_ns == NULL) {
447 defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
448 }
449 current_worklist_item->current_node->ns = defined_ns;
450 }
451
452 for (xmlAttrPtr attr = current_worklist_item->current_node->properties; attr; attr = attr->next) {
453 if (attr->ns == ns_to_redefine) {
454 if (defined_ns == NULL) {
455 defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
456 }
457 attr->ns = defined_ns;
458 }
459 }
460
461 for (xmlNodePtr child = current_worklist_item->current_node->children; child; child = child->next) {
462 if (child->type != XML_ELEMENT_NODE) {
463 continue;
464 }
465 if (worklist_size == worklist_capacity) {
466 if (UNEXPECTED(worklist_capacity >= SIZE_MAX / 3 * 2 / sizeof(dom_deep_ns_redef_item))) {
467 /* Shouldn't be possible to hit, but checked for safety anyway */
468 goto out;
469 }
470 worklist_capacity = worklist_capacity * 3 / 2;
471 worklist = erealloc(worklist, sizeof(dom_deep_ns_redef_item) * worklist_capacity);
472 }
473 worklist[worklist_size].current_node = child;
474 worklist[worklist_size].defined_ns = defined_ns;
475 worklist_size++;
476 }
477 }
478
479 out:
480 efree(worklist);
481 }
482
dom_remove_attribute(xmlNodePtr thisp,xmlNodePtr attrp)483 static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp)
484 {
485 ZEND_ASSERT(thisp != NULL);
486 ZEND_ASSERT(attrp != NULL);
487
488 switch (attrp->type) {
489 case XML_ATTRIBUTE_NODE:
490 if (php_dom_object_get_data(attrp) == NULL) {
491 node_list_unlink(attrp->children);
492 xmlUnlinkNode(attrp);
493 xmlFreeProp((xmlAttrPtr)attrp);
494 } else {
495 xmlUnlinkNode(attrp);
496 }
497 break;
498 case XML_NAMESPACE_DECL: {
499 /* They will always be removed, but can be re-added.
500 *
501 * If any reference was left to the namespace, the only effect is that
502 * the definition is potentially moved closer to the element using it.
503 * If no reference was left, it is actually removed. */
504 xmlNsPtr ns = (xmlNsPtr) attrp;
505 if (thisp->nsDef == ns) {
506 thisp->nsDef = ns->next;
507 } else if (thisp->nsDef != NULL) {
508 xmlNsPtr prev = thisp->nsDef;
509 xmlNsPtr cur = prev->next;
510 while (cur) {
511 if (cur == ns) {
512 prev->next = cur->next;
513 break;
514 }
515 prev = cur;
516 cur = cur->next;
517 }
518 } else {
519 /* defensive: attrp not defined in thisp ??? */
520 #if ZEND_DEBUG
521 ZEND_UNREACHABLE();
522 #endif
523 break; /* defensive */
524 }
525
526 ns->next = NULL;
527 php_libxml_set_old_ns(thisp->doc, ns); /* note: can't deallocate as it might be referenced by a "fake namespace node" */
528 /* xmlReconciliateNs() redefines at the top of the tree instead of closest to the child, own reconciliation here.
529 * Similarly, the DOM version has other issues too (see dom_libxml_reconcile_ensure_namespaces_are_declared). */
530 dom_deep_ns_redef(thisp, ns);
531
532 break;
533 }
534 EMPTY_SWITCH_DEFAULT_CASE();
535 }
536 return true;
537 }
538
539 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9
540 Since:
541 */
PHP_METHOD(DOMElement,removeAttribute)542 PHP_METHOD(DOMElement, removeAttribute)
543 {
544 zval *id;
545 xmlNodePtr nodep, attrp;
546 dom_object *intern;
547 size_t name_len;
548 char *name;
549
550 id = ZEND_THIS;
551 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
552 RETURN_THROWS();
553 }
554
555 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
556
557 attrp = dom_get_dom1_attribute(nodep, (xmlChar *)name);
558 if (attrp == NULL) {
559 RETURN_FALSE;
560 }
561
562 RETURN_BOOL(dom_remove_attribute(nodep, attrp));
563 }
564 /* }}} end dom_element_remove_attribute */
565
566 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8
567 Since:
568 */
PHP_METHOD(DOMElement,getAttributeNode)569 PHP_METHOD(DOMElement, getAttributeNode)
570 {
571 zval *id;
572 xmlNodePtr nodep, attrp;
573 size_t name_len;
574 int ret;
575 dom_object *intern;
576 char *name;
577
578 id = ZEND_THIS;
579 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
580 RETURN_THROWS();
581 }
582
583 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
584
585 attrp = dom_get_dom1_attribute(nodep, (xmlChar *)name);
586 if (attrp == NULL) {
587 RETURN_FALSE;
588 }
589
590 if (attrp->type == XML_NAMESPACE_DECL) {
591 xmlNsPtr original = (xmlNsPtr) attrp;
592 /* Keep parent alive, because we're a fake child. */
593 GC_ADDREF(&intern->std);
594 (void) php_dom_create_fake_namespace_decl(nodep, original, return_value, intern);
595 } else {
596 DOM_RET_OBJ((xmlNodePtr) attrp, &ret, intern);
597 }
598 }
599 /* }}} end dom_element_get_attribute_node */
600
601 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154
602 Since:
603 */
PHP_METHOD(DOMElement,setAttributeNode)604 PHP_METHOD(DOMElement, setAttributeNode)
605 {
606 zval *id, *node;
607 xmlNode *nodep;
608 xmlAttr *attrp, *existattrp = NULL;
609 dom_object *intern, *attrobj, *oldobj;
610 int ret;
611
612 id = ZEND_THIS;
613 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) {
614 RETURN_THROWS();
615 }
616
617 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
618
619 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
620
621 if (attrp->type != XML_ATTRIBUTE_NODE) {
622 zend_argument_value_error(1, "must have the node attribute");
623 RETURN_THROWS();
624 }
625
626 if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) {
627 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
628 RETURN_FALSE;
629 }
630
631 existattrp = xmlHasProp(nodep, attrp->name);
632 if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
633 if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
634 ((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
635 {
636 RETURN_NULL();
637 }
638 xmlUnlinkNode((xmlNodePtr) existattrp);
639 }
640
641 if (attrp->parent != NULL) {
642 xmlUnlinkNode((xmlNodePtr) attrp);
643 }
644
645 if (attrp->doc == NULL && nodep->doc != NULL && intern->document != NULL) {
646 dom_set_document_ref_pointers_attr(attrp, intern->document);
647 }
648
649 xmlAddChild(nodep, (xmlNodePtr) attrp);
650
651 /* Returns old property if removed otherwise NULL */
652 if (existattrp != NULL) {
653 DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern);
654 } else {
655 RETVAL_NULL();
656 }
657
658 }
659 /* }}} end dom_element_set_attribute_node */
660
661 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198
662 Since:
663 */
PHP_METHOD(DOMElement,removeAttributeNode)664 PHP_METHOD(DOMElement, removeAttributeNode)
665 {
666 zval *id, *node;
667 xmlNode *nodep;
668 xmlAttr *attrp;
669 dom_object *intern, *attrobj;
670 int ret;
671
672 id = ZEND_THIS;
673 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) {
674 RETURN_THROWS();
675 }
676
677 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
678
679 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
680
681 if (attrp->type != XML_ATTRIBUTE_NODE || attrp->parent != nodep) {
682 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
683 RETURN_FALSE;
684 }
685
686 xmlUnlinkNode((xmlNodePtr) attrp);
687
688 DOM_RET_OBJ((xmlNodePtr) attrp, &ret, intern);
689
690 }
691 /* }}} end dom_element_remove_attribute_node */
692
693 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D
694 Since:
695 */
PHP_METHOD(DOMElement,getElementsByTagName)696 PHP_METHOD(DOMElement, getElementsByTagName)
697 {
698 size_t name_len;
699 dom_object *intern, *namednode;
700 char *name;
701
702 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
703 RETURN_THROWS();
704 }
705
706 DOM_GET_THIS_INTERN(intern);
707
708 php_dom_create_iterator(return_value, DOM_NODELIST);
709 namednode = Z_DOMOBJ_P(return_value);
710 dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
711 }
712 /* }}} end dom_element_get_elements_by_tag_name */
713
714 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS
715 Since: DOM Level 2
716 */
PHP_METHOD(DOMElement,getAttributeNS)717 PHP_METHOD(DOMElement, getAttributeNS)
718 {
719 zval *id;
720 xmlNodePtr elemp;
721 xmlNsPtr nsptr;
722 dom_object *intern;
723 size_t uri_len = 0, name_len = 0;
724 char *uri, *name;
725 xmlChar *strattr;
726
727 id = ZEND_THIS;
728 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
729 RETURN_THROWS();
730 }
731
732 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
733
734 strattr = xmlGetNsProp(elemp, (xmlChar *) name, (xmlChar *) uri);
735
736 if (strattr != NULL) {
737 RETVAL_STRING((char *)strattr);
738 xmlFree(strattr);
739 } else {
740 if (xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) {
741 nsptr = dom_get_nsdecl(elemp, (xmlChar *)name);
742 if (nsptr != NULL) {
743 RETVAL_STRING((char *) nsptr->href);
744 } else {
745 RETVAL_EMPTY_STRING();
746 }
747 } else {
748 RETVAL_EMPTY_STRING();
749 }
750 }
751
752 }
753 /* }}} end dom_element_get_attribute_ns */
754
755 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS
756 Since: DOM Level 2
757 */
PHP_METHOD(DOMElement,setAttributeNS)758 PHP_METHOD(DOMElement, setAttributeNS)
759 {
760 zval *id;
761 xmlNodePtr elemp, nodep = NULL;
762 xmlNsPtr nsptr;
763 xmlAttr *attr;
764 size_t uri_len = 0, name_len = 0, value_len = 0;
765 char *uri, *name, *value;
766 char *localname = NULL, *prefix = NULL;
767 dom_object *intern;
768 int errorcode = 0, stricterror, is_xmlns = 0, name_valid;
769
770 id = ZEND_THIS;
771 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!ss", &uri, &uri_len, &name, &name_len, &value, &value_len) == FAILURE) {
772 RETURN_THROWS();
773 }
774
775 if (name_len == 0) {
776 zend_argument_value_error(2, "cannot be empty");
777 RETURN_THROWS();
778 }
779
780 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
781
782 stricterror = dom_get_strict_error(intern->document);
783
784 errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
785
786 if (errorcode == 0) {
787 if (uri_len > 0) {
788 nodep = (xmlNodePtr) xmlHasNsProp(elemp, (xmlChar *) localname, (xmlChar *) uri);
789 if (nodep != NULL && nodep->type != XML_ATTRIBUTE_DECL) {
790 node_list_unlink(nodep->children);
791 }
792
793 if ((xmlStrEqual((xmlChar *) prefix, (xmlChar *)"xmlns") ||
794 (prefix == NULL && xmlStrEqual((xmlChar *) localname, (xmlChar *)"xmlns"))) &&
795 xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) {
796 is_xmlns = 1;
797 if (prefix == NULL) {
798 nsptr = dom_get_nsdecl(elemp, NULL);
799 } else {
800 nsptr = dom_get_nsdecl(elemp, (xmlChar *)localname);
801 }
802 } else {
803 nsptr = xmlSearchNsByHref(elemp->doc, elemp, (xmlChar *)uri);
804 if (nsptr && nsptr->prefix == NULL) {
805 xmlNsPtr tmpnsptr;
806
807 tmpnsptr = nsptr->next;
808 while (tmpnsptr) {
809 if ((tmpnsptr->prefix != NULL) && (tmpnsptr->href != NULL) &&
810 (xmlStrEqual(tmpnsptr->href, (xmlChar *) uri))) {
811 nsptr = tmpnsptr;
812 break;
813 }
814 tmpnsptr = tmpnsptr->next;
815 }
816 if (tmpnsptr == NULL) {
817 nsptr = dom_get_ns_resolve_prefix_conflict(elemp, (const char *) nsptr->href);
818 }
819 }
820 }
821
822 if (nsptr == NULL) {
823 if (is_xmlns == 1) {
824 xmlNewNs(elemp, (xmlChar *)value, prefix == NULL ? NULL : (xmlChar *)localname);
825 } else {
826 nsptr = dom_get_ns(elemp, uri, &errorcode, prefix);
827 }
828 xmlReconciliateNs(elemp->doc, elemp);
829 } else {
830 if (is_xmlns == 1) {
831 if (nsptr->href) {
832 xmlFree((xmlChar *) nsptr->href);
833 }
834 nsptr->href = xmlStrdup((xmlChar *)value);
835 }
836 }
837
838 if (errorcode == 0 && is_xmlns == 0) {
839 xmlSetNsProp(elemp, nsptr, (xmlChar *)localname, (xmlChar *)value);
840 }
841 } else {
842 name_valid = xmlValidateName((xmlChar *) localname, 0);
843 if (name_valid != 0) {
844 errorcode = INVALID_CHARACTER_ERR;
845 stricterror = 1;
846 } else {
847 attr = xmlHasProp(elemp, (xmlChar *)localname);
848 if (attr != NULL && attr->type != XML_ATTRIBUTE_DECL) {
849 node_list_unlink(attr->children);
850 }
851 xmlSetProp(elemp, (xmlChar *)localname, (xmlChar *)value);
852 }
853 }
854 }
855
856 xmlFree(localname);
857 if (prefix != NULL) {
858 xmlFree(prefix);
859 }
860
861 if (errorcode != 0) {
862 php_dom_throw_error(errorcode, stricterror);
863 }
864
865 RETURN_NULL();
866 }
867 /* }}} end dom_element_set_attribute_ns */
868
dom_remove_eliminated_ns_single_element(xmlNodePtr node,xmlNsPtr eliminatedNs)869 static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs)
870 {
871 ZEND_ASSERT(node->type == XML_ELEMENT_NODE);
872 if (node->ns == eliminatedNs) {
873 node->ns = NULL;
874 }
875
876 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
877 if (attr->ns == eliminatedNs) {
878 attr->ns = NULL;
879 }
880 }
881 }
882
dom_remove_eliminated_ns(xmlNodePtr node,xmlNsPtr eliminatedNs)883 static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs)
884 {
885 dom_remove_eliminated_ns_single_element(node, eliminatedNs);
886
887 xmlNodePtr base = node;
888 node = node->children;
889 while (node != NULL) {
890 ZEND_ASSERT(node != base);
891
892 if (node->type == XML_ELEMENT_NODE) {
893 dom_remove_eliminated_ns_single_element(node, eliminatedNs);
894
895 if (node->children) {
896 node = node->children;
897 continue;
898 }
899 }
900
901 if (node->next) {
902 node = node->next;
903 } else {
904 /* Go upwards, until we find a parent node with a next sibling, or until we hit the base. */
905 do {
906 node = node->parent;
907 if (node == base) {
908 return;
909 }
910 } while (node->next == NULL);
911 node = node->next;
912 }
913 }
914 }
915
dom_eliminate_ns(xmlNodePtr nodep,xmlNsPtr nsptr)916 static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr)
917 {
918 if (nsptr->href != NULL) {
919 xmlFree((char *) nsptr->href);
920 nsptr->href = NULL;
921 }
922 if (nsptr->prefix != NULL) {
923 xmlFree((char *) nsptr->prefix);
924 nsptr->prefix = NULL;
925 }
926
927 /* Remove it from the list and move it to the old ns list */
928 xmlNsPtr current_ns = nodep->nsDef;
929 if (current_ns == nsptr) {
930 nodep->nsDef = nsptr->next;
931 } else {
932 do {
933 if (current_ns->next == nsptr) {
934 current_ns->next = nsptr->next;
935 break;
936 }
937 current_ns = current_ns->next;
938 } while (current_ns != NULL);
939 }
940 nsptr->next = NULL;
941 php_libxml_set_old_ns(nodep->doc, nsptr);
942
943 dom_remove_eliminated_ns(nodep, nsptr);
944 }
945
946 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS
947 Since: DOM Level 2
948 */
PHP_METHOD(DOMElement,removeAttributeNS)949 PHP_METHOD(DOMElement, removeAttributeNS)
950 {
951 zval *id;
952 xmlNode *nodep;
953 xmlAttr *attrp;
954 xmlNsPtr nsptr;
955 dom_object *intern;
956 size_t name_len, uri_len;
957 char *name, *uri;
958
959 id = ZEND_THIS;
960 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
961 RETURN_THROWS();
962 }
963
964 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
965
966 attrp = xmlHasNsProp(nodep, (xmlChar *)name, (xmlChar *)uri);
967
968 nsptr = dom_get_nsdecl(nodep, (xmlChar *)name);
969 if (nsptr != NULL) {
970 if (xmlStrEqual((xmlChar *)uri, nsptr->href)) {
971 dom_eliminate_ns(nodep, nsptr);
972 } else {
973 RETURN_NULL();
974 }
975 }
976
977 if (attrp && attrp->type != XML_ATTRIBUTE_DECL) {
978 if (php_dom_object_get_data((xmlNodePtr) attrp) == NULL) {
979 node_list_unlink(attrp->children);
980 xmlUnlinkNode((xmlNodePtr) attrp);
981 xmlFreeProp(attrp);
982 } else {
983 xmlUnlinkNode((xmlNodePtr) attrp);
984 }
985 }
986
987 RETURN_NULL();
988 }
989 /* }}} end dom_element_remove_attribute_ns */
990
991 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS
992 Since: DOM Level 2
993 */
PHP_METHOD(DOMElement,getAttributeNodeNS)994 PHP_METHOD(DOMElement, getAttributeNodeNS)
995 {
996 zval *id;
997 xmlNodePtr elemp;
998 xmlAttrPtr attrp;
999 dom_object *intern;
1000 size_t uri_len, name_len;
1001 int ret;
1002 char *uri, *name;
1003
1004 id = ZEND_THIS;
1005 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1006 RETURN_THROWS();
1007 }
1008
1009 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1010
1011 attrp = xmlHasNsProp(elemp, (xmlChar *)name, (xmlChar *)uri);
1012
1013 if (attrp == NULL) {
1014 if (xmlStrEqual((xmlChar *) uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) {
1015 xmlNsPtr nsptr;
1016 nsptr = dom_get_nsdecl(elemp, (xmlChar *)name);
1017 if (nsptr != NULL) {
1018 /* Keep parent alive, because we're a fake child. */
1019 GC_ADDREF(&intern->std);
1020 (void) php_dom_create_fake_namespace_decl(elemp, nsptr, return_value, intern);
1021 } else {
1022 RETURN_NULL();
1023 }
1024 } else {
1025 RETURN_NULL();
1026 }
1027 } else {
1028 DOM_RET_OBJ((xmlNodePtr) attrp, &ret, intern);
1029 }
1030
1031 }
1032 /* }}} end dom_element_get_attribute_node_ns */
1033
1034 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS
1035 Since: DOM Level 2
1036 */
PHP_METHOD(DOMElement,setAttributeNodeNS)1037 PHP_METHOD(DOMElement, setAttributeNodeNS)
1038 {
1039 zval *id, *node;
1040 xmlNode *nodep;
1041 xmlNs *nsp;
1042 xmlAttr *attrp, *existattrp = NULL;
1043 dom_object *intern, *attrobj, *oldobj;
1044 int ret;
1045
1046 id = ZEND_THIS;
1047 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) {
1048 RETURN_THROWS();
1049 }
1050
1051 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1052 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
1053
1054 /* ZPP Guarantees that a DOMAttr class is given, as it is converted to a xmlAttr
1055 * to pass to libxml (see http://www.xmlsoft.org/html/libxml-tree.html#xmlAttr)
1056 * if it is not of type XML_ATTRIBUTE_NODE it indicates a bug somewhere */
1057 ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
1058
1059 if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) {
1060 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
1061 RETURN_FALSE;
1062 }
1063
1064 nsp = attrp->ns;
1065 if (nsp != NULL) {
1066 existattrp = xmlHasNsProp(nodep, attrp->name, nsp->href);
1067 } else {
1068 existattrp = xmlHasProp(nodep, attrp->name);
1069 }
1070
1071 if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
1072 if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
1073 ((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
1074 {
1075 RETURN_NULL();
1076 }
1077 xmlUnlinkNode((xmlNodePtr) existattrp);
1078 }
1079
1080 if (attrp->parent != NULL) {
1081 xmlUnlinkNode((xmlNodePtr) attrp);
1082 }
1083
1084 if (attrp->doc == NULL && nodep->doc != NULL) {
1085 attrobj->document = intern->document;
1086 php_libxml_increment_doc_ref((php_libxml_node_object *)attrobj, NULL);
1087 }
1088
1089 xmlAddChild(nodep, (xmlNodePtr) attrp);
1090
1091 /* Returns old property if removed otherwise NULL */
1092 if (existattrp != NULL) {
1093 DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern);
1094 } else {
1095 RETVAL_NULL();
1096 }
1097
1098 }
1099 /* }}} end dom_element_set_attribute_node_ns */
1100
1101 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942
1102 Since: DOM Level 2
1103 */
PHP_METHOD(DOMElement,getElementsByTagNameNS)1104 PHP_METHOD(DOMElement, getElementsByTagNameNS)
1105 {
1106 size_t uri_len, name_len;
1107 dom_object *intern, *namednode;
1108 char *uri, *name;
1109
1110 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1111 RETURN_THROWS();
1112 }
1113
1114 DOM_GET_THIS_INTERN(intern);
1115
1116 php_dom_create_iterator(return_value, DOM_NODELIST);
1117 namednode = Z_DOMOBJ_P(return_value);
1118 dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
1119
1120 }
1121 /* }}} end dom_element_get_elements_by_tag_name_ns */
1122
1123 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr
1124 Since: DOM Level 2
1125 */
PHP_METHOD(DOMElement,hasAttribute)1126 PHP_METHOD(DOMElement, hasAttribute)
1127 {
1128 zval *id;
1129 xmlNode *nodep;
1130 dom_object *intern;
1131 char *name;
1132 size_t name_len;
1133 xmlNodePtr attr;
1134
1135 id = ZEND_THIS;
1136 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
1137 RETURN_THROWS();
1138 }
1139
1140 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1141
1142 attr = dom_get_dom1_attribute(nodep, (xmlChar *)name);
1143 if (attr == NULL) {
1144 RETURN_FALSE;
1145 } else {
1146 RETURN_TRUE;
1147 }
1148 }
1149 /* }}} end dom_element_has_attribute */
1150
1151 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS
1152 Since: DOM Level 2
1153 */
PHP_METHOD(DOMElement,hasAttributeNS)1154 PHP_METHOD(DOMElement, hasAttributeNS)
1155 {
1156 zval *id;
1157 xmlNodePtr elemp;
1158 xmlNs *nsp;
1159 dom_object *intern;
1160 size_t uri_len, name_len;
1161 char *uri, *name;
1162 xmlChar *value;
1163
1164 id = ZEND_THIS;
1165 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1166 RETURN_THROWS();
1167 }
1168
1169 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1170
1171 value = xmlGetNsProp(elemp, (xmlChar *)name, (xmlChar *)uri);
1172
1173 if (value != NULL) {
1174 xmlFree(value);
1175 RETURN_TRUE;
1176 } else {
1177 if (xmlStrEqual((xmlChar *)uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) {
1178 nsp = dom_get_nsdecl(elemp, (xmlChar *)name);
1179 if (nsp != NULL) {
1180 RETURN_TRUE;
1181 }
1182 }
1183 }
1184
1185 RETURN_FALSE;
1186 }
1187 /* }}} end dom_element_has_attribute_ns */
1188
php_set_attribute_id(xmlAttrPtr attrp,bool is_id)1189 static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id) /* {{{ */
1190 {
1191 if (is_id == 1 && attrp->atype != XML_ATTRIBUTE_ID) {
1192 xmlChar *id_val;
1193
1194 id_val = xmlNodeListGetString(attrp->doc, attrp->children, 1);
1195 if (id_val != NULL) {
1196 xmlAddID(NULL, attrp->doc, id_val, attrp);
1197 xmlFree(id_val);
1198 }
1199 } else if (is_id == 0 && attrp->atype == XML_ATTRIBUTE_ID) {
1200 xmlRemoveID(attrp->doc, attrp);
1201 attrp->atype = 0;
1202 }
1203 }
1204 /* }}} */
1205
1206 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr
1207 Since: DOM Level 3
1208 */
PHP_METHOD(DOMElement,setIdAttribute)1209 PHP_METHOD(DOMElement, setIdAttribute)
1210 {
1211 zval *id;
1212 xmlNode *nodep;
1213 xmlAttrPtr attrp;
1214 dom_object *intern;
1215 char *name;
1216 size_t name_len;
1217 bool is_id;
1218
1219 id = ZEND_THIS;
1220 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sb", &name, &name_len, &is_id) == FAILURE) {
1221 RETURN_THROWS();
1222 }
1223
1224 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1225
1226 attrp = xmlHasNsProp(nodep, (xmlChar *)name, NULL);
1227 if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1228 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1229 } else {
1230 php_set_attribute_id(attrp, is_id);
1231 }
1232
1233 RETURN_NULL();
1234 }
1235 /* }}} end dom_element_set_id_attribute */
1236
1237 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS
1238 Since: DOM Level 3
1239 */
PHP_METHOD(DOMElement,setIdAttributeNS)1240 PHP_METHOD(DOMElement, setIdAttributeNS)
1241 {
1242 zval *id;
1243 xmlNodePtr elemp;
1244 xmlAttrPtr attrp;
1245 dom_object *intern;
1246 size_t uri_len, name_len;
1247 char *uri, *name;
1248 bool is_id;
1249
1250 id = ZEND_THIS;
1251 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssb", &uri, &uri_len, &name, &name_len, &is_id) == FAILURE) {
1252 RETURN_THROWS();
1253 }
1254
1255 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1256
1257 attrp = xmlHasNsProp(elemp, (xmlChar *)name, (xmlChar *)uri);
1258 if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1259 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1260 } else {
1261 php_set_attribute_id(attrp, is_id);
1262 }
1263
1264 RETURN_NULL();
1265 }
1266 /* }}} end dom_element_set_id_attribute_ns */
1267
1268 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode
1269 Since: DOM Level 3
1270 */
PHP_METHOD(DOMElement,setIdAttributeNode)1271 PHP_METHOD(DOMElement, setIdAttributeNode)
1272 {
1273 zval *id, *node;
1274 xmlNode *nodep;
1275 xmlAttrPtr attrp;
1276 dom_object *intern, *attrobj;
1277 bool is_id;
1278
1279 id = ZEND_THIS;
1280 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, dom_attr_class_entry, &is_id) == FAILURE) {
1281 RETURN_THROWS();
1282 }
1283
1284 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1285 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
1286
1287 if (attrp->parent != nodep) {
1288 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1289 } else {
1290 php_set_attribute_id(attrp, is_id);
1291 }
1292
1293 RETURN_NULL();
1294 }
1295 /* }}} end dom_element_set_id_attribute_node */
1296
1297 /* {{{ URL:
1298 Since:
1299 */
PHP_METHOD(DOMElement,remove)1300 PHP_METHOD(DOMElement, remove)
1301 {
1302 dom_object *intern;
1303
1304 if (zend_parse_parameters_none() == FAILURE) {
1305 RETURN_THROWS();
1306 }
1307
1308 DOM_GET_THIS_INTERN(intern);
1309
1310 dom_child_node_remove(intern);
1311 }
1312 /* }}} end DOMElement::remove */
1313
PHP_METHOD(DOMElement,after)1314 PHP_METHOD(DOMElement, after)
1315 {
1316 uint32_t argc = 0;
1317 zval *args;
1318 dom_object *intern;
1319
1320 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1321 RETURN_THROWS();
1322 }
1323
1324 DOM_GET_THIS_INTERN(intern);
1325
1326 dom_parent_node_after(intern, args, argc);
1327 }
1328
PHP_METHOD(DOMElement,before)1329 PHP_METHOD(DOMElement, before)
1330 {
1331 uint32_t argc = 0;
1332 zval *args;
1333 dom_object *intern;
1334
1335 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1336 RETURN_THROWS();
1337 }
1338
1339 DOM_GET_THIS_INTERN(intern);
1340
1341 dom_parent_node_before(intern, args, argc);
1342 }
1343
1344 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
1345 Since: DOM Living Standard (DOM4)
1346 */
PHP_METHOD(DOMElement,append)1347 PHP_METHOD(DOMElement, append)
1348 {
1349 uint32_t argc = 0;
1350 zval *args;
1351 dom_object *intern;
1352
1353 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1354 RETURN_THROWS();
1355 }
1356
1357 DOM_GET_THIS_INTERN(intern);
1358
1359 dom_parent_node_append(intern, args, argc);
1360 }
1361 /* }}} end DOMElement::append */
1362
1363 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
1364 Since: DOM Living Standard (DOM4)
1365 */
PHP_METHOD(DOMElement,prepend)1366 PHP_METHOD(DOMElement, prepend)
1367 {
1368 uint32_t argc = 0;
1369 zval *args;
1370 dom_object *intern;
1371
1372 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1373 RETURN_THROWS();
1374 }
1375
1376 DOM_GET_THIS_INTERN(intern);
1377
1378 dom_parent_node_prepend(intern, args, argc);
1379 }
1380 /* }}} end DOMElement::prepend */
1381
1382 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1383 Since: DOM Living Standard (DOM4)
1384 */
PHP_METHOD(DOMElement,replaceWith)1385 PHP_METHOD(DOMElement, replaceWith)
1386 {
1387 uint32_t argc = 0;
1388 zval *args;
1389 dom_object *intern;
1390
1391 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1392 RETURN_THROWS();
1393 }
1394
1395 DOM_GET_THIS_INTERN(intern);
1396
1397 dom_child_replace_with(intern, args, argc);
1398 }
1399 /* }}} end DOMElement::prepend */
1400
1401 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1402 Since:
1403 */
PHP_METHOD(DOMElement,replaceChildren)1404 PHP_METHOD(DOMElement, replaceChildren)
1405 {
1406 uint32_t argc = 0;
1407 zval *args;
1408 dom_object *intern;
1409
1410 if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1411 RETURN_THROWS();
1412 }
1413
1414 DOM_GET_THIS_INTERN(intern);
1415
1416 dom_parent_node_replace_children(intern, args, argc);
1417 }
1418 /* }}} */
1419
1420 #define INSERT_ADJACENT_RES_FAILED ((void*) -1)
1421
dom_insert_adjacent(const zend_string * where,xmlNodePtr thisp,dom_object * this_intern,xmlNodePtr otherp)1422 static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp)
1423 {
1424 if (zend_string_equals_literal_ci(where, "beforebegin")) {
1425 if (thisp->parent == NULL) {
1426 return NULL;
1427 }
1428 if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1429 php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1430 return INSERT_ADJACENT_RES_FAILED;
1431 }
1432 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1433 return INSERT_ADJACENT_RES_FAILED;
1434 }
1435 otherp = xmlAddPrevSibling(thisp, otherp);
1436 } else if (zend_string_equals_literal_ci(where, "afterbegin")) {
1437 if (dom_hierarchy(thisp, otherp) == FAILURE) {
1438 php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1439 return INSERT_ADJACENT_RES_FAILED;
1440 }
1441 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1442 return INSERT_ADJACENT_RES_FAILED;
1443 }
1444 if (thisp->children == NULL) {
1445 otherp = xmlAddChild(thisp, otherp);
1446 } else {
1447 otherp = xmlAddPrevSibling(thisp->children, otherp);
1448 }
1449 } else if (zend_string_equals_literal_ci(where, "beforeend")) {
1450 if (dom_hierarchy(thisp, otherp) == FAILURE) {
1451 php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1452 return INSERT_ADJACENT_RES_FAILED;
1453 }
1454 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1455 return INSERT_ADJACENT_RES_FAILED;
1456 }
1457 otherp = xmlAddChild(thisp, otherp);
1458 } else if (zend_string_equals_literal_ci(where, "afterend")) {
1459 if (thisp->parent == NULL) {
1460 return NULL;
1461 }
1462 if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1463 php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1464 return INSERT_ADJACENT_RES_FAILED;
1465 }
1466 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1467 return INSERT_ADJACENT_RES_FAILED;
1468 }
1469 otherp = xmlAddNextSibling(thisp, otherp);
1470 } else {
1471 php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document));
1472 return INSERT_ADJACENT_RES_FAILED;
1473 }
1474 dom_reconcile_ns(thisp->doc, otherp);
1475 return otherp;
1476 }
1477
1478 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
1479 Since:
1480 */
PHP_METHOD(DOMElement,insertAdjacentElement)1481 PHP_METHOD(DOMElement, insertAdjacentElement)
1482 {
1483 zend_string *where;
1484 zval *element_zval, *id;
1485 xmlNodePtr thisp, otherp;
1486 dom_object *this_intern, *other_intern;
1487 int ret;
1488
1489 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) == FAILURE) {
1490 RETURN_THROWS();
1491 }
1492
1493 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1494 DOM_GET_OBJ(otherp, element_zval, xmlNodePtr, other_intern);
1495
1496 xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1497 if (result == NULL) {
1498 RETURN_NULL();
1499 } else if (result != INSERT_ADJACENT_RES_FAILED) {
1500 DOM_RET_OBJ(otherp, &ret, other_intern);
1501 } else {
1502 RETURN_THROWS();
1503 }
1504 }
1505 /* }}} end DOMElement::insertAdjacentElement */
1506
1507 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
1508 Since:
1509 */
PHP_METHOD(DOMElement,insertAdjacentText)1510 PHP_METHOD(DOMElement, insertAdjacentText)
1511 {
1512 zend_string *where, *data;
1513 dom_object *this_intern;
1514 zval *id;
1515 xmlNodePtr thisp;
1516
1517 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &where, &data) == FAILURE) {
1518 RETURN_THROWS();
1519 }
1520
1521 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1522
1523 if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(data)))) {
1524 zend_argument_value_error(2, "is too long");
1525 RETURN_THROWS();
1526 }
1527
1528 xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data));
1529 xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1530 if (result == NULL || result == INSERT_ADJACENT_RES_FAILED) {
1531 xmlFreeNode(otherp);
1532 }
1533 }
1534 /* }}} end DOMElement::insertAdjacentText */
1535 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
1536 Since:
1537 */
PHP_METHOD(DOMElement,toggleAttribute)1538 PHP_METHOD(DOMElement, toggleAttribute)
1539 {
1540 char *qname, *qname_tmp = NULL;
1541 size_t qname_length;
1542 bool force, force_is_null = true;
1543 xmlNodePtr thisp;
1544 zval *id;
1545 dom_object *intern;
1546 bool retval;
1547
1548 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b!", &qname, &qname_length, &force, &force_is_null) == FAILURE) {
1549 RETURN_THROWS();
1550 }
1551
1552 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1553
1554 /* Step 1 */
1555 if (xmlValidateName((xmlChar *) qname, 0) != 0) {
1556 php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
1557 RETURN_THROWS();
1558 }
1559
1560 /* Step 2 */
1561 if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE && (thisp->ns == NULL || xmlStrEqual(thisp->ns->href, (const xmlChar *) "http://www.w3.org/1999/xhtml"))) {
1562 qname_tmp = zend_str_tolower_dup_ex(qname, qname_length);
1563 if (qname_tmp != NULL) {
1564 qname = qname_tmp;
1565 }
1566 }
1567
1568 /* Step 3 */
1569 xmlNodePtr attribute = dom_get_dom1_attribute(thisp, (xmlChar *) qname);
1570
1571 /* Step 4 */
1572 if (attribute == NULL) {
1573 /* Step 4.1 */
1574 if (force_is_null || force) {
1575 /* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour.
1576 * It follows the same rules when you'd manually add an attribute using the other APIs. */
1577 int len;
1578 const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
1579 if (split == NULL || strncmp(qname, "xmlns:", len + 1) != 0) {
1580 /* unqualified name, or qualified name with no xml namespace declaration */
1581 dom_create_attribute(thisp, qname, "");
1582 } else {
1583 /* qualified name with xml namespace declaration */
1584 xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1));
1585 }
1586 retval = true;
1587 goto out;
1588 }
1589 /* Step 4.2 */
1590 retval = false;
1591 goto out;
1592 }
1593
1594 /* Step 5 */
1595 if (force_is_null || !force) {
1596 dom_remove_attribute(thisp, attribute);
1597 retval = false;
1598 goto out;
1599 }
1600
1601 /* Step 6 */
1602 retval = true;
1603
1604 out:
1605 if (qname_tmp) {
1606 efree(qname_tmp);
1607 }
1608 RETURN_BOOL(retval);
1609 }
1610 /* }}} end DOMElement::prepend */
1611
1612 #endif
1613