xref: /PHP-8.2/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 zend_result 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 zend_result 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 zend_result 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 zend_result 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 zend_result 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 zend_result 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 zend_result 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 _dom_deep_ns_redef_item {
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 	ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
618 
619 	if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) {
620 		php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
621 		RETURN_FALSE;
622 	}
623 
624 	existattrp = xmlHasProp(nodep, attrp->name);
625 	if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
626 		if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
627 			((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
628 		{
629 			RETURN_NULL();
630 		}
631 		xmlUnlinkNode((xmlNodePtr) existattrp);
632 	}
633 
634 	if (attrp->parent != NULL) {
635 		xmlUnlinkNode((xmlNodePtr) attrp);
636 	}
637 
638 	if (attrp->doc == NULL && nodep->doc != NULL) {
639 		attrobj->document = intern->document;
640 		php_libxml_increment_doc_ref((php_libxml_node_object *)attrobj, NULL);
641 	}
642 
643 	xmlAddChild(nodep, (xmlNodePtr) attrp);
644 	php_dom_reconcile_attribute_namespace_after_insertion(attrp);
645 
646 	/* Returns old property if removed otherwise NULL */
647 	if (existattrp != NULL) {
648 		DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern);
649 	} else {
650 		RETVAL_NULL();
651 	}
652 
653 }
654 /* }}} end dom_element_set_attribute_node */
655 
656 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198
657 Since:
658 */
PHP_METHOD(DOMElement,removeAttributeNode)659 PHP_METHOD(DOMElement, removeAttributeNode)
660 {
661 	zval *id, *node;
662 	xmlNode *nodep;
663 	xmlAttr *attrp;
664 	dom_object *intern, *attrobj;
665 	int ret;
666 
667 	id = ZEND_THIS;
668 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_attr_class_entry) == FAILURE) {
669 		RETURN_THROWS();
670 	}
671 
672 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
673 
674 	DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
675 
676 	ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
677 
678 	if (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 	php_dom_reconcile_attribute_namespace_after_insertion(attrp);
1088 
1089 	/* Returns old property if removed otherwise NULL */
1090 	if (existattrp != NULL) {
1091 		DOM_RET_OBJ((xmlNodePtr) existattrp, &ret, intern);
1092 	} else {
1093 		RETVAL_NULL();
1094 	}
1095 
1096 }
1097 /* }}} end dom_element_set_attribute_node_ns */
1098 
1099 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942
1100 Since: DOM Level 2
1101 */
PHP_METHOD(DOMElement,getElementsByTagNameNS)1102 PHP_METHOD(DOMElement, getElementsByTagNameNS)
1103 {
1104 	size_t uri_len, name_len;
1105 	dom_object *intern, *namednode;
1106 	char *uri, *name;
1107 
1108 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1109 		RETURN_THROWS();
1110 	}
1111 
1112 	DOM_GET_THIS_INTERN(intern);
1113 
1114 	php_dom_create_iterator(return_value, DOM_NODELIST);
1115 	namednode = Z_DOMOBJ_P(return_value);
1116 	dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
1117 
1118 }
1119 /* }}} end dom_element_get_elements_by_tag_name_ns */
1120 
1121 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr
1122 Since: DOM Level 2
1123 */
PHP_METHOD(DOMElement,hasAttribute)1124 PHP_METHOD(DOMElement, hasAttribute)
1125 {
1126 	zval *id;
1127 	xmlNode *nodep;
1128 	dom_object *intern;
1129 	char *name;
1130 	size_t name_len;
1131 	xmlNodePtr attr;
1132 
1133 	id = ZEND_THIS;
1134 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
1135 		RETURN_THROWS();
1136 	}
1137 
1138 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1139 
1140 	attr = dom_get_dom1_attribute(nodep, (xmlChar *)name);
1141 	if (attr == NULL) {
1142 		RETURN_FALSE;
1143 	} else {
1144 		RETURN_TRUE;
1145 	}
1146 }
1147 /* }}} end dom_element_has_attribute */
1148 
1149 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS
1150 Since: DOM Level 2
1151 */
PHP_METHOD(DOMElement,hasAttributeNS)1152 PHP_METHOD(DOMElement, hasAttributeNS)
1153 {
1154 	zval *id;
1155 	xmlNodePtr elemp;
1156 	xmlNs *nsp;
1157 	dom_object *intern;
1158 	size_t uri_len, name_len;
1159 	char *uri, *name;
1160 	xmlChar *value;
1161 
1162 	id = ZEND_THIS;
1163 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
1164 		RETURN_THROWS();
1165 	}
1166 
1167 	DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1168 
1169 	value = xmlGetNsProp(elemp, (xmlChar *)name, (xmlChar *)uri);
1170 
1171 	if (value != NULL) {
1172 		xmlFree(value);
1173 		RETURN_TRUE;
1174 	} else {
1175 		if (xmlStrEqual((xmlChar *)uri, (xmlChar *)DOM_XMLNS_NAMESPACE)) {
1176 			nsp = dom_get_nsdecl(elemp, (xmlChar *)name);
1177 			if (nsp != NULL) {
1178 				RETURN_TRUE;
1179 			}
1180 		}
1181 	}
1182 
1183 	RETURN_FALSE;
1184 }
1185 /* }}} end dom_element_has_attribute_ns */
1186 
php_set_attribute_id(xmlAttrPtr attrp,bool is_id)1187 static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id) /* {{{ */
1188 {
1189 	if (is_id == 1 && attrp->atype != XML_ATTRIBUTE_ID) {
1190 		xmlChar *id_val;
1191 
1192 		id_val = xmlNodeListGetString(attrp->doc, attrp->children, 1);
1193 		if (id_val != NULL) {
1194 			xmlAddID(NULL, attrp->doc, id_val, attrp);
1195 			xmlFree(id_val);
1196 		}
1197 	} else if (is_id == 0 && attrp->atype == XML_ATTRIBUTE_ID) {
1198 		xmlRemoveID(attrp->doc, attrp);
1199 		attrp->atype = 0;
1200 	}
1201 }
1202 /* }}} */
1203 
1204 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr
1205 Since: DOM Level 3
1206 */
PHP_METHOD(DOMElement,setIdAttribute)1207 PHP_METHOD(DOMElement, setIdAttribute)
1208 {
1209 	zval *id;
1210 	xmlNode *nodep;
1211 	xmlAttrPtr attrp;
1212 	dom_object *intern;
1213 	char *name;
1214 	size_t name_len;
1215 	bool is_id;
1216 
1217 	id = ZEND_THIS;
1218 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "sb", &name, &name_len, &is_id) == FAILURE) {
1219 		RETURN_THROWS();
1220 	}
1221 
1222 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1223 
1224 	attrp = xmlHasNsProp(nodep, (xmlChar *)name, NULL);
1225 	if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1226 		php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1227 	} else {
1228 		php_set_attribute_id(attrp, is_id);
1229 	}
1230 
1231 	RETURN_NULL();
1232 }
1233 /* }}} end dom_element_set_id_attribute */
1234 
1235 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS
1236 Since: DOM Level 3
1237 */
PHP_METHOD(DOMElement,setIdAttributeNS)1238 PHP_METHOD(DOMElement, setIdAttributeNS)
1239 {
1240 	zval *id;
1241 	xmlNodePtr elemp;
1242 	xmlAttrPtr attrp;
1243 	dom_object *intern;
1244 	size_t uri_len, name_len;
1245 	char *uri, *name;
1246 	bool is_id;
1247 
1248 	id = ZEND_THIS;
1249 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssb", &uri, &uri_len, &name, &name_len, &is_id) == FAILURE) {
1250 		RETURN_THROWS();
1251 	}
1252 
1253 	DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
1254 
1255 	attrp = xmlHasNsProp(elemp, (xmlChar *)name, (xmlChar *)uri);
1256 	if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
1257 		php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1258 	} else {
1259 		php_set_attribute_id(attrp, is_id);
1260 	}
1261 
1262 	RETURN_NULL();
1263 }
1264 /* }}} end dom_element_set_id_attribute_ns */
1265 
1266 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode
1267 Since: DOM Level 3
1268 */
PHP_METHOD(DOMElement,setIdAttributeNode)1269 PHP_METHOD(DOMElement, setIdAttributeNode)
1270 {
1271 	zval *id, *node;
1272 	xmlNode *nodep;
1273 	xmlAttrPtr attrp;
1274 	dom_object *intern, *attrobj;
1275 	bool is_id;
1276 
1277 	id = ZEND_THIS;
1278 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, dom_attr_class_entry, &is_id) == FAILURE) {
1279 		RETURN_THROWS();
1280 	}
1281 
1282 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
1283 	DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
1284 
1285 	if (attrp->parent != nodep) {
1286 		php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
1287 	} else {
1288 		php_set_attribute_id(attrp, is_id);
1289 	}
1290 
1291 	RETURN_NULL();
1292 }
1293 /* }}} end dom_element_set_id_attribute_node */
1294 
1295 /* {{{ URL:
1296 Since:
1297 */
PHP_METHOD(DOMElement,remove)1298 PHP_METHOD(DOMElement, remove)
1299 {
1300 	dom_object *intern;
1301 
1302 	if (zend_parse_parameters_none() == FAILURE) {
1303 		RETURN_THROWS();
1304 	}
1305 
1306 	DOM_GET_THIS_INTERN(intern);
1307 
1308 	dom_child_node_remove(intern);
1309 }
1310 /* }}} end DOMElement::remove */
1311 
PHP_METHOD(DOMElement,after)1312 PHP_METHOD(DOMElement, after)
1313 {
1314 	uint32_t argc = 0;
1315 	zval *args;
1316 	dom_object *intern;
1317 
1318 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1319 		RETURN_THROWS();
1320 	}
1321 
1322 	DOM_GET_THIS_INTERN(intern);
1323 
1324 	dom_parent_node_after(intern, args, argc);
1325 }
1326 
PHP_METHOD(DOMElement,before)1327 PHP_METHOD(DOMElement, before)
1328 {
1329 	uint32_t argc = 0;
1330 	zval *args;
1331 	dom_object *intern;
1332 
1333 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1334 		RETURN_THROWS();
1335 	}
1336 
1337 	DOM_GET_THIS_INTERN(intern);
1338 
1339 	dom_parent_node_before(intern, args, argc);
1340 }
1341 
1342 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
1343 Since: DOM Living Standard (DOM4)
1344 */
PHP_METHOD(DOMElement,append)1345 PHP_METHOD(DOMElement, append)
1346 {
1347 	uint32_t argc = 0;
1348 	zval *args;
1349 	dom_object *intern;
1350 
1351 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1352 		RETURN_THROWS();
1353 	}
1354 
1355 	DOM_GET_THIS_INTERN(intern);
1356 
1357 	dom_parent_node_append(intern, args, argc);
1358 }
1359 /* }}} end DOMElement::append */
1360 
1361 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
1362 Since: DOM Living Standard (DOM4)
1363 */
PHP_METHOD(DOMElement,prepend)1364 PHP_METHOD(DOMElement, prepend)
1365 {
1366 	uint32_t argc = 0;
1367 	zval *args;
1368 	dom_object *intern;
1369 
1370 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1371 		RETURN_THROWS();
1372 	}
1373 
1374 	DOM_GET_THIS_INTERN(intern);
1375 
1376 	dom_parent_node_prepend(intern, args, argc);
1377 }
1378 /* }}} end DOMElement::prepend */
1379 
1380 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1381 Since: DOM Living Standard (DOM4)
1382 */
PHP_METHOD(DOMElement,replaceWith)1383 PHP_METHOD(DOMElement, replaceWith)
1384 {
1385 	uint32_t argc = 0;
1386 	zval *args;
1387 	dom_object *intern;
1388 
1389 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1390 		RETURN_THROWS();
1391 	}
1392 
1393 	DOM_GET_THIS_INTERN(intern);
1394 
1395 	dom_child_replace_with(intern, args, argc);
1396 }
1397 /* }}} end DOMElement::prepend */
1398 
1399 /* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
1400 Since:
1401 */
PHP_METHOD(DOMElement,replaceChildren)1402 PHP_METHOD(DOMElement, replaceChildren)
1403 {
1404 	uint32_t argc = 0;
1405 	zval *args;
1406 	dom_object *intern;
1407 
1408 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "*", &args, &argc) == FAILURE) {
1409 		RETURN_THROWS();
1410 	}
1411 
1412 	DOM_GET_THIS_INTERN(intern);
1413 
1414 	dom_parent_node_replace_children(intern, args, argc);
1415 }
1416 /* }}} */
1417 
1418 #define INSERT_ADJACENT_RES_FAILED ((void*) -1)
1419 
dom_insert_adjacent(const zend_string * where,xmlNodePtr thisp,dom_object * this_intern,xmlNodePtr otherp)1420 static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp)
1421 {
1422 	if (zend_string_equals_literal_ci(where, "beforebegin")) {
1423 		if (thisp->parent == NULL) {
1424 			return NULL;
1425 		}
1426 		if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1427 			php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1428 			return INSERT_ADJACENT_RES_FAILED;
1429 		}
1430 		if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1431 			return INSERT_ADJACENT_RES_FAILED;
1432 		}
1433 		otherp = xmlAddPrevSibling(thisp, otherp);
1434 	} else if (zend_string_equals_literal_ci(where, "afterbegin")) {
1435 		if (dom_hierarchy(thisp, otherp) == FAILURE) {
1436 			php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1437 			return INSERT_ADJACENT_RES_FAILED;
1438 		}
1439 		if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1440 			return INSERT_ADJACENT_RES_FAILED;
1441 		}
1442 		if (thisp->children == NULL) {
1443 			otherp = xmlAddChild(thisp, otherp);
1444 		} else {
1445 			otherp = xmlAddPrevSibling(thisp->children, otherp);
1446 		}
1447 	} else if (zend_string_equals_literal_ci(where, "beforeend")) {
1448 		if (dom_hierarchy(thisp, otherp) == FAILURE) {
1449 			php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1450 			return INSERT_ADJACENT_RES_FAILED;
1451 		}
1452 		if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1453 			return INSERT_ADJACENT_RES_FAILED;
1454 		}
1455 		otherp = xmlAddChild(thisp, otherp);
1456 	} else if (zend_string_equals_literal_ci(where, "afterend")) {
1457 		if (thisp->parent == NULL) {
1458 			return NULL;
1459 		}
1460 		if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1461 			php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1462 			return INSERT_ADJACENT_RES_FAILED;
1463 		}
1464 		if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1465 			return INSERT_ADJACENT_RES_FAILED;
1466 		}
1467 		otherp = xmlAddNextSibling(thisp, otherp);
1468 	} else {
1469 		php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document));
1470 		return INSERT_ADJACENT_RES_FAILED;
1471 	}
1472 	dom_reconcile_ns(thisp->doc, otherp);
1473 	return otherp;
1474 }
1475 
1476 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
1477 Since:
1478 */
PHP_METHOD(DOMElement,insertAdjacentElement)1479 PHP_METHOD(DOMElement, insertAdjacentElement)
1480 {
1481 	zend_string *where;
1482 	zval *element_zval, *id;
1483 	xmlNodePtr thisp, otherp;
1484 	dom_object *this_intern, *other_intern;
1485 	int ret;
1486 
1487 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) == FAILURE) {
1488 		RETURN_THROWS();
1489 	}
1490 
1491 	DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1492 	DOM_GET_OBJ(otherp, element_zval, xmlNodePtr, other_intern);
1493 
1494 	xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1495 	if (result == NULL) {
1496 		RETURN_NULL();
1497 	} else if (result != INSERT_ADJACENT_RES_FAILED) {
1498 		DOM_RET_OBJ(otherp, &ret, other_intern);
1499 	} else {
1500 		RETURN_THROWS();
1501 	}
1502 }
1503 /* }}} end DOMElement::insertAdjacentElement */
1504 
1505 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
1506 Since:
1507 */
PHP_METHOD(DOMElement,insertAdjacentText)1508 PHP_METHOD(DOMElement, insertAdjacentText)
1509 {
1510 	zend_string *where, *data;
1511 	dom_object *this_intern;
1512 	zval *id;
1513 	xmlNodePtr thisp;
1514 
1515 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &where, &data) == FAILURE) {
1516 		RETURN_THROWS();
1517 	}
1518 
1519 	DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1520 
1521 	if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(data)))) {
1522 		zend_argument_value_error(2, "is too long");
1523 		RETURN_THROWS();
1524 	}
1525 
1526 	xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data));
1527 	xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1528 	if (result == NULL || result == INSERT_ADJACENT_RES_FAILED) {
1529 		xmlFreeNode(otherp);
1530 	}
1531 }
1532 /* }}} end DOMElement::insertAdjacentText */
1533 /* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
1534 Since:
1535 */
PHP_METHOD(DOMElement,toggleAttribute)1536 PHP_METHOD(DOMElement, toggleAttribute)
1537 {
1538 	char *qname, *qname_tmp = NULL;
1539 	size_t qname_length;
1540 	bool force, force_is_null = true;
1541 	xmlNodePtr thisp;
1542 	zval *id;
1543 	dom_object *intern;
1544 	bool retval;
1545 
1546 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b!", &qname, &qname_length, &force, &force_is_null) == FAILURE) {
1547 		RETURN_THROWS();
1548 	}
1549 
1550 	DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1551 
1552 	/* Step 1 */
1553 	if (xmlValidateName((xmlChar *) qname, 0) != 0) {
1554 		php_dom_throw_error(INVALID_CHARACTER_ERR, 1);
1555 		RETURN_THROWS();
1556 	}
1557 
1558 	/* Step 2 */
1559 	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"))) {
1560 		qname_tmp = zend_str_tolower_dup_ex(qname, qname_length);
1561 		if (qname_tmp != NULL) {
1562 			qname = qname_tmp;
1563 		}
1564 	}
1565 
1566 	/* Step 3 */
1567 	xmlNodePtr attribute = dom_get_dom1_attribute(thisp, (xmlChar *) qname);
1568 
1569 	/* Step 4 */
1570 	if (attribute == NULL) {
1571 		/* Step 4.1 */
1572 		if (force_is_null || force) {
1573 			/* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour.
1574 			 * It follows the same rules when you'd manually add an attribute using the other APIs. */
1575 			int len;
1576 			const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
1577 			if (split == NULL || strncmp(qname, "xmlns:", len + 1) != 0) {
1578 				/* unqualified name, or qualified name with no xml namespace declaration */
1579 				dom_create_attribute(thisp, qname, "");
1580 			} else {
1581 				/* qualified name with xml namespace declaration */
1582 				xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1));
1583 			}
1584 			retval = true;
1585 			goto out;
1586 		}
1587 		/* Step 4.2 */
1588 		retval = false;
1589 		goto out;
1590 	}
1591 
1592 	/* Step 5 */
1593 	if (force_is_null || !force) {
1594 		dom_remove_attribute(thisp, attribute);
1595 		retval = false;
1596 		goto out;
1597 	}
1598 
1599 	/* Step 6 */
1600 	retval = true;
1601 
1602 out:
1603 	if (qname_tmp) {
1604 		efree(qname_tmp);
1605 	}
1606 	RETURN_BOOL(retval);
1607 }
1608 /* }}} end DOMElement::prepend */
1609 
1610 #endif
1611