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