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 /* {{{ */
PHP_METHOD(DOMElement,__construct)40 PHP_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) {
52 RETURN_THROWS();
53 }
54
55 name_valid = xmlValidateName(BAD_CAST name, 0);
56 if (name_valid != 0) {
57 php_dom_throw_error(INVALID_CHARACTER_ERR, true);
58 RETURN_THROWS();
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);
80 RETURN_THROWS();
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);
88 php_dom_throw_error(NAMESPACE_ERR, true);
89 RETURN_THROWS();
90 }
91 nodep = xmlNewNode(NULL, BAD_CAST name);
92 }
93
94 if (!nodep) {
95 php_dom_throw_error(INVALID_STATE_ERR, true);
96 RETURN_THROWS();
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
113 readonly=yes
114 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-104682815
115 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-tagname
116 Since:
117 */
dom_element_tag_name_read(dom_object * obj,zval * retval)118 zend_result dom_element_tag_name_read(dom_object *obj, zval *retval)
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);
125 ZVAL_NEW_STR(retval, result);
126
127 return SUCCESS;
128 }
129
130 /* }}} */
131
dom_element_reflected_attribute_read(dom_object * obj,zval * retval,const char * name)132 static 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) {
138 ZVAL_EMPTY_STRING(retval);
139 return SUCCESS;
140 }
141
142 ZVAL_STRING(retval, (const char *) content);
143 xmlFree(content);
144
145 return SUCCESS;
146 }
147
dom_element_reflected_attribute_write(dom_object * obj,zval * newval,const char * name)148 static 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) {
153 php_dom_throw_error(INVALID_STATE_ERR, true);
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
163 URL: https://dom.spec.whatwg.org/#dom-element-classname
164 Since:
165 */
dom_element_class_name_read(dom_object * obj,zval * retval)166 zend_result dom_element_class_name_read(dom_object *obj, zval *retval)
167 {
168 return dom_element_reflected_attribute_read(obj, retval, "class");
169 }
170
dom_element_class_name_write(dom_object * obj,zval * newval)171 zend_result dom_element_class_name_write(dom_object *obj, zval *newval)
172 {
173 if (dom_element_reflected_attribute_write(obj, newval, "class")) {
174 return SUCCESS;
175 }
176 return FAILURE;
177 }
178 /* }}} */
179
180 /* {{{ classList TokenList
181 URL: https://dom.spec.whatwg.org/#dom-element-classlist
182 */
dom_element_class_list_read(dom_object * obj,zval * retval)183 zend_result dom_element_class_list_read(dom_object *obj, zval *retval)
184 {
185 const uint32_t PROP_INDEX = 0;
186
187 #if ZEND_DEBUG
188 zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false);
189 const zend_property_info *prop_info = zend_get_property_info(dom_modern_element_class_entry, class_list_str, 0);
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
208 URL: https://dom.spec.whatwg.org/#dom-element-id
209 Since:
210 */
dom_element_id_read(dom_object * obj,zval * retval)211 zend_result dom_element_id_read(dom_object *obj, zval *retval)
212 {
213 return dom_element_reflected_attribute_read(obj, retval, "id");
214 }
215
216 static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id, php_libxml_ref_obj *document);
217
dom_element_id_write(dom_object * obj,zval * newval)218 zend_result dom_element_id_write(dom_object *obj, zval *newval)
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
230 readonly=yes
231 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Element-schemaTypeInfo
232 Since: DOM Level 3
233 */
dom_element_schema_type_info_read(dom_object * obj,zval * retval)234 zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval)
235 {
236 ZVAL_NULL(retval);
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. */
dom_get_attribute_or_nsdecl(dom_object * intern,xmlNodePtr elem,const xmlChar * name,size_t name_len)243 static 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
289 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattribute
290 Since:
291 */
PHP_METHOD(DOMElement,getAttribute)292 PHP_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) {
305 RETURN_THROWS();
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) {
313 case XML_ATTRIBUTE_NODE:
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 }
331 RETURN_EMPTY_STRING();
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
342 Since:
343 */
PHP_METHOD(DOMElement,getAttributeNames)344 PHP_METHOD(DOMElement, getAttributeNames)
345 {
346 zval *id;
347 xmlNode *nodep;
348 dom_object *intern;
349 zval tmp;
350
351 if (zend_parse_parameters_none() == FAILURE) {
352 RETURN_THROWS();
353 }
354
355 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
356
357 array_init(return_value);
358 HashTable *ht = Z_ARRVAL_P(return_value);
359 zend_hash_real_init_packed(ht);
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 }
369 zend_hash_next_index_insert(ht, &tmp);
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));
375 zend_hash_next_index_insert(ht, &tmp);
376 }
377 }
378 /* }}} end DOMElement::getAttributeNames() */
379
dom_create_attribute(xmlNodePtr nodep,const char * name,const char * value)380 static 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
dom_check_register_attribute_id(xmlAttrPtr attr,php_libxml_ref_obj * document)389 static 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
400 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattribute
401 Since:
402 */
PHP_METHOD(DOMElement,setAttribute)403 PHP_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) {
414 RETURN_THROWS();
415 }
416
417 if (name_len == 0) {
418 zend_argument_must_not_be_empty_error(1);
419 RETURN_THROWS();
420 }
421
422 name_valid = xmlValidateName(BAD_CAST name, 0);
423 if (name_valid != 0) {
424 php_dom_throw_error(INVALID_CHARACTER_ERR, true);
425 RETURN_THROWS();
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;
432 if (php_dom_ns_is_html_and_document_is_html(nodep)) {
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) {
442 dom_attr_value_will_change(intern, attr);
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) {
460 case XML_ATTRIBUTE_NODE:
461 dom_attr_value_will_change(intern, (xmlAttrPtr) attr);
462 node_list_unlink(attr->children);
463 break;
464 case XML_NAMESPACE_DECL:
465 RETURN_FALSE;
466 EMPTY_SWITCH_DEFAULT_CASE();
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");
473 RETURN_THROWS();
474 }
475 if (attr->type == XML_NAMESPACE_DECL) {
476 RETURN_TRUE;
477 }
478
479 DOM_RET_OBJ(attr, intern);
480 }
481 }
482 /* }}} end dom_element_set_attribute */
483
484 typedef 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. */
dom_deep_ns_redef(xmlNodePtr node,xmlNsPtr ns_to_redefine)490 static 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
538 out:
539 efree(worklist);
540 }
541
dom_remove_attribute(xmlNodePtr thisp,xmlNodePtr attrp)542 static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp)
543 {
544 ZEND_ASSERT(thisp != NULL);
545 ZEND_ASSERT(attrp != NULL);
546
547 switch (attrp->type) {
548 case XML_ATTRIBUTE_NODE:
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
580 ZEND_UNREACHABLE();
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 }
593 EMPTY_SWITCH_DEFAULT_CASE();
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
599 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattribute
600 Since:
601 */
PHP_METHOD(DOMElement,removeAttribute)602 PHP_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) {
610 RETURN_THROWS();
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) {
617 RETURN_FALSE;
618 }
619
620 RETURN_BOOL(dom_remove_attribute(nodep, attrp));
621 }
622
PHP_METHOD(Dom_Element,removeAttribute)623 PHP_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) {
631 RETURN_THROWS();
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
644 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenode
645 Since:
646 */
PHP_METHOD(DOMElement,getAttributeNode)647 PHP_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) {
657 RETURN_THROWS();
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 }
667 RETURN_FALSE;
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
dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAMETERS,bool use_ns,bool modern)681 static 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) {
691 RETURN_THROWS();
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);
705 RETURN_THROWS();
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)) {
712 php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
713 RETURN_FALSE;
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) {
738 dom_set_document_ref_pointers_attr(attrp, intern->document);
739 }
740
741 xmlAddChild(nodep, (xmlNodePtr) attrp);
742 if (!modern) {
743 dom_mark_ids_modified(intern->document);
744 php_dom_reconcile_attribute_namespace_after_insertion(attrp);
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
758 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenode
759 Since:
760 */
PHP_METHOD(DOMElement,setAttributeNode)761 PHP_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
768 Since:
769 */
dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * node_ce)770 static 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) {
778 RETURN_THROWS();
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) {
788 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
789 RETURN_FALSE;
790 }
791
792 xmlUnlinkNode((xmlNodePtr) attrp);
793
794 DOM_RET_OBJ((xmlNodePtr) attrp, intern);
795 }
796
PHP_METHOD(DOMElement,removeAttributeNode)797 PHP_METHOD(DOMElement, removeAttributeNode)
798 {
799 dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
800 }
801
PHP_METHOD(Dom_Element,removeAttributeNode)802 PHP_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
809 Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagname
810 Since:
811 */
dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS,bool modern)812 static 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) {
819 RETURN_THROWS();
820 }
821
822 if (name_len > INT_MAX) {
823 zend_argument_value_error(1, "is too long");
824 RETURN_THROWS();
825 }
826
827 DOM_GET_THIS_INTERN(intern);
828
829 if (modern) {
830 php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
831 } else {
832 php_dom_create_iterator(return_value, DOM_NODELIST, false);
833 }
834 namednode = Z_DOMOBJ_P(return_value);
835 dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
836 }
837
PHP_METHOD(DOMElement,getElementsByTagName)838 PHP_METHOD(DOMElement, getElementsByTagName)
839 {
840 dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
841 }
842
PHP_METHOD(Dom_Element,getElementsByTagName)843 PHP_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 */
dom_get_attribute_ns(dom_object * intern,xmlNodePtr elemp,const char * uri,size_t uri_len,const char * name,bool * should_free_result)850 static 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
877 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributens
878 Since: DOM Level 2
879 */
PHP_METHOD(DOMElement,getAttributeNS)880 PHP_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) {
890 RETURN_THROWS();
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 }
901 RETURN_EMPTY_STRING();
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
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)911 static 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) {
914 zend_argument_must_not_be_empty_error(2);
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 */
dom_set_attribute_ns_modern(dom_object * intern,xmlNodePtr elemp,zend_string * uri,const zend_string * name,const char * value)1010 static 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) {
1016 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
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
1033 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributens
1034 Since: DOM Level 2
1035 */
PHP_METHOD(DOMElement,setAttributeNS)1036 PHP_METHOD(DOMElement, setAttributeNS)
1037 {
1038 zval *id;
1039 xmlNodePtr elemp;
1040 size_t value_len = 0;
1041 char *value;
1042 zend_string *uri;
1043 zend_string *name = NULL;
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
dom_remove_eliminated_ns_single_element(xmlNodePtr node,xmlNsPtr eliminatedNs)1061 static 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
dom_remove_eliminated_ns(xmlNodePtr node,xmlNsPtr eliminatedNs)1075 static 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
dom_eliminate_ns(xmlNodePtr nodep,xmlNsPtr nsptr)1092 static 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
1123 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattributens
1124 Since: DOM Level 2
1125 */
PHP_METHOD(DOMElement,removeAttributeNS)1126 PHP_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
1174 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenodens
1175 Since: DOM Level 2
1176 */
PHP_METHOD(DOMElement,getAttributeNodeNS)1177 PHP_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
1222 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenodens
1223 Since: DOM Level 2
1224 */
PHP_METHOD(DOMElement,setAttributeNodeNS)1225 PHP_METHOD(DOMElement, setAttributeNodeNS)
1226 {
1227 dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ false);
1228 }
1229
PHP_METHOD(Dom_Element,setAttributeNodeNS)1230 PHP_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
1237 Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
1238 Since: DOM Level 2
1239 */
dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS,bool modern)1240 static 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) {
1263 php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
1264 } else {
1265 php_dom_create_iterator(return_value, DOM_NODELIST, false);
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
PHP_METHOD(DOMElement,getElementsByTagNameNS)1271 PHP_METHOD(DOMElement, getElementsByTagNameNS)
1272 {
1273 dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1274 }
1275
PHP_METHOD(Dom_Element,getElementsByTagNameNS)1276 PHP_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
1283 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattribute
1284 Since: DOM Level 2
1285 */
PHP_METHOD(DOMElement,hasAttribute)1286 PHP_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) {
1304 RETURN_FALSE;
1305 } else {
1306 RETURN_TRUE;
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
1312 Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattributens
1313 Since: DOM Level 2
1314 */
PHP_METHOD(DOMElement,hasAttributeNS)1315 PHP_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) {
1333 RETURN_FALSE;
1334 } else {
1335 if (should_free_result) {
1336 xmlFree(BAD_CAST result);
1337 }
1338 RETURN_TRUE;
1339 }
1340 }
1341 /* }}} end dom_element_has_attribute_ns */
1342
php_set_attribute_id(xmlAttrPtr attrp,bool is_id,php_libxml_ref_obj * document)1343 static 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
1357 Since: DOM Level 3
1358 */
PHP_METHOD(DOMElement,setIdAttribute)1359 PHP_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) {
1378 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
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
1386 Since: DOM Level 3
1387 */
PHP_METHOD(DOMElement,setIdAttributeNS)1388 PHP_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) {
1407 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
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
1415 Since: DOM Level 3
1416 */
dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAMETERS,zend_class_entry * attr_ce)1417 static 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) {
1434 php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1435 } else {
1436 php_set_attribute_id(attrp, is_id, intern->document);
1437 }
1438 }
1439
PHP_METHOD(DOMElement,setIdAttributeNode)1440 PHP_METHOD(DOMElement, setIdAttributeNode)
1441 {
1442 dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
1443 }
1444
PHP_METHOD(Dom_Element,setIdAttributeNode)1445 PHP_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:
1452 Since:
1453 */
PHP_METHOD(DOMElement,remove)1454 PHP_METHOD(DOMElement, remove)
1455 {
1456 dom_object *intern;
1457
1458 if (zend_parse_parameters_none() == FAILURE) {
1459 RETURN_THROWS();
1460 }
1461
1462 DOM_GET_THIS_INTERN(intern);
1463
1464 dom_child_node_remove(intern);
1465 }
1466 /* }}} end DOMElement::remove */
1467
PHP_METHOD(DOMElement,after)1468 PHP_METHOD(DOMElement, after)
1469 {
1470 uint32_t argc = 0;
1471 zval *args;
1472 dom_object *intern;
1473
1474 ZEND_PARSE_PARAMETERS_START(0, -1)
1475 Z_PARAM_VARIADIC('*', args, argc)
1476 ZEND_PARSE_PARAMETERS_END();
1477
1478 DOM_GET_THIS_INTERN(intern);
1479
1480 dom_parent_node_after(intern, args, argc);
1481 }
1482
PHP_METHOD(DOMElement,before)1483 PHP_METHOD(DOMElement, before)
1484 {
1485 uint32_t argc = 0;
1486 zval *args;
1487 dom_object *intern;
1488
1489 ZEND_PARSE_PARAMETERS_START(0, -1)
1490 Z_PARAM_VARIADIC('*', args, argc)
1491 ZEND_PARSE_PARAMETERS_END();
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
1499 Since: DOM Living Standard (DOM4)
1500 */
PHP_METHOD(DOMElement,append)1501 PHP_METHOD(DOMElement, append)
1502 {
1503 uint32_t argc = 0;
1504 zval *args;
1505 dom_object *intern;
1506
1507 ZEND_PARSE_PARAMETERS_START(0, -1)
1508 Z_PARAM_VARIADIC('*', args, argc)
1509 ZEND_PARSE_PARAMETERS_END();
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
1518 Since: DOM Living Standard (DOM4)
1519 */
PHP_METHOD(DOMElement,prepend)1520 PHP_METHOD(DOMElement, prepend)
1521 {
1522 uint32_t argc = 0;
1523 zval *args;
1524 dom_object *intern;
1525
1526 ZEND_PARSE_PARAMETERS_START(0, -1)
1527 Z_PARAM_VARIADIC('*', args, argc)
1528 ZEND_PARSE_PARAMETERS_END();
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
1537 Since: DOM Living Standard (DOM4)
1538 */
PHP_METHOD(DOMElement,replaceWith)1539 PHP_METHOD(DOMElement, replaceWith)
1540 {
1541 uint32_t argc = 0;
1542 zval *args;
1543 dom_object *intern;
1544
1545 ZEND_PARSE_PARAMETERS_START(0, -1)
1546 Z_PARAM_VARIADIC('*', args, argc)
1547 ZEND_PARSE_PARAMETERS_END();
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
1556 Since:
1557 */
PHP_METHOD(DOMElement,replaceChildren)1558 PHP_METHOD(DOMElement, replaceChildren)
1559 {
1560 uint32_t argc = 0;
1561 zval *args;
1562 dom_object *intern;
1563
1564 ZEND_PARSE_PARAMETERS_START(0, -1)
1565 Z_PARAM_VARIADIC('*', args, argc)
1566 ZEND_PARSE_PARAMETERS_END();
1567
1568 DOM_GET_THIS_INTERN(intern);
1569
1570 dom_parent_node_replace_children(intern, args, argc);
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
dom_insert_adjacent(const zend_string * where,xmlNodePtr thisp,dom_object * this_intern,xmlNodePtr otherp)1578 static 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 {
1615 php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document));
1616 return INSERT_ADJACENT_RES_SYNTAX_FAILED;
1617 }
1618 return otherp;
1619 }
1620
1621 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
1622 Since:
1623 */
dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAMETERS,const zend_string * where,zval * element_zval)1624 static 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
PHP_METHOD(DOMElement,insertAdjacentElement)1643 PHP_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
PHP_METHOD(Dom_Element,insertAdjacentElement)1655 PHP_METHOD(Dom_Element, insertAdjacentElement)
1656 {
1657 zval *element_zval, *where_zv;
1658
1659 ZEND_PARSE_PARAMETERS_START(2, 2)
1660 Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
1661 Z_PARAM_OBJECT_OF_CLASS(element_zval, dom_modern_element_class_entry)
1662 ZEND_PARSE_PARAMETERS_END();
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
1670 Since:
1671 */
dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAMETERS,const zend_string * where,const zend_string * data)1672 static 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
1680 if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(data)))) {
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
PHP_METHOD(DOMElement,insertAdjacentText)1692 PHP_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
PHP_METHOD(Dom_Element,insertAdjacentText)1703 PHP_METHOD(Dom_Element, insertAdjacentText)
1704 {
1705 zval *where_zv;
1706 zend_string *data;
1707
1708 ZEND_PARSE_PARAMETERS_START(2, 2)
1709 Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
1710 Z_PARAM_STR(data)
1711 ZEND_PARSE_PARAMETERS_END();
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 /* https://html.spec.whatwg.org/#dom-element-insertadjacenthtml */
PHP_METHOD(Dom_Element,insertAdjacentHTML)1719 PHP_METHOD(Dom_Element, insertAdjacentHTML)
1720 {
1721 zval *where_zv;
1722 zend_string *string;
1723
1724 dom_object *this_intern;
1725 zval *id;
1726 xmlNodePtr thisp;
1727
1728 bool created_context = false;
1729
1730 ZEND_PARSE_PARAMETERS_START(2, 2)
1731 Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
1732 Z_PARAM_STR(string)
1733 ZEND_PARSE_PARAMETERS_END();
1734
1735 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1736
1737 const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
1738
1739 /* 1. We don't do injection sinks. */
1740
1741 /* 2. Let context be NULL */
1742 xmlNodePtr context = NULL;
1743
1744 /* 3. Use the first matching item from this list: (...) */
1745 switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
1746 case sizeof("BeforeBegin") - 1 + 'f':
1747 case sizeof("AfterEnd") - 1 + 't':
1748 /* 1. Set context to this's parent. */
1749 context = thisp->parent;
1750
1751 /* 2. If context is null or a Document, throw a "NoModificationAllowedError" DOMException. */
1752 if (context == NULL || context->type == XML_DOCUMENT_NODE || context->type == XML_HTML_DOCUMENT_NODE) {
1753 php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, true);
1754 RETURN_THROWS();
1755 }
1756 break;
1757 case sizeof("AfterBegin") - 1 + 't':
1758 case sizeof("BeforeEnd") - 1 + 'f':
1759 /* Set context to this. */
1760 context = thisp;
1761 break;
1762 EMPTY_SWITCH_DEFAULT_CASE();
1763 }
1764
1765 /* 4. If context is not an Element or all of the following are true: (...) */
1766 if (context->type != XML_ELEMENT_NODE
1767 || (php_dom_ns_is_html_and_document_is_html(context) && xmlStrEqual(context->name, BAD_CAST "html"))) {
1768 /* set context to the result of creating an element given this's node document, body, and the HTML namespace. */
1769 xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_get_ns_mapper(this_intern));
1770
1771 context = xmlNewDocNode(thisp->doc, html_ns, BAD_CAST "body", NULL);
1772 created_context = true;
1773 if (UNEXPECTED(context == NULL)) {
1774 php_dom_throw_error(INVALID_STATE_ERR, true);
1775 goto err;
1776 }
1777 }
1778
1779 /* 5. Let fragment be the result of invoking the fragment parsing algorithm steps with context and compliantString. */
1780 xmlNodePtr fragment = dom_parse_fragment(this_intern, context, string);
1781 if (fragment == NULL) {
1782 goto err;
1783 }
1784
1785 php_libxml_invalidate_node_list_cache(this_intern->document);
1786
1787 /* 6. Use the first matching item from this list: (...) */
1788 switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
1789 case sizeof("BeforeBegin") - 1 + 'f':
1790 php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp);
1791 break;
1792 case sizeof("AfterEnd") - 1 + 't':
1793 php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp->next);
1794 break;
1795 case sizeof("AfterBegin") - 1 + 't':
1796 php_dom_pre_insert(this_intern->document, fragment, thisp, thisp->children);
1797 break;
1798 case sizeof("BeforeEnd") - 1 + 'f':
1799 php_dom_node_append(this_intern->document, fragment, thisp);
1800 break;
1801 EMPTY_SWITCH_DEFAULT_CASE();
1802 }
1803
1804 err:
1805 if (created_context) {
1806 xmlFreeNode(context);
1807 }
1808 }
1809
1810 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
1811 Since:
1812 */
PHP_METHOD(DOMElement,toggleAttribute)1813 PHP_METHOD(DOMElement, toggleAttribute)
1814 {
1815 char *qname, *qname_tmp = NULL;
1816 size_t qname_length;
1817 bool force, force_is_null = true;
1818 xmlNodePtr thisp;
1819 zval *id;
1820 dom_object *intern;
1821 bool retval;
1822
1823 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b!", &qname, &qname_length, &force, &force_is_null) == FAILURE) {
1824 RETURN_THROWS();
1825 }
1826
1827 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1828
1829 /* Step 1 */
1830 if (xmlValidateName(BAD_CAST qname, 0) != 0) {
1831 php_dom_throw_error(INVALID_CHARACTER_ERR, true);
1832 RETURN_THROWS();
1833 }
1834
1835 bool follow_spec = php_dom_follow_spec_intern(intern);
1836
1837 /* Step 2 */
1838 if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE
1839 && ((!follow_spec && thisp->ns == NULL) || (thisp->ns != NULL && xmlStrEqual(thisp->ns->href, BAD_CAST DOM_XHTML_NS_URI)))) {
1840 qname_tmp = zend_str_tolower_dup_ex(qname, qname_length);
1841 if (qname_tmp != NULL) {
1842 qname = qname_tmp;
1843 }
1844 }
1845
1846 /* Step 3 */
1847 xmlNodePtr attribute = dom_get_attribute_or_nsdecl(intern, thisp, BAD_CAST qname, qname_length);
1848
1849 /* Step 4 */
1850 if (attribute == NULL) {
1851 /* Step 4.1 */
1852 if (force_is_null || force) {
1853 if (follow_spec) {
1854 xmlSetNsProp(thisp, NULL, BAD_CAST qname, NULL);
1855 } else {
1856 /* The behaviour for namespaces isn't defined by spec, but this is based on observing browsers' behaviour.
1857 * It follows the same rules when you'd manually add an attribute using the other APIs. */
1858 int len;
1859 const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
1860 if (split == NULL || strncmp(qname, "xmlns:", len + 1 /* +1 for matching ':' too */) != 0) {
1861 /* unqualified name, or qualified name with no xml namespace declaration */
1862 dom_create_attribute(thisp, qname, "");
1863 } else {
1864 /* qualified name with xml namespace declaration */
1865 xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1));
1866 }
1867 }
1868 retval = true;
1869 goto out;
1870 }
1871 /* Step 4.2 */
1872 retval = false;
1873 goto out;
1874 }
1875
1876 /* Step 5 */
1877 if (force_is_null || !force) {
1878 dom_remove_attribute(thisp, attribute);
1879 retval = false;
1880 goto out;
1881 }
1882
1883 /* Step 6 */
1884 retval = true;
1885
1886 out:
1887 if (qname_tmp) {
1888 efree(qname_tmp);
1889 }
1890 RETURN_BOOL(retval);
1891 }
1892 /* }}} end DOMElement::prepend */
1893
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS,bool all)1894 static void php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS, bool all)
1895 {
1896 zend_string *selectors_str;
1897
1898 ZEND_PARSE_PARAMETERS_START(1, 1)
1899 Z_PARAM_STR(selectors_str)
1900 ZEND_PARSE_PARAMETERS_END();
1901
1902 xmlNodePtr thisp;
1903 dom_object *intern;
1904 zval *id;
1905 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1906
1907 if (all) {
1908 dom_parent_node_query_selector_all(thisp, intern, return_value, selectors_str);
1909 } else {
1910 dom_parent_node_query_selector(thisp, intern, return_value, selectors_str);
1911 }
1912 }
1913
PHP_METHOD(Dom_Element,querySelector)1914 PHP_METHOD(Dom_Element, querySelector)
1915 {
1916 php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1917 }
1918
PHP_METHOD(Dom_Element,querySelectorAll)1919 PHP_METHOD(Dom_Element, querySelectorAll)
1920 {
1921 php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1922 }
1923
PHP_METHOD(Dom_Element,matches)1924 PHP_METHOD(Dom_Element, matches)
1925 {
1926 zend_string *selectors_str;
1927
1928 ZEND_PARSE_PARAMETERS_START(1, 1)
1929 Z_PARAM_STR(selectors_str)
1930 ZEND_PARSE_PARAMETERS_END();
1931
1932 xmlNodePtr thisp;
1933 dom_object *intern;
1934 zval *id;
1935 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1936
1937 dom_element_matches(thisp, intern, return_value, selectors_str);
1938 }
1939
PHP_METHOD(Dom_Element,closest)1940 PHP_METHOD(Dom_Element, closest)
1941 {
1942 zend_string *selectors_str;
1943
1944 ZEND_PARSE_PARAMETERS_START(1, 1)
1945 Z_PARAM_STR(selectors_str)
1946 ZEND_PARSE_PARAMETERS_END();
1947
1948 xmlNodePtr thisp;
1949 dom_object *intern;
1950 zval *id;
1951 DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1952
1953 dom_element_closest(thisp, intern, return_value, selectors_str);
1954 }
1955
dom_modern_element_substituted_node_value_read(dom_object * obj,zval * retval)1956 zend_result dom_modern_element_substituted_node_value_read(dom_object *obj, zval *retval)
1957 {
1958 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
1959
1960 xmlChar *content = xmlNodeGetContent(nodep);
1961
1962 if (UNEXPECTED(content == NULL)) {
1963 php_dom_throw_error(INVALID_STATE_ERR, true);
1964 return FAILURE;
1965 } else {
1966 ZVAL_STRING(retval, (const char *) content);
1967 xmlFree(content);
1968 }
1969
1970 return SUCCESS;
1971 }
1972
dom_modern_element_substituted_node_value_write(dom_object * obj,zval * newval)1973 zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zval *newval)
1974 {
1975 DOM_PROP_NODE(xmlNodePtr, nodep, obj);
1976
1977 php_libxml_invalidate_node_list_cache(obj->document);
1978 dom_remove_all_children(nodep);
1979 xmlNodeSetContentLen(nodep, (xmlChar *) Z_STRVAL_P(newval), Z_STRLEN_P(newval));
1980
1981 return SUCCESS;
1982 }
1983
dom_element_get_in_scope_namespace_info(php_dom_libxml_ns_mapper * ns_mapper,HashTable * result,xmlNodePtr nodep,dom_object * intern)1984 static void dom_element_get_in_scope_namespace_info(php_dom_libxml_ns_mapper *ns_mapper, HashTable *result, xmlNodePtr nodep, dom_object *intern)
1985 {
1986 HashTable prefix_to_ns_table;
1987 zend_hash_init(&prefix_to_ns_table, 0, NULL, NULL, false);
1988 zend_hash_real_init_mixed(&prefix_to_ns_table);
1989
1990 /* https://www.w3.org/TR/1999/REC-xpath-19991116/#namespace-nodes */
1991 for (const xmlNode *cur = nodep; cur != NULL; cur = cur->parent) {
1992 if (cur->type == XML_ELEMENT_NODE) {
1993 /* Find the last attribute */
1994 const xmlAttr *last = NULL;
1995 for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) {
1996 last = attr;
1997 }
1998
1999 /* Reversed loop because the parent traversal is reversed as well,
2000 * this will keep the ordering consistent. */
2001 for (const xmlAttr *attr = last; attr != NULL; attr = attr->prev) {
2002 if (attr->ns != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token)
2003 && attr->children != NULL && attr->children->content != NULL) {
2004 const char *prefix = attr->ns->prefix == NULL ? NULL : (const char *) attr->name;
2005 const char *key = prefix == NULL ? "" : prefix;
2006 xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, prefix, (const char *) attr->children->content);
2007 /* NULL is a valid value for the sentinel */
2008 zval zv;
2009 ZVAL_PTR(&zv, ns);
2010 zend_hash_str_add(&prefix_to_ns_table, key, strlen(key), &zv);
2011 }
2012 }
2013 }
2014 }
2015
2016 xmlNsPtr ns;
2017 zend_string *prefix;
2018 ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR(&prefix_to_ns_table, prefix, ns) {
2019 if (ZSTR_LEN(prefix) == 0 && (ns == NULL || ns->href == NULL || *ns->href == '\0')) {
2020 /* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */
2021 continue;
2022 }
2023
2024 zval zv;
2025 object_init_ex(&zv, dom_namespace_info_class_entry);
2026 zend_object *obj = Z_OBJ(zv);
2027
2028 if (ZSTR_LEN(prefix) != 0) {
2029 ZVAL_STR_COPY(OBJ_PROP_NUM(obj, 0), prefix);
2030 } else {
2031 ZVAL_NULL(OBJ_PROP_NUM(obj, 0));
2032 }
2033
2034 if (ns != NULL && ns->href != NULL && *ns->href != '\0') {
2035 ZVAL_STRING(OBJ_PROP_NUM(obj, 1), (const char *) ns->href);
2036 } else {
2037 ZVAL_NULL(OBJ_PROP_NUM(obj, 1));
2038 }
2039
2040 php_dom_create_object(nodep, OBJ_PROP_NUM(obj, 2), intern);
2041
2042 zend_hash_next_index_insert_new(result, &zv);
2043 } ZEND_HASH_FOREACH_END();
2044
2045 zend_hash_destroy(&prefix_to_ns_table);
2046 }
2047
PHP_METHOD(Dom_Element,getInScopeNamespaces)2048 PHP_METHOD(Dom_Element, getInScopeNamespaces)
2049 {
2050 zval *id;
2051 xmlNode *nodep;
2052 dom_object *intern;
2053
2054 ZEND_PARSE_PARAMETERS_NONE();
2055
2056 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2057
2058 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
2059
2060 array_init(return_value);
2061 HashTable *result = Z_ARRVAL_P(return_value);
2062
2063 dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
2064 }
2065
PHP_METHOD(Dom_Element,getDescendantNamespaces)2066 PHP_METHOD(Dom_Element, getDescendantNamespaces)
2067 {
2068 zval *id;
2069 xmlNode *nodep;
2070 dom_object *intern;
2071
2072 ZEND_PARSE_PARAMETERS_NONE();
2073
2074 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2075
2076 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
2077
2078 array_init(return_value);
2079 HashTable *result = Z_ARRVAL_P(return_value);
2080
2081 dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
2082
2083 xmlNodePtr cur = nodep->children;
2084 while (cur != NULL) {
2085 if (cur->type == XML_ELEMENT_NODE) {
2086 /* TODO: this could be more optimized by updating the same HashTable repeatedly
2087 * instead of recreating it on every node. */
2088 dom_element_get_in_scope_namespace_info(ns_mapper, result, cur, intern);
2089 }
2090
2091 cur = php_dom_next_in_tree_order(cur, nodep);
2092 }
2093 }
2094
PHP_METHOD(Dom_Element,rename)2095 PHP_METHOD(Dom_Element, rename)
2096 {
2097 zend_string *namespace_uri, *qualified_name;
2098 ZEND_PARSE_PARAMETERS_START(2, 2)
2099 Z_PARAM_STR_OR_NULL(namespace_uri)
2100 Z_PARAM_STR(qualified_name)
2101 ZEND_PARSE_PARAMETERS_END();
2102
2103 zval *id;
2104 dom_object *intern;
2105 xmlNodePtr nodep;
2106 DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
2107
2108 xmlChar *localname = NULL, *prefix = NULL;
2109 int errorcode = dom_validate_and_extract(namespace_uri, qualified_name, &localname, &prefix);
2110 if (UNEXPECTED(errorcode != 0)) {
2111 php_dom_throw_error(errorcode, /* strict */ true);
2112 goto cleanup;
2113 }
2114
2115 if (nodep->type == XML_ATTRIBUTE_NODE) {
2116 /* Check for duplicate attributes. */
2117 xmlAttrPtr existing = xmlHasNsProp(nodep->parent, localname, namespace_uri && ZSTR_VAL(namespace_uri)[0] != '\0' ? BAD_CAST ZSTR_VAL(namespace_uri) : NULL);
2118 if (existing != NULL && existing != (xmlAttrPtr) nodep) {
2119 php_dom_throw_error_with_message(INVALID_MODIFICATION_ERR, "An attribute with the given name in the given namespace already exists", /* strict */ true);
2120 goto cleanup;
2121 }
2122 } else {
2123 ZEND_ASSERT(nodep->type == XML_ELEMENT_NODE);
2124
2125 /* Check for moving to or away from the HTML namespace. */
2126 bool is_currently_html_ns = php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token);
2127 bool will_be_html_ns = namespace_uri != NULL && zend_string_equals_literal(namespace_uri, DOM_XHTML_NS_URI);
2128 if (is_currently_html_ns != will_be_html_ns) {
2129 if (is_currently_html_ns) {
2130 php_dom_throw_error_with_message(
2131 INVALID_MODIFICATION_ERR,
2132 "It is not possible to move an element out of the HTML namespace because the HTML namespace is tied to the HTMLElement class",
2133 /* strict */ true
2134 );
2135 } else {
2136 php_dom_throw_error_with_message(
2137 INVALID_MODIFICATION_ERR,
2138 "It is not possible to move an element into the HTML namespace because the HTML namespace is tied to the HTMLElement class",
2139 /* strict */ true
2140 );
2141 }
2142 goto cleanup;
2143 }
2144
2145 /* If we currently have a template but the new element type won't be a template, then throw away the templated content. */
2146 if (is_currently_html_ns && xmlStrEqual(nodep->name, BAD_CAST "template") && !xmlStrEqual(localname, BAD_CAST "template")) {
2147 php_dom_throw_error_with_message(
2148 INVALID_MODIFICATION_ERR,
2149 "It is not possible to rename the template element because it hosts a document fragment",
2150 /* strict */ true
2151 );
2152 goto cleanup;
2153 }
2154 }
2155
2156 php_libxml_invalidate_node_list_cache(intern->document);
2157
2158 php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
2159
2160 /* Update namespace uri + prefix by querying the namespace mapper */
2161 /* prefix can be NULL here, but that is taken care of by the called APIs. */
2162 nodep->ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), namespace_uri);
2163
2164 /* Change the local name */
2165 if (xmlDictOwns(nodep->doc->dict, nodep->name) != 1) {
2166 xmlFree((xmlChar *) nodep->name);
2167 }
2168 const xmlChar *copy = xmlDictLookup(nodep->doc->dict, localname, -1);
2169 if (copy != NULL) {
2170 nodep->name = copy;
2171 } else {
2172 nodep->name = localname;
2173 localname = NULL;
2174 }
2175
2176 cleanup:
2177 xmlFree(localname);
2178 xmlFree(prefix);
2179 }
2180
2181 #endif
2182