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