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