php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
element.c
Go to the documentation of this file.
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 "zend_enum.h"
25#include "php_dom.h"
26#include "namespace_compat.h"
27#include "private_data.h"
28#include "internal_helpers.h"
29#include "dom_properties.h"
30#include "token_list.h"
31
32/*
33* class DOMElement extends DOMNode
34*
35* URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-745549614
36* Since:
37*/
38
39/* {{{ */
40PHP_METHOD(DOMElement, __construct)
41{
42 xmlNodePtr nodep = NULL, oldnode = NULL;
43 dom_object *intern;
44 char *name, *value = NULL, *uri = NULL;
45 char *localname = NULL, *prefix = NULL;
46 int errorcode = 0;
47 size_t name_len, value_len = 0, uri_len = 0;
48 int name_valid;
49 xmlNsPtr nsptr = NULL;
50
51 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s", &name, &name_len, &value, &value_len, &uri, &uri_len) == FAILURE) {
53 }
54
55 name_valid = xmlValidateName(BAD_CAST name, 0);
56 if (name_valid != 0) {
59 }
60
61 /* Namespace logic is separate and only when uri passed in to insure no BC breakage */
62 if (uri_len > 0) {
63 errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
64 if (errorcode == 0) {
65 nodep = xmlNewNode (NULL, BAD_CAST localname);
66 if (nodep != NULL && uri != NULL) {
67 nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
68 xmlSetNs(nodep, nsptr);
69 }
70 }
71 xmlFree(localname);
72 if (prefix != NULL) {
73 xmlFree(prefix);
74 }
75 if (errorcode != 0) {
76 if (nodep != NULL) {
77 xmlFreeNode(nodep);
78 }
79 php_dom_throw_error(errorcode, true);
81 }
82 } else {
83 /* If you don't pass a namespace uri, then you can't set a prefix */
84 localname = (char *) xmlSplitQName2(BAD_CAST name, (xmlChar **) &prefix);
85 if (prefix != NULL) {
86 xmlFree(localname);
87 xmlFree(prefix);
90 }
91 nodep = xmlNewNode(NULL, BAD_CAST name);
92 }
93
94 if (!nodep) {
97 }
98
99 if (value_len > 0) {
100 xmlNodeSetContentLen(nodep, BAD_CAST value, value_len);
101 }
102
103 intern = Z_DOMOBJ_P(ZEND_THIS);
104 oldnode = dom_object_get_node(intern);
105 if (oldnode != NULL) {
106 php_libxml_node_decrement_resource((php_libxml_node_object *)intern);
107 }
108 php_libxml_increment_node_ptr((php_libxml_node_object *)intern, nodep, (void *)intern);
109}
110/* }}} end DOMElement::__construct */
111
112/* {{{ tagName string
113readonly=yes
114URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-104682815
115Modern spec URL: https://dom.spec.whatwg.org/#dom-element-tagname
116Since:
117*/
119{
120 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
121
122 bool uppercase = php_dom_follow_spec_intern(obj) && php_dom_ns_is_html_and_document_is_html(nodep);
123
124 zend_string *result = dom_node_get_node_name_attribute_or_element((const xmlNode *) nodep, uppercase);
126
127 return SUCCESS;
128}
129
130/* }}} */
131
132static zend_result dom_element_reflected_attribute_read(dom_object *obj, zval *retval, const char *name)
133{
134 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
135
136 xmlChar *content = xmlGetNoNsProp(nodep, (const xmlChar *) name);
137 if (content == NULL) {
139 return SUCCESS;
140 }
141
142 ZVAL_STRING(retval, (const char *) content);
143 xmlFree(content);
144
145 return SUCCESS;
146}
147
148static xmlAttrPtr dom_element_reflected_attribute_write(dom_object *obj, zval *newval, const char *name)
149{
150 xmlNode *nodep = dom_object_get_node(obj);
151
152 if (nodep == NULL) {
154 return NULL;
155 }
156
157 /* Typed property, so it is a string already */
158 ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
159 return xmlSetNsProp(nodep, NULL, (const xmlChar *) name, (const xmlChar *) Z_STRVAL_P(newval));
160}
161
162/* {{{ className string
163URL: https://dom.spec.whatwg.org/#dom-element-classname
164Since:
165*/
167{
168 return dom_element_reflected_attribute_read(obj, retval, "class");
169}
170
172{
173 if (dom_element_reflected_attribute_write(obj, newval, "class")) {
174 return SUCCESS;
175 }
176 return FAILURE;
177}
178/* }}} */
179
180/* {{{ classList TokenList
181URL: https://dom.spec.whatwg.org/#dom-element-classlist
182*/
184{
185 const uint32_t PROP_INDEX = 0;
186
187#if ZEND_DEBUG
188 zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false);
190 zend_string_release_ex(class_list_str, false);
191 ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX);
192#endif
193
194 zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX);
195 if (Z_ISUNDEF_P(cached_token_list)) {
196 object_init_ex(cached_token_list, dom_token_list_class_entry);
197 dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list));
198 dom_token_list_ctor(intern, obj);
199 }
200
201 ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_token_list));
202
203 return SUCCESS;
204}
205/* }}} */
206
207/* {{{ id string
208URL: https://dom.spec.whatwg.org/#dom-element-id
209Since:
210*/
212{
213 return dom_element_reflected_attribute_read(obj, retval, "id");
214}
215
216static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id, php_libxml_ref_obj *document);
217
219{
220 xmlAttrPtr attr = dom_element_reflected_attribute_write(obj, newval, "id");
221 if (!attr) {
222 return FAILURE;
223 }
224 php_set_attribute_id(attr, true, obj->document);
225 return SUCCESS;
226}
227/* }}} */
228
229/* {{{ schemaTypeInfo typeinfo
230readonly=yes
231URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Element-schemaTypeInfo
232Since: DOM Level 3
233*/
235{
237 return SUCCESS;
238}
239
240/* }}} */
241
242/* Note: the object returned is not necessarily a node, but can be an attribute or a namespace declaration. */
243static xmlNodePtr dom_get_attribute_or_nsdecl(dom_object *intern, xmlNodePtr elem, const xmlChar *name, size_t name_len) /* {{{ */
244{
245 if (!php_dom_follow_spec_intern(intern)) {
246 int len;
247 const xmlChar *nqname = xmlSplitQName3(name, &len);
248
249 if (nqname != NULL) {
250 xmlNsPtr ns;
251 if (strncmp((const char *) name, "xmlns:", len + 1) == 0) {
252 ns = elem->nsDef;
253 while (ns) {
254 if (xmlStrEqual(ns->prefix, nqname)) {
255 break;
256 }
257 ns = ns->next;
258 }
259 return (xmlNodePtr)ns;
260 }
261 xmlChar *prefix = xmlStrndup(name, len);
262 ns = xmlSearchNs(elem->doc, elem, prefix);
263 if (prefix != NULL) {
264 xmlFree(prefix);
265 }
266 if (ns != NULL) {
267 return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href);
268 }
269 } else {
270 if (xmlStrEqual(name, BAD_CAST "xmlns")) {
271 xmlNsPtr nsPtr = elem->nsDef;
272 while (nsPtr) {
273 if (nsPtr->prefix == NULL) {
274 return (xmlNodePtr)nsPtr;
275 }
276 nsPtr = nsPtr->next;
277 }
278 return NULL;
279 }
280 }
281 return (xmlNodePtr) xmlHasNsProp(elem, name, NULL);
282 } else {
283 return (xmlNodePtr) php_dom_get_attribute_node(elem, name, name_len);
284 }
285}
286/* }}} */
287
288/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9
289Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattribute
290Since:
291*/
292PHP_METHOD(DOMElement, getAttribute)
293{
294 zval *id;
295 xmlNode *nodep;
296 char *name;
297 xmlChar *value = NULL;
298 dom_object *intern;
299 xmlNodePtr attr;
300 size_t name_len;
301 bool should_free = false;
302
303 id = ZEND_THIS;
304 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
306 }
307
308 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
309
310 attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
311 if (attr) {
312 switch (attr->type) {
314 value = xmlNodeListGetString(attr->doc, attr->children, 1);
315 should_free = true;
316 break;
317 case XML_NAMESPACE_DECL:
318 value = BAD_CAST ((xmlNsPtr)attr)->href;
319 should_free = false;
320 break;
321 default:
322 value = BAD_CAST ((xmlAttributePtr)attr)->defaultValue;
323 should_free = false;
324 }
325 }
326
327 if (value == NULL) {
328 if (php_dom_follow_spec_intern(intern)) {
329 RETURN_NULL();
330 }
332 } else {
333 RETVAL_STRING((char *)value);
334 if (should_free) {
335 xmlFree(value);
336 }
337 }
338}
339/* }}} end dom_element_get_attribute */
340
341/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-getattributenames
342Since:
343*/
344PHP_METHOD(DOMElement, getAttributeNames)
345{
346 zval *id;
347 xmlNode *nodep;
348 dom_object *intern;
349 zval tmp;
350
353 }
354
355 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
356
360
361 if (!php_dom_follow_spec_intern(intern)) {
362 for (xmlNsPtr nsptr = nodep->nsDef; nsptr; nsptr = nsptr->next) {
363 const char *prefix = (const char *) nsptr->prefix;
364 if (prefix == NULL) {
365 ZVAL_STRING(&tmp, "xmlns");
366 } else {
367 ZVAL_NEW_STR(&tmp, dom_node_concatenated_name_helper(strlen(prefix), prefix, strlen("xmlns"), (const char *) "xmlns"));
368 }
370 }
371 }
372
373 for (xmlAttrPtr attr = nodep->properties; attr; attr = attr->next) {
374 ZVAL_NEW_STR(&tmp, dom_node_get_node_name_attribute_or_element((const xmlNode *) attr, false));
376 }
377}
378/* }}} end DOMElement::getAttributeNames() */
379
380static xmlNodePtr dom_create_attribute(xmlNodePtr nodep, const char *name, const char* value)
381{
382 if (xmlStrEqual(BAD_CAST name, BAD_CAST "xmlns")) {
383 return (xmlNodePtr) xmlNewNs(nodep, BAD_CAST value, NULL);
384 } else {
385 return (xmlNodePtr) xmlSetProp(nodep, BAD_CAST name, BAD_CAST value);
386 }
387}
388
389static void dom_check_register_attribute_id(xmlAttrPtr attr, php_libxml_ref_obj *document)
390{
391 dom_mark_ids_modified(document);
392
393 if (attr->atype != XML_ATTRIBUTE_ID && attr->doc->type == XML_HTML_DOCUMENT_NODE && attr->ns == NULL && xmlStrEqual(attr->name, BAD_CAST "id")) {
394 /* To respect XML's ID behaviour, we only do this registration for HTML documents. */
395 attr->atype = XML_ATTRIBUTE_ID;
396 }
397}
398
399/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082
400Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattribute
401Since:
402*/
403PHP_METHOD(DOMElement, setAttribute)
404{
405 zval *id;
406 xmlNode *nodep;
407 int name_valid;
408 size_t name_len, value_len;
409 dom_object *intern;
410 char *name, *value;
411
412 id = ZEND_THIS;
413 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &value, &value_len) == FAILURE) {
415 }
416
417 if (name_len == 0) {
420 }
421
422 name_valid = xmlValidateName(BAD_CAST name, 0);
423 if (name_valid != 0) {
426 }
427
428 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
429
430 if (php_dom_follow_spec_intern(intern)) {
431 xmlChar *name_processed = BAD_CAST name;
433 char *lowercase_copy = zend_str_tolower_dup_ex(name, name_len);
434 if (lowercase_copy != NULL) {
435 name_processed = BAD_CAST lowercase_copy;
436 }
437 }
438
439 /* Can't use xmlSetNsProp unconditionally here because that doesn't take into account the qualified name matching... */
440 xmlAttrPtr attr = php_dom_get_attribute_node(nodep, BAD_CAST name, name_len);
441 if (attr != NULL) {
443 dom_remove_all_children((xmlNodePtr) attr);
444 xmlNodePtr node = xmlNewDocText(attr->doc, BAD_CAST value);
445 xmlAddChild((xmlNodePtr) attr, node);
446 } else {
447 attr = xmlSetNsProp(nodep, NULL, name_processed, BAD_CAST value);
448 if (EXPECTED(attr != NULL)) {
449 dom_check_register_attribute_id(attr, intern->document);
450 }
451 }
452
453 if (name_processed != BAD_CAST name) {
454 efree(name_processed);
455 }
456 } else {
457 xmlNodePtr attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
458 if (attr != NULL) {
459 switch (attr->type) {
461 dom_attr_value_will_change(intern, (xmlAttrPtr) attr);
462 node_list_unlink(attr->children);
463 break;
464 case XML_NAMESPACE_DECL:
467 }
468 }
469
470 attr = dom_create_attribute(nodep, name, value);
471 if (!attr) {
472 zend_argument_value_error(1, "must be a valid XML attribute");
474 }
475 if (attr->type == XML_NAMESPACE_DECL) {
477 }
478
479 DOM_RET_OBJ(attr, intern);
480 }
481}
482/* }}} end dom_element_set_attribute */
483
484typedef struct dom_deep_ns_redef_item {
485 xmlNodePtr current_node;
486 xmlNsPtr defined_ns;
487} dom_deep_ns_redef_item;
488
489/* Reconciliation for a *single* namespace, but reconciles *closest* to the subtree needing it. */
490static void dom_deep_ns_redef(xmlNodePtr node, xmlNsPtr ns_to_redefine)
491{
492 size_t worklist_capacity = 128;
493 dom_deep_ns_redef_item *worklist = emalloc(sizeof(dom_deep_ns_redef_item) * worklist_capacity);
494 worklist[0].current_node = node;
495 worklist[0].defined_ns = NULL;
496 size_t worklist_size = 1;
497
498 while (worklist_size > 0) {
499 worklist_size--;
500 dom_deep_ns_redef_item *current_worklist_item = &worklist[worklist_size];
501 ZEND_ASSERT(current_worklist_item->current_node->type == XML_ELEMENT_NODE);
502 xmlNsPtr defined_ns = current_worklist_item->defined_ns;
503
504 if (current_worklist_item->current_node->ns == ns_to_redefine) {
505 if (defined_ns == NULL) {
506 defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
507 }
508 current_worklist_item->current_node->ns = defined_ns;
509 }
510
511 for (xmlAttrPtr attr = current_worklist_item->current_node->properties; attr; attr = attr->next) {
512 if (attr->ns == ns_to_redefine) {
513 if (defined_ns == NULL) {
514 defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
515 }
516 attr->ns = defined_ns;
517 }
518 }
519
520 for (xmlNodePtr child = current_worklist_item->current_node->children; child; child = child->next) {
521 if (child->type != XML_ELEMENT_NODE) {
522 continue;
523 }
524 if (worklist_size == worklist_capacity) {
525 if (UNEXPECTED(worklist_capacity >= SIZE_MAX / 3 * 2 / sizeof(dom_deep_ns_redef_item))) {
526 /* Shouldn't be possible to hit, but checked for safety anyway */
527 goto out;
528 }
529 worklist_capacity = worklist_capacity * 3 / 2;
530 worklist = erealloc(worklist, sizeof(dom_deep_ns_redef_item) * worklist_capacity);
531 }
532 worklist[worklist_size].current_node = child;
533 worklist[worklist_size].defined_ns = defined_ns;
534 worklist_size++;
535 }
536 }
537
538out:
539 efree(worklist);
540}
541
542static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp)
543{
544 ZEND_ASSERT(thisp != NULL);
545 ZEND_ASSERT(attrp != NULL);
546
547 switch (attrp->type) {
549 if (php_dom_object_get_data(attrp) == NULL) {
550 node_list_unlink(attrp->children);
551 xmlUnlinkNode(attrp);
552 xmlFreeProp((xmlAttrPtr)attrp);
553 } else {
554 xmlUnlinkNode(attrp);
555 }
556 break;
557 case XML_NAMESPACE_DECL: {
558 /* They will always be removed, but can be re-added.
559 *
560 * If any reference was left to the namespace, the only effect is that
561 * the definition is potentially moved closer to the element using it.
562 * If no reference was left, it is actually removed. */
563 xmlNsPtr ns = (xmlNsPtr) attrp;
564 if (thisp->nsDef == ns) {
565 thisp->nsDef = ns->next;
566 } else if (thisp->nsDef != NULL) {
567 xmlNsPtr prev = thisp->nsDef;
568 xmlNsPtr cur = prev->next;
569 while (cur) {
570 if (cur == ns) {
571 prev->next = cur->next;
572 break;
573 }
574 prev = cur;
575 cur = cur->next;
576 }
577 } else {
578 /* defensive: attrp not defined in thisp ??? */
579#if ZEND_DEBUG
581#endif
582 break; /* defensive */
583 }
584
585 ns->next = NULL;
586 php_libxml_set_old_ns(thisp->doc, ns); /* note: can't deallocate as it might be referenced by a "fake namespace node" */
587 /* xmlReconciliateNs() redefines at the top of the tree instead of closest to the child, own reconciliation here.
588 * Similarly, the DOM version has other issues too (see dom_libxml_reconcile_ensure_namespaces_are_declared). */
589 dom_deep_ns_redef(thisp, ns);
590
591 break;
592 }
594 }
595 return true;
596}
597
598/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9
599Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattribute
600Since:
601*/
602PHP_METHOD(DOMElement, removeAttribute)
603{
604 xmlNodePtr nodep, attrp;
605 dom_object *intern;
606 size_t name_len;
607 char *name;
608
609 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
611 }
612
613 DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
614
615 attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
616 if (attrp == NULL) {
618 }
619
620 RETURN_BOOL(dom_remove_attribute(nodep, attrp));
621}
622
623PHP_METHOD(Dom_Element, removeAttribute)
624{
625 xmlNodePtr nodep, attrp;
626 dom_object *intern;
627 size_t name_len;
628 char *name;
629
630 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
632 }
633
634 DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
635
636 attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
637 if (attrp != NULL) {
638 dom_remove_attribute(nodep, attrp);
639 }
640}
641/* }}} end dom_element_remove_attribute */
642
643/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8
644Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenode
645Since:
646*/
647PHP_METHOD(DOMElement, getAttributeNode)
648{
649 zval *id;
650 xmlNodePtr nodep, attrp;
651 size_t name_len;
652 dom_object *intern;
653 char *name;
654
655 id = ZEND_THIS;
656 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
658 }
659
660 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
661
662 attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
663 if (attrp == NULL) {
664 if (php_dom_follow_spec_intern(intern)) {
665 RETURN_NULL();
666 }
668 }
669
670 if (attrp->type == XML_NAMESPACE_DECL) {
671 xmlNsPtr original = (xmlNsPtr) attrp;
672 /* Keep parent alive, because we're a fake child. */
673 GC_ADDREF(&intern->std);
674 (void) php_dom_create_fake_namespace_decl(nodep, original, return_value, intern);
675 } else {
676 DOM_RET_OBJ((xmlNodePtr) attrp, intern);
677 }
678}
679/* }}} end dom_element_get_attribute_node */
680
681static void dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAMETERS, bool use_ns, bool modern)
682{
683 zval *id, *node;
684 xmlNode *nodep;
685 xmlNs *nsp;
686 xmlAttr *attrp, *existattrp = NULL;
687 dom_object *intern, *attrobj, *oldobj;
688
689 id = ZEND_THIS;
690 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_get_attr_ce(modern)) == FAILURE) {
692 }
693
694 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
695 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
696
697 /* ZPP Guarantees that a DOMAttr class is given, as it is converted to a xmlAttr
698 * to pass to libxml (see http://www.xmlsoft.org/html/libxml-tree.html#xmlAttr)
699 * if it is not of type XML_ATTRIBUTE_NODE it indicates a bug somewhere */
700 ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
701
702 if (modern) {
703 if (attrp->parent != NULL && attrp->parent != nodep) {
704 php_dom_throw_error(INUSE_ATTRIBUTE_ERR, /* strict */ true);
706 }
707 if (attrp->doc != NULL && attrp->doc != nodep->doc) {
708 php_dom_adopt_node((xmlNodePtr) attrp, intern, nodep->doc);
709 }
710 } else {
711 if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) {
714 }
715 }
716
717 nsp = attrp->ns;
718 if (use_ns && nsp != NULL) {
719 existattrp = xmlHasNsProp(nodep, attrp->name, nsp->href);
720 } else {
721 existattrp = xmlHasProp(nodep, attrp->name);
722 }
723
724 if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
725 if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
726 ((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
727 {
728 RETURN_NULL();
729 }
730 xmlUnlinkNode((xmlNodePtr) existattrp);
731 }
732
733 if (attrp->parent != NULL) {
734 xmlUnlinkNode((xmlNodePtr) attrp);
735 }
736
737 if (attrp->doc == NULL && nodep->doc != NULL && intern->document != NULL) {
739 }
740
741 xmlAddChild(nodep, (xmlNodePtr) attrp);
742 if (!modern) {
743 dom_mark_ids_modified(intern->document);
745 } else {
746 dom_check_register_attribute_id(attrp, intern->document);
747 }
748
749 /* Returns old property if removed otherwise NULL */
750 if (existattrp != NULL) {
751 DOM_RET_OBJ((xmlNodePtr) existattrp, intern);
752 } else {
753 RETURN_NULL();
754 }
755}
756
757/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154
758Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenode
759Since:
760*/
761PHP_METHOD(DOMElement, setAttributeNode)
762{
763 dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ false, /* modern */ false);
764}
765/* }}} end dom_element_set_attribute_node */
766
767/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198
768Since:
769*/
770static void dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
771{
772 zval *node;
773 xmlNode *nodep;
774 xmlAttr *attrp;
775 dom_object *intern, *attrobj;
776
777 if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, node_ce) == FAILURE) {
779 }
780
781 DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
782
783 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
784
785 ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
786
787 if (attrp->parent != nodep) {
790 }
791
792 xmlUnlinkNode((xmlNodePtr) attrp);
793
794 DOM_RET_OBJ((xmlNodePtr) attrp, intern);
795}
796
797PHP_METHOD(DOMElement, removeAttributeNode)
798{
799 dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
800}
801
802PHP_METHOD(Dom_Element, removeAttributeNode)
803{
804 dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_attr_class_entry);
805}
806/* }}} end dom_element_remove_attribute_node */
807
808/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D
809Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagname
810Since:
811*/
812static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, bool modern)
813{
814 size_t name_len;
815 dom_object *intern, *namednode;
816 char *name;
817
818 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &name, &name_len) == FAILURE) {
820 }
821
822 if (name_len > INT_MAX) {
823 zend_argument_value_error(1, "is too long");
825 }
826
827 DOM_GET_THIS_INTERN(intern);
828
829 if (modern) {
831 } else {
833 }
834 namednode = Z_DOMOBJ_P(return_value);
835 dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
836}
837
838PHP_METHOD(DOMElement, getElementsByTagName)
839{
840 dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
841}
842
843PHP_METHOD(Dom_Element, getElementsByTagName)
844{
845 dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
846}
847/* }}} end dom_element_get_elements_by_tag_name */
848
849/* should_free_result must be initialized to false */
850static const xmlChar *dom_get_attribute_ns(dom_object *intern, xmlNodePtr elemp, const char *uri, size_t uri_len, const char *name, bool *should_free_result)
851{
852 bool follow_spec = php_dom_follow_spec_intern(intern);
853 if (follow_spec && uri_len == 0) {
854 uri = NULL;
855 }
856
857 xmlChar *strattr = xmlGetNsProp(elemp, BAD_CAST name, BAD_CAST uri);
858
859 if (strattr != NULL) {
860 *should_free_result = true;
861 return strattr;
862 } else {
863 if (!follow_spec && xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
864 xmlNsPtr nsptr = dom_get_nsdecl(elemp, BAD_CAST name);
865 if (nsptr != NULL) {
866 return nsptr->href;
867 } else {
868 return NULL;
869 }
870 } else {
871 return NULL;
872 }
873 }
874}
875
876/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS
877Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributens
878Since: DOM Level 2
879*/
880PHP_METHOD(DOMElement, getAttributeNS)
881{
882 zval *id;
883 xmlNodePtr elemp;
884 dom_object *intern;
885 size_t uri_len = 0, name_len = 0;
886 char *uri, *name;
887
888 id = ZEND_THIS;
889 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
891 }
892
893 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
894
895 bool should_free_result = false;
896 const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result);
897 if (result == NULL) {
898 if (php_dom_follow_spec_intern(intern)) {
899 RETURN_NULL();
900 }
902 } else {
903 RETVAL_STRING((const char *) result);
904 if (should_free_result) {
905 xmlFree(BAD_CAST result);
906 }
907 }
908}
909/* }}} end dom_element_get_attribute_ns */
910
911static void dom_set_attribute_ns_legacy(dom_object *intern, xmlNodePtr elemp, char *uri, size_t uri_len, char *name, size_t name_len, const char *value)
912{
913 if (name_len == 0) {
915 return;
916 }
917
918 xmlNodePtr nodep = NULL;
919 xmlNsPtr nsptr;
920 xmlAttr *attr;
921 char *localname = NULL, *prefix = NULL;
922 int is_xmlns = 0, name_valid;
923 bool stricterror = dom_get_strict_error(intern->document);
924
925 int errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
926
927 if (errorcode == 0) {
928 dom_mark_ids_modified(intern->document);
929
930 if (uri_len > 0) {
931 nodep = (xmlNodePtr) xmlHasNsProp(elemp, BAD_CAST localname, BAD_CAST uri);
932 if (nodep != NULL && nodep->type != XML_ATTRIBUTE_DECL) {
933 node_list_unlink(nodep->children);
934 }
935
936 if ((xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns") ||
937 (prefix == NULL && xmlStrEqual(BAD_CAST localname, BAD_CAST "xmlns"))) &&
938 xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
939 is_xmlns = 1;
940 if (prefix == NULL) {
941 nsptr = dom_get_nsdecl(elemp, NULL);
942 } else {
943 nsptr = dom_get_nsdecl(elemp, BAD_CAST localname);
944 }
945 } else {
946 nsptr = xmlSearchNsByHref(elemp->doc, elemp, BAD_CAST uri);
947 if (nsptr && nsptr->prefix == NULL) {
948 xmlNsPtr tmpnsptr;
949
950 tmpnsptr = nsptr->next;
951 while (tmpnsptr) {
952 if ((tmpnsptr->prefix != NULL) && (tmpnsptr->href != NULL) &&
953 (xmlStrEqual(tmpnsptr->href, BAD_CAST uri))) {
954 nsptr = tmpnsptr;
955 break;
956 }
957 tmpnsptr = tmpnsptr->next;
958 }
959 if (tmpnsptr == NULL) {
960 nsptr = dom_get_ns_resolve_prefix_conflict(elemp, (const char *) nsptr->href);
961 }
962 }
963 }
964
965 if (nsptr == NULL) {
966 if (is_xmlns == 1) {
967 xmlNewNs(elemp, BAD_CAST value, prefix == NULL ? NULL : BAD_CAST localname);
968 } else {
969 nsptr = dom_get_ns(elemp, uri, &errorcode, prefix);
970 }
971 xmlReconciliateNs(elemp->doc, elemp);
972 } else {
973 if (is_xmlns == 1) {
974 if (nsptr->href) {
975 xmlFree(BAD_CAST nsptr->href);
976 }
977 nsptr->href = xmlStrdup(BAD_CAST value);
978 }
979 }
980
981 if (errorcode == 0 && is_xmlns == 0) {
982 xmlSetNsProp(elemp, nsptr, BAD_CAST localname, BAD_CAST value);
983 }
984 } else {
985 name_valid = xmlValidateName(BAD_CAST localname, 0);
986 if (name_valid != 0) {
987 errorcode = INVALID_CHARACTER_ERR;
988 stricterror = 1;
989 } else {
990 attr = xmlHasProp(elemp, BAD_CAST localname);
991 if (attr != NULL && attr->type != XML_ATTRIBUTE_DECL) {
992 node_list_unlink(attr->children);
993 }
994 xmlSetProp(elemp, BAD_CAST localname, BAD_CAST value);
995 }
996 }
997 }
998
999 xmlFree(localname);
1000 if (prefix != NULL) {
1001 xmlFree(prefix);
1002 }
1003
1004 if (errorcode != 0) {
1005 php_dom_throw_error(errorcode, stricterror);
1006 }
1007}
1008
1009/* https://dom.spec.whatwg.org/#dom-element-setattributens */
1010static void dom_set_attribute_ns_modern(dom_object *intern, xmlNodePtr elemp, zend_string *uri, const zend_string *name, const char *value)
1011{
1012 xmlChar *localname = NULL, *prefix = NULL;
1013 int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
1014
1015 if (errorcode == 0) {
1017 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
1018 xmlAttrPtr attr = xmlSetNsProp(elemp, ns, localname, BAD_CAST value);
1019 if (UNEXPECTED(attr == NULL)) {
1020 php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
1021 } else {
1022 dom_check_register_attribute_id(attr, intern->document);
1023 }
1024 } else {
1025 php_dom_throw_error(errorcode, /* strict */ true);
1026 }
1027
1028 xmlFree(localname);
1029 xmlFree(prefix);
1030}
1031
1032/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS
1033Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributens
1034Since: DOM Level 2
1035*/
1036PHP_METHOD(DOMElement, setAttributeNS)
1037{
1038 zval *id;
1039 xmlNodePtr elemp;
1040 size_t value_len = 0;
1041 char *value;
1042 zend_string *uri;
1044 dom_object *intern;
1045
1046 id = ZEND_THIS;
1047 if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!Ss", &uri, &name, &value, &value_len) == FAILURE) {
1048 RETURN_THROWS();
1049 }
1050
1051 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1052
1053 if (php_dom_follow_spec_intern(intern)) {
1054 dom_set_attribute_ns_modern(intern, elemp, uri, name, value);
1055 } else {
1056 dom_set_attribute_ns_legacy(intern, elemp, uri ? ZSTR_VAL(uri) : NULL, uri ? ZSTR_LEN(uri) : 0, ZSTR_VAL(name), ZSTR_LEN(name), value);
1057 }
1058}
1059/* }}} end dom_element_set_attribute_ns */
1060
1061static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs)
1062{
1063 ZEND_ASSERT(node->type == XML_ELEMENT_NODE);
1064 if (node->ns == eliminatedNs) {
1065 node->ns = NULL;
1066 }
1067
1068 for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
1069 if (attr->ns == eliminatedNs) {
1070 attr->ns = NULL;
1071 }
1072 }
1073}
1074
1075static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs)
1076{
1077 dom_remove_eliminated_ns_single_element(node, eliminatedNs);
1078
1079 xmlNodePtr base = node;
1080 node = node->children;
1081 while (node != NULL) {
1082 ZEND_ASSERT(node != base);
1083
1084 if (node->type == XML_ELEMENT_NODE) {
1085 dom_remove_eliminated_ns_single_element(node, eliminatedNs);
1086 }
1087
1088 node = php_dom_next_in_tree_order(node, base);
1089 }
1090}
1091
1092static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr)
1093{
1094 if (nsptr->href != NULL) {
1095 xmlFree((char *) nsptr->href);
1096 nsptr->href = NULL;
1097 }
1098 if (nsptr->prefix != NULL) {
1099 xmlFree((char *) nsptr->prefix);
1100 nsptr->prefix = NULL;
1101 }
1102
1103 /* Remove it from the list and move it to the old ns list */
1104 xmlNsPtr current_ns = nodep->nsDef;
1105 if (current_ns == nsptr) {
1106 nodep->nsDef = nsptr->next;
1107 } else {
1108 do {
1109 if (current_ns->next == nsptr) {
1110 current_ns->next = nsptr->next;
1111 break;
1112 }
1113 current_ns = current_ns->next;
1114 } while (current_ns != NULL);
1115 }
1116 nsptr->next = NULL;
1117 php_libxml_set_old_ns(nodep->doc, nsptr);
1118
1119 dom_remove_eliminated_ns(nodep, nsptr);
1120}
1121
1122/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS
1123Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattributens
1124Since: DOM Level 2
1125*/
1126PHP_METHOD(DOMElement, removeAttributeNS)
1127{
1128 zval *id;
1129 xmlNode *nodep;
1130 xmlAttr *attrp;
1131 xmlNsPtr nsptr;
1132 dom_object *intern;
1133 size_t name_len, uri_len;
1134 char *name, *uri;
1135
1136 id = ZEND_THIS;
1137 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1138 RETURN_THROWS();
1139 }
1140
1141 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1142
1143 bool follow_spec = php_dom_follow_spec_intern(intern);
1144 if (follow_spec && uri_len == 0) {
1145 uri = NULL;
1146 }
1147
1148 attrp = xmlHasNsProp(nodep, BAD_CAST name, BAD_CAST uri);
1149
1150 if (!follow_spec) {
1151 nsptr = dom_get_nsdecl(nodep, BAD_CAST name);
1152 if (nsptr != NULL) {
1153 if (xmlStrEqual(BAD_CAST uri, nsptr->href)) {
1154 dom_eliminate_ns(nodep, nsptr);
1155 } else {
1156 return;
1157 }
1158 }
1159 }
1160
1161 if (attrp && attrp->type != XML_ATTRIBUTE_DECL) {
1162 if (php_dom_object_get_data((xmlNodePtr) attrp) == NULL) {
1163 node_list_unlink(attrp->children);
1164 xmlUnlinkNode((xmlNodePtr) attrp);
1165 xmlFreeProp(attrp);
1166 } else {
1167 xmlUnlinkNode((xmlNodePtr) attrp);
1168 }
1169 }
1170}
1171/* }}} end dom_element_remove_attribute_ns */
1172
1173/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS
1174Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenodens
1175Since: DOM Level 2
1176*/
1177PHP_METHOD(DOMElement, getAttributeNodeNS)
1178{
1179 zval *id;
1180 xmlNodePtr elemp;
1181 xmlAttrPtr attrp;
1182 dom_object *intern;
1183 size_t uri_len, name_len;
1184 char *uri, *name;
1185
1186 id = ZEND_THIS;
1187 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1188 RETURN_THROWS();
1189 }
1190
1191 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1192
1193 bool follow_spec = php_dom_follow_spec_intern(intern);
1194 if (follow_spec && uri_len == 0) {
1195 uri = NULL;
1196 }
1197
1198 attrp = xmlHasNsProp(elemp, BAD_CAST name, BAD_CAST uri);
1199
1200 if (attrp == NULL) {
1201 if (!follow_spec && xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
1202 xmlNsPtr nsptr;
1203 nsptr = dom_get_nsdecl(elemp, BAD_CAST name);
1204 if (nsptr != NULL) {
1205 /* Keep parent alive, because we're a fake child. */
1206 GC_ADDREF(&intern->std);
1207 (void) php_dom_create_fake_namespace_decl(elemp, nsptr, return_value, intern);
1208 } else {
1209 RETURN_NULL();
1210 }
1211 } else {
1212 RETURN_NULL();
1213 }
1214 } else {
1215 DOM_RET_OBJ((xmlNodePtr) attrp, intern);
1216 }
1217
1218}
1219/* }}} end dom_element_get_attribute_node_ns */
1220
1221/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS
1222Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenodens
1223Since: DOM Level 2
1224*/
1225PHP_METHOD(DOMElement, setAttributeNodeNS)
1226{
1227 dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ false);
1228}
1229
1230PHP_METHOD(Dom_Element, setAttributeNodeNS)
1231{
1232 dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ true);
1233}
1234/* }}} end dom_element_set_attribute_node_ns */
1235
1236/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942
1237Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
1238Since: DOM Level 2
1239*/
1240static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS, bool modern)
1241{
1242 size_t uri_len, name_len;
1243 dom_object *intern, *namednode;
1244 char *uri, *name;
1245
1246 if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!p", &uri, &uri_len, &name, &name_len) == FAILURE) {
1247 RETURN_THROWS();
1248 }
1249
1250 if (uri_len > INT_MAX) {
1251 zend_argument_value_error(1, "is too long");
1252 RETURN_THROWS();
1253 }
1254
1255 if (name_len > INT_MAX) {
1256 zend_argument_value_error(2, "is too long");
1257 RETURN_THROWS();
1258 }
1259
1260 DOM_GET_THIS_INTERN(intern);
1261
1262 if (modern) {
1264 } else {
1266 }
1267 namednode = Z_DOMOBJ_P(return_value);
1268 dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
1269}
1270
1271PHP_METHOD(DOMElement, getElementsByTagNameNS)
1272{
1273 dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1274}
1275
1276PHP_METHOD(Dom_Element, getElementsByTagNameNS)
1277{
1278 dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1279}
1280/* }}} end dom_element_get_elements_by_tag_name_ns */
1281
1282/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr
1283Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattribute
1284Since: DOM Level 2
1285*/
1286PHP_METHOD(DOMElement, hasAttribute)
1287{
1288 zval *id;
1289 xmlNode *nodep;
1290 dom_object *intern;
1291 char *name;
1292 size_t name_len;
1293 xmlNodePtr attr;
1294
1295 id = ZEND_THIS;
1296 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
1297 RETURN_THROWS();
1298 }
1299
1300 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1301
1302 attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
1303 if (attr == NULL) {
1305 } else {
1307 }
1308}
1309/* }}} end dom_element_has_attribute */
1310
1311/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS
1312Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattributens
1313Since: DOM Level 2
1314*/
1315PHP_METHOD(DOMElement, hasAttributeNS)
1316{
1317 zval *id;
1318 xmlNodePtr elemp;
1319 dom_object *intern;
1320 size_t uri_len, name_len;
1321 char *uri, *name;
1322
1323 id = ZEND_THIS;
1324 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1325 RETURN_THROWS();
1326 }
1327
1328 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1329
1330 bool should_free_result = false;
1331 const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result);
1332 if (result == NULL) {
1334 } else {
1335 if (should_free_result) {
1336 xmlFree(BAD_CAST result);
1337 }
1339 }
1340}
1341/* }}} end dom_element_has_attribute_ns */
1342
1343static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id, php_libxml_ref_obj *document) /* {{{ */
1344{
1345 if (is_id && attrp->atype != XML_ATTRIBUTE_ID) {
1346 attrp->atype = XML_ATTRIBUTE_ID;
1347 } else if (!is_id && attrp->atype == XML_ATTRIBUTE_ID) {
1348 xmlRemoveID(attrp->doc, attrp);
1349 attrp->atype = 0;
1350 }
1351
1352 dom_mark_ids_modified(document);
1353}
1354/* }}} */
1355
1356/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr
1357Since: DOM Level 3
1358*/
1359PHP_METHOD(DOMElement, setIdAttribute)
1360{
1361 zval *id;
1362 xmlNode *nodep;
1363 xmlAttrPtr attrp;
1364 dom_object *intern;
1365 char *name;
1366 size_t name_len;
1367 bool is_id;
1368
1369 id = ZEND_THIS;
1370 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sb", &name, &name_len, &is_id) == FAILURE) {
1371 RETURN_THROWS();
1372 }
1373
1374 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1375
1376 attrp = xmlHasNsProp(nodep, BAD_CAST name, NULL);
1377 if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1379 } else {
1380 php_set_attribute_id(attrp, is_id, intern->document);
1381 }
1382}
1383/* }}} end dom_element_set_id_attribute */
1384
1385/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS
1386Since: DOM Level 3
1387*/
1388PHP_METHOD(DOMElement, setIdAttributeNS)
1389{
1390 zval *id;
1391 xmlNodePtr elemp;
1392 xmlAttrPtr attrp;
1393 dom_object *intern;
1394 size_t uri_len, name_len;
1395 char *uri, *name;
1396 bool is_id;
1397
1398 id = ZEND_THIS;
1399 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssb", &uri, &uri_len, &name, &name_len, &is_id) == FAILURE) {
1400 RETURN_THROWS();
1401 }
1402
1403 DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1404
1405 attrp = xmlHasNsProp(elemp, BAD_CAST name, BAD_CAST uri);
1406 if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1408 } else {
1409 php_set_attribute_id(attrp, is_id, intern->document);
1410 }
1411}
1412/* }}} end dom_element_set_id_attribute_ns */
1413
1414/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode
1415Since: DOM Level 3
1416*/
1417static void dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *attr_ce)
1418{
1419 zval *id, *node;
1420 xmlNode *nodep;
1421 xmlAttrPtr attrp;
1422 dom_object *intern, *attrobj;
1423 bool is_id;
1424
1425 id = ZEND_THIS;
1426 if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, attr_ce, &is_id) != SUCCESS) {
1427 RETURN_THROWS();
1428 }
1429
1430 DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1431 DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
1432
1433 if (attrp->parent != nodep) {
1435 } else {
1436 php_set_attribute_id(attrp, is_id, intern->document);
1437 }
1438}
1439
1440PHP_METHOD(DOMElement, setIdAttributeNode)
1441{
1442 dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
1443}
1444
1445PHP_METHOD(Dom_Element, setIdAttributeNode)
1446{
1447 dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_attr_class_entry);
1448}
1449/* }}} end dom_element_set_id_attribute_node */
1450
1451/* {{{ URL:
1452Since:
1453*/
1454PHP_METHOD(DOMElement, remove)
1455{
1456 dom_object *intern;
1457
1459 RETURN_THROWS();
1460 }
1461
1462 DOM_GET_THIS_INTERN(intern);
1463
1464 dom_child_node_remove(intern);
1465}
1466/* }}} end DOMElement::remove */
1467
1468PHP_METHOD(DOMElement, after)
1469{
1470 uint32_t argc = 0;
1471 zval *args;
1472 dom_object *intern;
1473
1475 Z_PARAM_VARIADIC('*', args, argc)
1477
1478 DOM_GET_THIS_INTERN(intern);
1479
1480 dom_parent_node_after(intern, args, argc);
1481}
1482
1483PHP_METHOD(DOMElement, before)
1484{
1485 uint32_t argc = 0;
1486 zval *args;
1487 dom_object *intern;
1488
1490 Z_PARAM_VARIADIC('*', args, argc)
1492
1493 DOM_GET_THIS_INTERN(intern);
1494
1495 dom_parent_node_before(intern, args, argc);
1496}
1497
1498/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
1499Since: DOM Living Standard (DOM4)
1500*/
1501PHP_METHOD(DOMElement, append)
1502{
1503 uint32_t argc = 0;
1504 zval *args;
1505 dom_object *intern;
1506
1508 Z_PARAM_VARIADIC('*', args, argc)
1510
1511 DOM_GET_THIS_INTERN(intern);
1512
1513 dom_parent_node_append(intern, args, argc);
1514}
1515/* }}} end DOMElement::append */
1516
1517/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
1518Since: DOM Living Standard (DOM4)
1519*/
1520PHP_METHOD(DOMElement, prepend)
1521{
1522 uint32_t argc = 0;
1523 zval *args;
1524 dom_object *intern;
1525
1527 Z_PARAM_VARIADIC('*', args, argc)
1529
1530 DOM_GET_THIS_INTERN(intern);
1531
1532 dom_parent_node_prepend(intern, args, argc);
1533}
1534/* }}} end DOMElement::prepend */
1535
1536/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1537Since: DOM Living Standard (DOM4)
1538*/
1539PHP_METHOD(DOMElement, replaceWith)
1540{
1541 uint32_t argc = 0;
1542 zval *args;
1543 dom_object *intern;
1544
1546 Z_PARAM_VARIADIC('*', args, argc)
1548
1549 DOM_GET_THIS_INTERN(intern);
1550
1551 dom_child_replace_with(intern, args, argc);
1552}
1553/* }}} end DOMElement::prepend */
1554
1555/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1556Since:
1557*/
1558PHP_METHOD(DOMElement, replaceChildren)
1559{
1560 uint32_t argc = 0;
1561 zval *args;
1562 dom_object *intern;
1563
1565 Z_PARAM_VARIADIC('*', args, argc)
1567
1568 DOM_GET_THIS_INTERN(intern);
1569
1571}
1572/* }}} */
1573
1574#define INSERT_ADJACENT_RES_ADOPT_FAILED ((void*) -1)
1575#define INSERT_ADJACENT_RES_SYNTAX_FAILED INSERT_ADJACENT_RES_ADOPT_FAILED
1576#define INSERT_ADJACENT_RES_PRE_INSERT_FAILED ((void*) -2)
1577
1578static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp)
1579{
1580 if (zend_string_equals_literal_ci(where, "beforebegin")) {
1581 if (thisp->parent == NULL) {
1582 return NULL;
1583 }
1584 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1585 return INSERT_ADJACENT_RES_ADOPT_FAILED;
1586 }
1587 if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp)) {
1588 return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
1589 }
1590 } else if (zend_string_equals_literal_ci(where, "afterbegin")) {
1591 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1592 return INSERT_ADJACENT_RES_ADOPT_FAILED;
1593 }
1594 if (!php_dom_pre_insert(this_intern->document, otherp, thisp, thisp->children)) {
1595 return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
1596 }
1597 } else if (zend_string_equals_literal_ci(where, "beforeend")) {
1598 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1599 return INSERT_ADJACENT_RES_ADOPT_FAILED;
1600 }
1601 if (!php_dom_pre_insert(this_intern->document, otherp, thisp, NULL)) {
1602 return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
1603 }
1604 } else if (zend_string_equals_literal_ci(where, "afterend")) {
1605 if (thisp->parent == NULL) {
1606 return NULL;
1607 }
1608 if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1609 return INSERT_ADJACENT_RES_ADOPT_FAILED;
1610 }
1611 if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp->next)) {
1612 return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
1613 }
1614 } else {
1616 return INSERT_ADJACENT_RES_SYNTAX_FAILED;
1617 }
1618 return otherp;
1619}
1620
1621/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
1622Since:
1623*/
1624static void dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAMETERS, const zend_string *where, zval *element_zval)
1625{
1626 zval *id;
1627 xmlNodePtr thisp, otherp;
1628 dom_object *this_intern, *other_intern;
1629
1630 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1631 DOM_GET_OBJ(otherp, element_zval, xmlNodePtr, other_intern);
1632
1633 xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1634 if (result == NULL) {
1635 RETURN_NULL();
1636 } else if (result != INSERT_ADJACENT_RES_ADOPT_FAILED && result != INSERT_ADJACENT_RES_PRE_INSERT_FAILED) {
1637 DOM_RET_OBJ(otherp, other_intern);
1638 } else {
1639 RETURN_THROWS();
1640 }
1641}
1642
1643PHP_METHOD(DOMElement, insertAdjacentElement)
1644{
1645 zend_string *where;
1646 zval *element_zval;
1647
1648 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) != SUCCESS) {
1649 RETURN_THROWS();
1650 }
1651
1652 dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, element_zval);
1653}
1654
1655PHP_METHOD(Dom_Element, insertAdjacentElement)
1656{
1657 zval *element_zval, *where_zv;
1658
1663
1664 const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
1665 dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, element_zval);
1666}
1667/* }}} end DOMElement::insertAdjacentElement */
1668
1669/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
1670Since:
1671*/
1672static void dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAMETERS, const zend_string *where, const zend_string *data)
1673{
1674 dom_object *this_intern;
1675 zval *id;
1676 xmlNodePtr thisp;
1677
1678 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1679
1681 zend_argument_value_error(2, "is too long");
1682 RETURN_THROWS();
1683 }
1684
1685 xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data));
1686 xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1687 if (result == NULL || result == INSERT_ADJACENT_RES_ADOPT_FAILED) {
1688 xmlFreeNode(otherp);
1689 }
1690}
1691
1692PHP_METHOD(DOMElement, insertAdjacentText)
1693{
1694 zend_string *where, *data;
1695
1696 if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &where, &data) == FAILURE) {
1697 RETURN_THROWS();
1698 }
1699
1700 dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, data);
1701}
1702
1703PHP_METHOD(Dom_Element, insertAdjacentText)
1704{
1705 zval *where_zv;
1707
1712
1713 const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
1714 dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, data);
1715}
1716/* }}} end DOMElement::insertAdjacentText */
1717
1718/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
1719Since:
1720*/
1721PHP_METHOD(DOMElement, toggleAttribute)
1722{
1723 char *qname, *qname_tmp = NULL;
1724 size_t qname_length;
1725 bool force, force_is_null = true;
1726 xmlNodePtr thisp;
1727 zval *id;
1728 dom_object *intern;
1729 bool retval;
1730
1731 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b!", &qname, &qname_length, &force, &force_is_null) == FAILURE) {
1732 RETURN_THROWS();
1733 }
1734
1735 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1736
1737 /* Step 1 */
1738 if (xmlValidateName(BAD_CAST qname, 0) != 0) {
1740 RETURN_THROWS();
1741 }
1742
1743 bool follow_spec = php_dom_follow_spec_intern(intern);
1744
1745 /* Step 2 */
1746 if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE
1747 && ((!follow_spec && thisp->ns == NULL) || (thisp->ns != NULL && xmlStrEqual(thisp->ns->href, BAD_CAST DOM_XHTML_NS_URI)))) {
1748 qname_tmp = zend_str_tolower_dup_ex(qname, qname_length);
1749 if (qname_tmp != NULL) {
1750 qname = qname_tmp;
1751 }
1752 }
1753
1754 /* Step 3 */
1755 xmlNodePtr attribute = dom_get_attribute_or_nsdecl(intern, thisp, BAD_CAST qname, qname_length);
1756
1757 /* Step 4 */
1758 if (attribute == NULL) {
1759 /* Step 4.1 */
1760 if (force_is_null || force) {
1761 if (follow_spec) {
1762 xmlSetNsProp(thisp, NULL, BAD_CAST qname, NULL);
1763 } else {
1764 /* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour.
1765 * It follows the same rules when you'd manually add an attribute using the other APIs. */
1766 int len;
1767 const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
1768 if (split == NULL || strncmp(qname, "xmlns:", len + 1 /* +1 for matching ':' too */) != 0) {
1769 /* unqualified name, or qualified name with no xml namespace declaration */
1770 dom_create_attribute(thisp, qname, "");
1771 } else {
1772 /* qualified name with xml namespace declaration */
1773 xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1));
1774 }
1775 }
1776 retval = true;
1777 goto out;
1778 }
1779 /* Step 4.2 */
1780 retval = false;
1781 goto out;
1782 }
1783
1784 /* Step 5 */
1785 if (force_is_null || !force) {
1786 dom_remove_attribute(thisp, attribute);
1787 retval = false;
1788 goto out;
1789 }
1790
1791 /* Step 6 */
1792 retval = true;
1793
1794out:
1795 if (qname_tmp) {
1796 efree(qname_tmp);
1797 }
1799}
1800/* }}} end DOMElement::prepend */
1801
1802static void php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS, bool all)
1803{
1804 zend_string *selectors_str;
1805
1807 Z_PARAM_STR(selectors_str)
1809
1810 xmlNodePtr thisp;
1811 dom_object *intern;
1812 zval *id;
1813 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1814
1815 if (all) {
1816 dom_parent_node_query_selector_all(thisp, intern, return_value, selectors_str);
1817 } else {
1818 dom_parent_node_query_selector(thisp, intern, return_value, selectors_str);
1819 }
1820}
1821
1822PHP_METHOD(Dom_Element, querySelector)
1823{
1824 php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1825}
1826
1827PHP_METHOD(Dom_Element, querySelectorAll)
1828{
1829 php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1830}
1831
1832PHP_METHOD(Dom_Element, matches)
1833{
1834 zend_string *selectors_str;
1835
1837 Z_PARAM_STR(selectors_str)
1839
1840 xmlNodePtr thisp;
1841 dom_object *intern;
1842 zval *id;
1843 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1844
1845 dom_element_matches(thisp, intern, return_value, selectors_str);
1846}
1847
1848PHP_METHOD(Dom_Element, closest)
1849{
1850 zend_string *selectors_str;
1851
1853 Z_PARAM_STR(selectors_str)
1855
1856 xmlNodePtr thisp;
1857 dom_object *intern;
1858 zval *id;
1859 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1860
1861 dom_element_closest(thisp, intern, return_value, selectors_str);
1862}
1863
1865{
1866 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
1867
1868 xmlChar *content = xmlNodeGetContent(nodep);
1869
1870 if (UNEXPECTED(content == NULL)) {
1872 return FAILURE;
1873 } else {
1874 ZVAL_STRING(retval, (const char *) content);
1875 xmlFree(content);
1876 }
1877
1878 return SUCCESS;
1879}
1880
1882{
1883 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
1884
1885 php_libxml_invalidate_node_list_cache(obj->document);
1887 xmlNodeSetContentLen(nodep, (xmlChar *) Z_STRVAL_P(newval), Z_STRLEN_P(newval));
1888
1889 return SUCCESS;
1890}
1891
1892static void dom_element_get_in_scope_namespace_info(php_dom_libxml_ns_mapper *ns_mapper, HashTable *result, xmlNodePtr nodep, dom_object *intern)
1893{
1894 HashTable prefix_to_ns_table;
1895 zend_hash_init(&prefix_to_ns_table, 0, NULL, NULL, false);
1896 zend_hash_real_init_mixed(&prefix_to_ns_table);
1897
1898 /* https://www.w3.org/TR/1999/REC-xpath-19991116/#namespace-nodes */
1899 for (const xmlNode *cur = nodep; cur != NULL; cur = cur->parent) {
1900 if (cur->type == XML_ELEMENT_NODE) {
1901 /* Find the last attribute */
1902 const xmlAttr *last = NULL;
1903 for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) {
1904 last = attr;
1905 }
1906
1907 /* Reversed loop because the parent traversal is reversed as well,
1908 * this will keep the ordering consistent. */
1909 for (const xmlAttr *attr = last; attr != NULL; attr = attr->prev) {
1911 && attr->children != NULL && attr->children->content != NULL) {
1912 const char *prefix = attr->ns->prefix == NULL ? NULL : (const char *) attr->name;
1913 const char *key = prefix == NULL ? "" : prefix;
1914 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, prefix, (const char *) attr->children->content);
1915 /* NULL is a valid value for the sentinel */
1916 zval zv;
1917 ZVAL_PTR(&zv, ns);
1918 zend_hash_str_add(&prefix_to_ns_table, key, strlen(key), &zv);
1919 }
1920 }
1921 }
1922 }
1923
1924 xmlNsPtr ns;
1926 ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR(&prefix_to_ns_table, prefix, ns) {
1927 if (ZSTR_LEN(prefix) == 0 && (ns == NULL || ns->href == NULL || *ns->href == '\0')) {
1928 /* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */
1929 continue;
1930 }
1931
1932 zval zv;
1934 zend_object *obj = Z_OBJ(zv);
1935
1936 if (ZSTR_LEN(prefix) != 0) {
1938 } else {
1939 ZVAL_NULL(OBJ_PROP_NUM(obj, 0));
1940 }
1941
1942 if (ns != NULL && ns->href != NULL && *ns->href != '\0') {
1943 ZVAL_STRING(OBJ_PROP_NUM(obj, 1), (const char *) ns->href);
1944 } else {
1945 ZVAL_NULL(OBJ_PROP_NUM(obj, 1));
1946 }
1947
1948 php_dom_create_object(nodep, OBJ_PROP_NUM(obj, 2), intern);
1949
1952
1953 zend_hash_destroy(&prefix_to_ns_table);
1954}
1955
1956PHP_METHOD(Dom_Element, getInScopeNamespaces)
1957{
1958 zval *id;
1959 xmlNode *nodep;
1960 dom_object *intern;
1961
1963
1964 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
1965
1967
1970
1971 dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
1972}
1973
1974PHP_METHOD(Dom_Element, getDescendantNamespaces)
1975{
1976 zval *id;
1977 xmlNode *nodep;
1978 dom_object *intern;
1979
1981
1982 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
1983
1985
1988
1989 dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
1990
1991 xmlNodePtr cur = nodep->children;
1992 while (cur != NULL) {
1993 if (cur->type == XML_ELEMENT_NODE) {
1994 /* TODO: this could be more optimized by updating the same HashTable repeatedly
1995 * instead of recreating it on every node. */
1996 dom_element_get_in_scope_namespace_info(ns_mapper, result, cur, intern);
1997 }
1998
1999 cur = php_dom_next_in_tree_order(cur, nodep);
2000 }
2001}
2002
2003PHP_METHOD(Dom_Element, rename)
2004{
2005 zend_string *namespace_uri, *qualified_name;
2007 Z_PARAM_STR_OR_NULL(namespace_uri)
2008 Z_PARAM_STR(qualified_name)
2010
2011 zval *id;
2012 dom_object *intern;
2013 xmlNodePtr nodep;
2014 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2015
2016 xmlChar *localname = NULL, *prefix = NULL;
2017 int errorcode = dom_validate_and_extract(namespace_uri, qualified_name, &localname, &prefix);
2018 if (UNEXPECTED(errorcode != 0)) {
2019 php_dom_throw_error(errorcode, /* strict */ true);
2020 goto cleanup;
2021 }
2022
2023 if (nodep->type == XML_ATTRIBUTE_NODE) {
2024 /* Check for duplicate attributes. */
2025 xmlAttrPtr existing = xmlHasNsProp(nodep->parent, localname, namespace_uri && ZSTR_VAL(namespace_uri)[0] != '\0' ? BAD_CAST ZSTR_VAL(namespace_uri) : NULL);
2026 if (existing != NULL && existing != (xmlAttrPtr) nodep) {
2027 php_dom_throw_error_with_message(INVALID_MODIFICATION_ERR, "An attribute with the given name in the given namespace already exists", /* strict */ true);
2028 goto cleanup;
2029 }
2030 } else {
2031 ZEND_ASSERT(nodep->type == XML_ELEMENT_NODE);
2032
2033 /* Check for moving to or away from the HTML namespace. */
2034 bool is_currently_html_ns = php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token);
2035 bool will_be_html_ns = namespace_uri != NULL && zend_string_equals_literal(namespace_uri, DOM_XHTML_NS_URI);
2036 if (is_currently_html_ns != will_be_html_ns) {
2037 if (is_currently_html_ns) {
2040 "It is not possible to move an element out of the HTML namespace because the HTML namespace is tied to the HTMLElement class",
2041 /* strict */ true
2042 );
2043 } else {
2046 "It is not possible to move an element into the HTML namespace because the HTML namespace is tied to the HTMLElement class",
2047 /* strict */ true
2048 );
2049 }
2050 goto cleanup;
2051 }
2052
2053 /* If we currently have a template but the new element type won't be a template, then throw away the templated content. */
2054 if (is_currently_html_ns && xmlStrEqual(nodep->name, BAD_CAST "template") && !xmlStrEqual(localname, BAD_CAST "template")) {
2057 "It is not possible to rename the template element because it hosts a document fragment",
2058 /* strict */ true
2059 );
2060 goto cleanup;
2061 }
2062 }
2063
2064 php_libxml_invalidate_node_list_cache(intern->document);
2065
2067
2068 /* Update namespace uri + prefix by querying the namespace mapper */
2069 /* prefix can be NULL here, but that is taken care of by the called APIs. */
2070 nodep->ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), namespace_uri);
2071
2072 /* Change the local name */
2073 if (xmlDictOwns(nodep->doc->dict, nodep->name) != 1) {
2074 xmlFree((xmlChar *) nodep->name);
2075 }
2076 const xmlChar *copy = xmlDictLookup(nodep->doc->dict, localname, -1);
2077 if (copy != NULL) {
2078 nodep->name = copy;
2079 } else {
2080 nodep->name = localname;
2081 localname = NULL;
2082 }
2083
2084cleanup:
2085 xmlFree(localname);
2086 xmlFree(prefix);
2087}
2088
2089#endif
size_t len
Definition apprentice.c:174
rename(string $from, string $to, $context=null)
prev(array|object &$array)
copy(string $from, string $to, $context=null)
PHP_DOM_EXPORT zend_class_entry * dom_namespace_info_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_attr_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_element_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_modern_attr_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_modern_element_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_adjacent_position_class_entry
PHP_DOM_EXPORT zend_class_entry * dom_token_list_class_entry
zend_result dom_element_tag_name_read(dom_object *obj, zval *retval)
zend_result dom_element_id_read(dom_object *obj, zval *retval)
zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zval *newval)
zend_result dom_element_class_name_read(dom_object *obj, zval *retval)
zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval)
zend_result dom_element_class_name_write(dom_object *obj, zval *newval)
zend_result dom_modern_element_substituted_node_value_read(dom_object *obj, zval *retval)
zend_result dom_element_class_list_read(dom_object *obj, zval *retval)
#define DOM_PROP_NODE(type, name, obj)
zend_result dom_element_id_write(dom_object *obj, zval *newval)
void php_dom_throw_error_with_message(dom_exception_code error_code, const char *error_message, bool strict_error)
void php_dom_throw_error(dom_exception_code error_code, bool strict_error)
@ INVALID_STATE_ERR
@ NOT_FOUND_ERR
@ INVALID_MODIFICATION_ERR
@ INUSE_ATTRIBUTE_ERR
@ NAMESPACE_ERR
@ INVALID_CHARACTER_ERR
@ SYNTAX_ERR
@ WRONG_DOCUMENT_ERR
zval * zv
Definition ffi.c:3975
new_type attr
Definition ffi.c:4364
HashTable * ht
Definition ffi.c:4838
#define SIZE_MAX
Definition funcs.c:51
#define NULL
Definition gdcache.h:45
#define prefix
#define SUCCESS
Definition hash_sha3.c:261
#define split
Definition hash_tiger.c:112
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
PHP_DOM_EXPORT const php_dom_ns_magic_token * php_dom_ns_is_html_magic_token
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(php_dom_libxml_ns_mapper *mapper, const xmlChar *prefix, size_t prefix_len, zend_string *uri)
PHP_DOM_EXPORT const php_dom_ns_magic_token * php_dom_ns_is_xmlns_magic_token
PHP_DOM_EXPORT bool php_dom_ns_is_fast_ex(xmlNsPtr ns, const php_dom_ns_magic_token *magic_token)
PHP_DOM_EXPORT bool php_dom_ns_is_fast(const xmlNode *nodep, const php_dom_ns_magic_token *magic_token)
PHP_DOM_EXPORT bool php_dom_ns_is_html_and_document_is_html(const xmlNode *nodep)
PHP_DOM_EXPORT void php_dom_reconcile_attribute_namespace_after_insertion(xmlAttrPtr attrp)
#define DOM_XHTML_NS_URI
PHP_DOM_EXPORT php_dom_libxml_ns_mapper * php_dom_get_ns_mapper(dom_object *object)
PHP_DOM_EXPORT xmlNsPtr php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(php_dom_libxml_ns_mapper *mapper, const char *prefix, const char *uri)
#define DOM_XMLNS_NS_URI
#define INT_MAX
Definition php.h:237
#define PHP_METHOD
Definition php.h:365
void dom_attr_value_will_change(dom_object *obj, xmlAttrPtr attrp)
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
xmlAttrPtr php_dom_get_attribute_node(xmlNodePtr elem, const xmlChar *name, size_t name_len)
void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc)
xmlNsPtr dom_get_nsdecl(xmlNode *node, xmlChar *localName)
void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t nodesc)
xmlNsPtr dom_get_ns(xmlNodePtr node, char *uri, int *errorcode, char *prefix)
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
int dom_check_qname(char *qname, char **localname, char **prefix, int uri_len, int name_len)
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
void node_list_unlink(xmlNodePtr node)
xmlNsPtr dom_get_ns_resolve_prefix_conflict(xmlNodePtr tree, const char *uri)
bool dom_get_strict_error(php_libxml_ref_obj *document)
xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr original, zval *return_value, dom_object *parent_intern)
void dom_child_replace_with(dom_object *context, zval *nodes, uint32_t nodesc)
#define DOM_GET_OBJ(__ptr, __id, __prtype, __intern)
Definition php_dom.h:237
bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePtr parent, xmlNodePtr insertion_point)
void dom_parent_node_before(dom_object *context, zval *nodes, uint32_t nodesc)
void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc)
void dom_set_document_ref_pointers_attr(xmlAttrPtr attr, php_libxml_ref_obj *document)
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str)
int dom_validate_and_extract(const zend_string *namespace, const zend_string *qname, xmlChar **localName, xmlChar **prefix)
void dom_child_node_remove(dom_object *context)
void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc)
void dom_namednode_iter(dom_object *basenode, int ntype, dom_object *intern, xmlHashTablePtr ht, const char *local, size_t local_len, const char *ns, size_t ns_len)
zend_string * dom_node_concatenated_name_helper(size_t name_len, const char *name, size_t prefix_len, const char *prefix)
void php_dom_create_iterator(zval *return_value, dom_iterator_type iterator_type, bool modern)
#define DOM_GET_THIS_INTERN(__intern)
Definition php_dom.h:235
zend_string * dom_node_get_node_name_attribute_or_element(const xmlNode *nodep, bool uppercase)
@ DOM_NODELIST
Definition php_dom.h:115
@ DOM_HTMLCOLLECTION
Definition php_dom.h:118
void dom_remove_all_children(xmlNodePtr nodep)
const XML_ATTRIBUTE_ID
const XML_HTML_DOCUMENT_NODE
const XML_ELEMENT_NODE
const XML_ATTRIBUTE_NODE
unsigned char key[REFLECTION_KEY_LEN]
#define zend_hash_str_add(...)
Definition phpdbg.h:77
zend_constant * data
void * ptr
Definition xml_common.h:26
zend_object std
Definition xml_common.h:29
php_libxml_ref_obj * document
Definition xml_common.h:27
void dom_token_list_ctor(dom_token_list_object *intern, dom_object *element_obj)
#define DOM_GET_THIS_OBJ(__ptr, __id, __prtype, __intern)
Definition xml_common.h:79
PHP_DOM_EXPORT xmlNodePtr dom_object_get_node(dom_object *obj)
struct _dom_object dom_object
#define DOM_RET_OBJ(obj, domobject)
Definition xml_common.h:76
PHP_DOM_EXPORT dom_object * php_dom_object_get_data(xmlNodePtr obj)
#define Z_DOMOBJ_P(zv)
Definition xml_common.h:36
PHP_DOM_EXPORT bool php_dom_create_object(xmlNodePtr obj, zval *return_value, dom_object *domobj)
#define INTERNAL_FUNCTION_PARAMETERS
Definition zend.h:49
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API zend_result zend_parse_parameters(uint32_t num_args, const char *type_spec,...)
Definition zend_API.c:1300
ZEND_API ZEND_COLD void zend_argument_must_not_be_empty_error(uint32_t arg_num)
Definition zend_API.c:443
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define RETVAL_STRING(s)
Definition zend_API.h:1017
#define Z_PARAM_STR_OR_NULL(dest)
Definition zend_API.h:2089
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define RETURN_NULL()
Definition zend_API.h:1036
#define ZVAL_STRING(z, s)
Definition zend_API.h:956
#define zend_parse_parameters_none()
Definition zend_API.h:353
#define Z_PARAM_STR(dest)
Definition zend_API.h:2086
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_VARIADIC(spec, dest, dest_num)
Definition zend_API.h:2124
#define RETURN_BOOL(b)
Definition zend_API.h:1035
#define RETURN_THROWS()
Definition zend_API.h:1060
#define ZEND_THIS
Definition zend_API.h:523
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce)
Definition zend_API.h:1976
#define RETURN_EMPTY_STRING()
Definition zend_API.h:1047
#define RETURN_TRUE
Definition zend_API.h:1059
#define array_init(arg)
Definition zend_API.h:537
#define ZVAL_EMPTY_STRING(z)
Definition zend_API.h:961
#define efree(ptr)
Definition zend_alloc.h:155
#define erealloc(ptr, size)
Definition zend_alloc.h:159
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
strncmp(string $string1, string $string2, int $length)
zend_string_release_ex(func->internal_function.function_name, 0)
zval * args
#define OBJ_PROP_TO_NUM(offset)
#define OBJ_PROP_NUM(obj, num)
struct _zend_property_info zend_property_info
ZEND_API void(ZEND_FASTCALL *zend_touch_vm_stack_data)(void *vm_stack_data)
ZEND_API void ZEND_FASTCALL zend_hash_destroy(HashTable *ht)
Definition zend_hash.c:1727
ZEND_API void ZEND_FASTCALL zend_hash_real_init_packed(HashTable *ht)
Definition zend_hash.c:330
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert_new(HashTable *ht, zval *pData)
Definition zend_hash.c:1229
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
Definition zend_hash.c:1224
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht)
Definition zend_hash.c:338
#define zend_hash_init(ht, nSize, pHashFunction, pDestructor, persistent)
Definition zend_hash.h:108
#define ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR(ht, _key, _ptr)
Definition zend_hash.h:1438
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
struct _zend_string zend_string
ZEND_API zend_property_info * zend_get_property_info(const zend_class_entry *ce, zend_string *member, int silent)
ZEND_API char *ZEND_FASTCALL zend_str_tolower_dup_ex(const char *source, size_t length)
int last
#define EXPECTED(condition)
#define ZEND_ASSERT(c)
#define ZEND_UNREACHABLE()
#define EMPTY_SWITCH_DEFAULT_CASE()
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
#define ZEND_SIZE_T_INT_OVFL(size)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_INIT_LITERAL(s, persistent)
#define zend_string_equals_literal(str, literal)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define zend_string_equals_literal_ci(str, c)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define Z_STRVAL_P(zval_p)
Definition zend_types.h:975
#define Z_ISUNDEF_P(zval_p)
Definition zend_types.h:957
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_NULL(z)
#define IS_STRING
Definition zend_types.h:606
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define GC_ADDREF(p)
Definition zend_types.h:709
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
@ FAILURE
Definition zend_types.h:61
#define ZVAL_NEW_STR(z, s)
#define ZVAL_PTR(z, p)
#define ZVAL_OBJ_COPY(z, o)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define Z_OBJ(zval)
Definition zend_types.h:989
zval retval
zval * return_value
zend_property_info * prop_info
zend_string * name
bool result
value
out($f, $s)