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