php-src/ext/dom/element.c
Niels Dossche d70f3ba9a5
Fix GH-16465: Heap buffer overflow in DOMNode->getElementByTagName
If the input contains NUL bytes then the length doesn't match the actual
duplicated string's length. Note that libxml can't handle this properly
anyway so we just reject NUL bytes and too long strings.

Closes GH-16467.
2024-10-16 22:55:18 +02:00

2090 lines
58 KiB
C

/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Christian Stocker <chregu@php.net> |
| Rob Richards <rrichards@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "php.h"
#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
#include "zend_enum.h"
#include "php_dom.h"
#include "namespace_compat.h"
#include "private_data.h"
#include "internal_helpers.h"
#include "dom_properties.h"
#include "token_list.h"
/*
* class DOMElement extends DOMNode
*
* URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-745549614
* Since:
*/
/* {{{ */
PHP_METHOD(DOMElement, __construct)
{
xmlNodePtr nodep = NULL, oldnode = NULL;
dom_object *intern;
char *name, *value = NULL, *uri = NULL;
char *localname = NULL, *prefix = NULL;
int errorcode = 0;
size_t name_len, value_len = 0, uri_len = 0;
int name_valid;
xmlNsPtr nsptr = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s!s", &name, &name_len, &value, &value_len, &uri, &uri_len) == FAILURE) {
RETURN_THROWS();
}
name_valid = xmlValidateName(BAD_CAST name, 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, true);
RETURN_THROWS();
}
/* Namespace logic is separate and only when uri passed in to insure no BC breakage */
if (uri_len > 0) {
errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
if (errorcode == 0) {
nodep = xmlNewNode (NULL, BAD_CAST localname);
if (nodep != NULL && uri != NULL) {
nsptr = dom_get_ns(nodep, uri, &errorcode, prefix);
xmlSetNs(nodep, nsptr);
}
}
xmlFree(localname);
if (prefix != NULL) {
xmlFree(prefix);
}
if (errorcode != 0) {
if (nodep != NULL) {
xmlFreeNode(nodep);
}
php_dom_throw_error(errorcode, true);
RETURN_THROWS();
}
} else {
/* If you don't pass a namespace uri, then you can't set a prefix */
localname = (char *) xmlSplitQName2(BAD_CAST name, (xmlChar **) &prefix);
if (prefix != NULL) {
xmlFree(localname);
xmlFree(prefix);
php_dom_throw_error(NAMESPACE_ERR, true);
RETURN_THROWS();
}
nodep = xmlNewNode(NULL, BAD_CAST name);
}
if (!nodep) {
php_dom_throw_error(INVALID_STATE_ERR, true);
RETURN_THROWS();
}
if (value_len > 0) {
xmlNodeSetContentLen(nodep, BAD_CAST value, value_len);
}
intern = Z_DOMOBJ_P(ZEND_THIS);
oldnode = dom_object_get_node(intern);
if (oldnode != NULL) {
php_libxml_node_decrement_resource((php_libxml_node_object *)intern);
}
php_libxml_increment_node_ptr((php_libxml_node_object *)intern, nodep, (void *)intern);
}
/* }}} end DOMElement::__construct */
/* {{{ tagName string
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-104682815
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-tagname
Since:
*/
zend_result dom_element_tag_name_read(dom_object *obj, zval *retval)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
bool uppercase = php_dom_follow_spec_intern(obj) && php_dom_ns_is_html_and_document_is_html(nodep);
zend_string *result = dom_node_get_node_name_attribute_or_element((const xmlNode *) nodep, uppercase);
ZVAL_NEW_STR(retval, result);
return SUCCESS;
}
/* }}} */
static zend_result dom_element_reflected_attribute_read(dom_object *obj, zval *retval, const char *name)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
xmlChar *content = xmlGetNoNsProp(nodep, (const xmlChar *) name);
if (content == NULL) {
ZVAL_EMPTY_STRING(retval);
return SUCCESS;
}
ZVAL_STRING(retval, (const char *) content);
xmlFree(content);
return SUCCESS;
}
static xmlAttrPtr dom_element_reflected_attribute_write(dom_object *obj, zval *newval, const char *name)
{
xmlNode *nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, true);
return NULL;
}
/* Typed property, so it is a string already */
ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
return xmlSetNsProp(nodep, NULL, (const xmlChar *) name, (const xmlChar *) Z_STRVAL_P(newval));
}
/* {{{ className string
URL: https://dom.spec.whatwg.org/#dom-element-classname
Since:
*/
zend_result dom_element_class_name_read(dom_object *obj, zval *retval)
{
return dom_element_reflected_attribute_read(obj, retval, "class");
}
zend_result dom_element_class_name_write(dom_object *obj, zval *newval)
{
if (dom_element_reflected_attribute_write(obj, newval, "class")) {
return SUCCESS;
}
return FAILURE;
}
/* }}} */
/* {{{ classList TokenList
URL: https://dom.spec.whatwg.org/#dom-element-classlist
*/
zend_result dom_element_class_list_read(dom_object *obj, zval *retval)
{
const uint32_t PROP_INDEX = 0;
#if ZEND_DEBUG
zend_string *class_list_str = ZSTR_INIT_LITERAL("classList", false);
const zend_property_info *prop_info = zend_get_property_info(dom_modern_element_class_entry, class_list_str, 0);
zend_string_release_ex(class_list_str, false);
ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == PROP_INDEX);
#endif
zval *cached_token_list = OBJ_PROP_NUM(&obj->std, PROP_INDEX);
if (Z_ISUNDEF_P(cached_token_list)) {
object_init_ex(cached_token_list, dom_token_list_class_entry);
dom_token_list_object *intern = php_dom_token_list_from_obj(Z_OBJ_P(cached_token_list));
dom_token_list_ctor(intern, obj);
}
ZVAL_OBJ_COPY(retval, Z_OBJ_P(cached_token_list));
return SUCCESS;
}
/* }}} */
/* {{{ id string
URL: https://dom.spec.whatwg.org/#dom-element-id
Since:
*/
zend_result dom_element_id_read(dom_object *obj, zval *retval)
{
return dom_element_reflected_attribute_read(obj, retval, "id");
}
static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id, php_libxml_ref_obj *document);
zend_result dom_element_id_write(dom_object *obj, zval *newval)
{
xmlAttrPtr attr = dom_element_reflected_attribute_write(obj, newval, "id");
if (!attr) {
return FAILURE;
}
php_set_attribute_id(attr, true, obj->document);
return SUCCESS;
}
/* }}} */
/* {{{ schemaTypeInfo typeinfo
readonly=yes
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#Element-schemaTypeInfo
Since: DOM Level 3
*/
zend_result dom_element_schema_type_info_read(dom_object *obj, zval *retval)
{
ZVAL_NULL(retval);
return SUCCESS;
}
/* }}} */
/* Note: the object returned is not necessarily a node, but can be an attribute or a namespace declaration. */
static xmlNodePtr dom_get_attribute_or_nsdecl(dom_object *intern, xmlNodePtr elem, const xmlChar *name, size_t name_len) /* {{{ */
{
if (!php_dom_follow_spec_intern(intern)) {
int len;
const xmlChar *nqname = xmlSplitQName3(name, &len);
if (nqname != NULL) {
xmlNsPtr ns;
if (strncmp((const char *) name, "xmlns:", len + 1) == 0) {
ns = elem->nsDef;
while (ns) {
if (xmlStrEqual(ns->prefix, nqname)) {
break;
}
ns = ns->next;
}
return (xmlNodePtr)ns;
}
xmlChar *prefix = xmlStrndup(name, len);
ns = xmlSearchNs(elem->doc, elem, prefix);
if (prefix != NULL) {
xmlFree(prefix);
}
if (ns != NULL) {
return (xmlNodePtr)xmlHasNsProp(elem, nqname, ns->href);
}
} else {
if (xmlStrEqual(name, BAD_CAST "xmlns")) {
xmlNsPtr nsPtr = elem->nsDef;
while (nsPtr) {
if (nsPtr->prefix == NULL) {
return (xmlNodePtr)nsPtr;
}
nsPtr = nsPtr->next;
}
return NULL;
}
}
return (xmlNodePtr) xmlHasNsProp(elem, name, NULL);
} else {
return (xmlNodePtr) php_dom_get_attribute_node(elem, name, name_len);
}
}
/* }}} */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-666EE0F9
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattribute
Since:
*/
PHP_METHOD(DOMElement, getAttribute)
{
zval *id;
xmlNode *nodep;
char *name;
xmlChar *value = NULL;
dom_object *intern;
xmlNodePtr attr;
size_t name_len;
bool should_free = false;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attr) {
switch (attr->type) {
case XML_ATTRIBUTE_NODE:
value = xmlNodeListGetString(attr->doc, attr->children, 1);
should_free = true;
break;
case XML_NAMESPACE_DECL:
value = BAD_CAST ((xmlNsPtr)attr)->href;
should_free = false;
break;
default:
value = BAD_CAST ((xmlAttributePtr)attr)->defaultValue;
should_free = false;
}
}
if (value == NULL) {
if (php_dom_follow_spec_intern(intern)) {
RETURN_NULL();
}
RETURN_EMPTY_STRING();
} else {
RETVAL_STRING((char *)value);
if (should_free) {
xmlFree(value);
}
}
}
/* }}} end dom_element_get_attribute */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-getattributenames
Since:
*/
PHP_METHOD(DOMElement, getAttributeNames)
{
zval *id;
xmlNode *nodep;
dom_object *intern;
zval tmp;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
array_init(return_value);
HashTable *ht = Z_ARRVAL_P(return_value);
zend_hash_real_init_packed(ht);
if (!php_dom_follow_spec_intern(intern)) {
for (xmlNsPtr nsptr = nodep->nsDef; nsptr; nsptr = nsptr->next) {
const char *prefix = (const char *) nsptr->prefix;
if (prefix == NULL) {
ZVAL_STRING(&tmp, "xmlns");
} else {
ZVAL_NEW_STR(&tmp, dom_node_concatenated_name_helper(strlen(prefix), prefix, strlen("xmlns"), (const char *) "xmlns"));
}
zend_hash_next_index_insert(ht, &tmp);
}
}
for (xmlAttrPtr attr = nodep->properties; attr; attr = attr->next) {
ZVAL_NEW_STR(&tmp, dom_node_get_node_name_attribute_or_element((const xmlNode *) attr, false));
zend_hash_next_index_insert(ht, &tmp);
}
}
/* }}} end DOMElement::getAttributeNames() */
static xmlNodePtr dom_create_attribute(xmlNodePtr nodep, const char *name, const char* value)
{
if (xmlStrEqual(BAD_CAST name, BAD_CAST "xmlns")) {
return (xmlNodePtr) xmlNewNs(nodep, BAD_CAST value, NULL);
} else {
return (xmlNodePtr) xmlSetProp(nodep, BAD_CAST name, BAD_CAST value);
}
}
static void dom_check_register_attribute_id(xmlAttrPtr attr, php_libxml_ref_obj *document)
{
dom_mark_ids_modified(document);
if (attr->atype != XML_ATTRIBUTE_ID && attr->doc->type == XML_HTML_DOCUMENT_NODE && attr->ns == NULL && xmlStrEqual(attr->name, BAD_CAST "id")) {
/* To respect XML's ID behaviour, we only do this registration for HTML documents. */
attr->atype = XML_ATTRIBUTE_ID;
}
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68F082
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattribute
Since:
*/
PHP_METHOD(DOMElement, setAttribute)
{
zval *id;
xmlNode *nodep;
int name_valid;
size_t name_len, value_len;
dom_object *intern;
char *name, *value;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &name, &name_len, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
if (name_len == 0) {
zend_argument_must_not_be_empty_error(1);
RETURN_THROWS();
}
name_valid = xmlValidateName(BAD_CAST name, 0);
if (name_valid != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, true);
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
if (php_dom_follow_spec_intern(intern)) {
xmlChar *name_processed = BAD_CAST name;
if (php_dom_ns_is_html_and_document_is_html(nodep)) {
char *lowercase_copy = zend_str_tolower_dup_ex(name, name_len);
if (lowercase_copy != NULL) {
name_processed = BAD_CAST lowercase_copy;
}
}
/* Can't use xmlSetNsProp unconditionally here because that doesn't take into account the qualified name matching... */
xmlAttrPtr attr = php_dom_get_attribute_node(nodep, BAD_CAST name, name_len);
if (attr != NULL) {
dom_attr_value_will_change(intern, attr);
dom_remove_all_children((xmlNodePtr) attr);
xmlNodePtr node = xmlNewDocText(attr->doc, BAD_CAST value);
xmlAddChild((xmlNodePtr) attr, node);
} else {
attr = xmlSetNsProp(nodep, NULL, name_processed, BAD_CAST value);
if (EXPECTED(attr != NULL)) {
dom_check_register_attribute_id(attr, intern->document);
}
}
if (name_processed != BAD_CAST name) {
efree(name_processed);
}
} else {
xmlNodePtr attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attr != NULL) {
switch (attr->type) {
case XML_ATTRIBUTE_NODE:
dom_attr_value_will_change(intern, (xmlAttrPtr) attr);
node_list_unlink(attr->children);
break;
case XML_NAMESPACE_DECL:
RETURN_FALSE;
EMPTY_SWITCH_DEFAULT_CASE();
}
}
attr = dom_create_attribute(nodep, name, value);
if (!attr) {
zend_argument_value_error(1, "must be a valid XML attribute");
RETURN_THROWS();
}
if (attr->type == XML_NAMESPACE_DECL) {
RETURN_TRUE;
}
DOM_RET_OBJ(attr, intern);
}
}
/* }}} end dom_element_set_attribute */
typedef struct dom_deep_ns_redef_item {
xmlNodePtr current_node;
xmlNsPtr defined_ns;
} dom_deep_ns_redef_item;
/* Reconciliation for a *single* namespace, but reconciles *closest* to the subtree needing it. */
static void dom_deep_ns_redef(xmlNodePtr node, xmlNsPtr ns_to_redefine)
{
size_t worklist_capacity = 128;
dom_deep_ns_redef_item *worklist = emalloc(sizeof(dom_deep_ns_redef_item) * worklist_capacity);
worklist[0].current_node = node;
worklist[0].defined_ns = NULL;
size_t worklist_size = 1;
while (worklist_size > 0) {
worklist_size--;
dom_deep_ns_redef_item *current_worklist_item = &worklist[worklist_size];
ZEND_ASSERT(current_worklist_item->current_node->type == XML_ELEMENT_NODE);
xmlNsPtr defined_ns = current_worklist_item->defined_ns;
if (current_worklist_item->current_node->ns == ns_to_redefine) {
if (defined_ns == NULL) {
defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
}
current_worklist_item->current_node->ns = defined_ns;
}
for (xmlAttrPtr attr = current_worklist_item->current_node->properties; attr; attr = attr->next) {
if (attr->ns == ns_to_redefine) {
if (defined_ns == NULL) {
defined_ns = xmlNewNs(current_worklist_item->current_node, ns_to_redefine->href, ns_to_redefine->prefix);
}
attr->ns = defined_ns;
}
}
for (xmlNodePtr child = current_worklist_item->current_node->children; child; child = child->next) {
if (child->type != XML_ELEMENT_NODE) {
continue;
}
if (worklist_size == worklist_capacity) {
if (UNEXPECTED(worklist_capacity >= SIZE_MAX / 3 * 2 / sizeof(dom_deep_ns_redef_item))) {
/* Shouldn't be possible to hit, but checked for safety anyway */
goto out;
}
worklist_capacity = worklist_capacity * 3 / 2;
worklist = erealloc(worklist, sizeof(dom_deep_ns_redef_item) * worklist_capacity);
}
worklist[worklist_size].current_node = child;
worklist[worklist_size].defined_ns = defined_ns;
worklist_size++;
}
}
out:
efree(worklist);
}
static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp)
{
ZEND_ASSERT(thisp != NULL);
ZEND_ASSERT(attrp != NULL);
switch (attrp->type) {
case XML_ATTRIBUTE_NODE:
if (php_dom_object_get_data(attrp) == NULL) {
node_list_unlink(attrp->children);
xmlUnlinkNode(attrp);
xmlFreeProp((xmlAttrPtr)attrp);
} else {
xmlUnlinkNode(attrp);
}
break;
case XML_NAMESPACE_DECL: {
/* They will always be removed, but can be re-added.
*
* If any reference was left to the namespace, the only effect is that
* the definition is potentially moved closer to the element using it.
* If no reference was left, it is actually removed. */
xmlNsPtr ns = (xmlNsPtr) attrp;
if (thisp->nsDef == ns) {
thisp->nsDef = ns->next;
} else if (thisp->nsDef != NULL) {
xmlNsPtr prev = thisp->nsDef;
xmlNsPtr cur = prev->next;
while (cur) {
if (cur == ns) {
prev->next = cur->next;
break;
}
prev = cur;
cur = cur->next;
}
} else {
/* defensive: attrp not defined in thisp ??? */
#if ZEND_DEBUG
ZEND_UNREACHABLE();
#endif
break; /* defensive */
}
ns->next = NULL;
php_libxml_set_old_ns(thisp->doc, ns); /* note: can't deallocate as it might be referenced by a "fake namespace node" */
/* xmlReconciliateNs() redefines at the top of the tree instead of closest to the child, own reconciliation here.
* Similarly, the DOM version has other issues too (see dom_libxml_reconcile_ensure_namespaces_are_declared). */
dom_deep_ns_redef(thisp, ns);
break;
}
EMPTY_SWITCH_DEFAULT_CASE();
}
return true;
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6D6AC0F9
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattribute
Since:
*/
PHP_METHOD(DOMElement, removeAttribute)
{
xmlNodePtr nodep, attrp;
dom_object *intern;
size_t name_len;
char *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attrp == NULL) {
RETURN_FALSE;
}
RETURN_BOOL(dom_remove_attribute(nodep, attrp));
}
PHP_METHOD(Dom_Element, removeAttribute)
{
xmlNodePtr nodep, attrp;
dom_object *intern;
size_t name_len;
char *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attrp != NULL) {
dom_remove_attribute(nodep, attrp);
}
}
/* }}} end dom_element_remove_attribute */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-217A91B8
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenode
Since:
*/
PHP_METHOD(DOMElement, getAttributeNode)
{
zval *id;
xmlNodePtr nodep, attrp;
size_t name_len;
dom_object *intern;
char *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attrp == NULL) {
if (php_dom_follow_spec_intern(intern)) {
RETURN_NULL();
}
RETURN_FALSE;
}
if (attrp->type == XML_NAMESPACE_DECL) {
xmlNsPtr original = (xmlNsPtr) attrp;
/* Keep parent alive, because we're a fake child. */
GC_ADDREF(&intern->std);
(void) php_dom_create_fake_namespace_decl(nodep, original, return_value, intern);
} else {
DOM_RET_OBJ((xmlNodePtr) attrp, intern);
}
}
/* }}} end dom_element_get_attribute_node */
static void dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAMETERS, bool use_ns, bool modern)
{
zval *id, *node;
xmlNode *nodep;
xmlNs *nsp;
xmlAttr *attrp, *existattrp = NULL;
dom_object *intern, *attrobj, *oldobj;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, dom_get_attr_ce(modern)) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
/* ZPP Guarantees that a DOMAttr class is given, as it is converted to a xmlAttr
* to pass to libxml (see http://www.xmlsoft.org/html/libxml-tree.html#xmlAttr)
* if it is not of type XML_ATTRIBUTE_NODE it indicates a bug somewhere */
ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
if (modern) {
if (attrp->parent != NULL && attrp->parent != nodep) {
php_dom_throw_error(INUSE_ATTRIBUTE_ERR, /* strict */ true);
RETURN_THROWS();
}
if (attrp->doc != NULL && attrp->doc != nodep->doc) {
php_dom_adopt_node((xmlNodePtr) attrp, intern, nodep->doc);
}
} else {
if (!(attrp->doc == NULL || attrp->doc == nodep->doc)) {
php_dom_throw_error(WRONG_DOCUMENT_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
}
nsp = attrp->ns;
if (use_ns && nsp != NULL) {
existattrp = xmlHasNsProp(nodep, attrp->name, nsp->href);
} else {
existattrp = xmlHasProp(nodep, attrp->name);
}
if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
{
RETURN_NULL();
}
xmlUnlinkNode((xmlNodePtr) existattrp);
}
if (attrp->parent != NULL) {
xmlUnlinkNode((xmlNodePtr) attrp);
}
if (attrp->doc == NULL && nodep->doc != NULL) {
attrobj->document = intern->document;
php_libxml_increment_doc_ref((php_libxml_node_object *)attrobj, NULL);
}
xmlAddChild(nodep, (xmlNodePtr) attrp);
if (!modern) {
dom_mark_ids_modified(intern->document);
php_dom_reconcile_attribute_namespace_after_insertion(attrp);
} else {
dom_check_register_attribute_id(attrp, intern->document);
}
/* Returns old property if removed otherwise NULL */
if (existattrp != NULL) {
DOM_RET_OBJ((xmlNodePtr) existattrp, intern);
} else {
RETURN_NULL();
}
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-887236154
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenode
Since:
*/
PHP_METHOD(DOMElement, setAttributeNode)
{
dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ false, /* modern */ false);
}
/* }}} end dom_element_set_attribute_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-D589198
Since:
*/
static void dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *node_ce)
{
zval *node;
xmlNode *nodep;
xmlAttr *attrp;
dom_object *intern, *attrobj;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node, node_ce) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, ZEND_THIS, xmlNodePtr, intern);
DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
ZEND_ASSERT(attrp->type == XML_ATTRIBUTE_NODE);
if (attrp->parent != nodep) {
php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
RETURN_FALSE;
}
xmlUnlinkNode((xmlNodePtr) attrp);
DOM_RET_OBJ((xmlNodePtr) attrp, intern);
}
PHP_METHOD(DOMElement, removeAttributeNode)
{
dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
}
PHP_METHOD(Dom_Element, removeAttributeNode)
{
dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_attr_class_entry);
}
/* }}} end dom_element_remove_attribute_node */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1938918D
Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagname
Since:
*/
static void dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAMETERS, bool modern)
{
size_t name_len;
dom_object *intern, *namednode;
char *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
if (name_len > INT_MAX) {
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
if (modern) {
php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
} else {
php_dom_create_iterator(return_value, DOM_NODELIST, false);
}
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, NULL, 0);
}
PHP_METHOD(DOMElement, getElementsByTagName)
{
dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
}
PHP_METHOD(Dom_Element, getElementsByTagName)
{
dom_element_get_elements_by_tag_name(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
}
/* }}} end dom_element_get_elements_by_tag_name */
/* should_free_result must be initialized to false */
static const xmlChar *dom_get_attribute_ns(dom_object *intern, xmlNodePtr elemp, const char *uri, size_t uri_len, const char *name, bool *should_free_result)
{
bool follow_spec = php_dom_follow_spec_intern(intern);
if (follow_spec && uri_len == 0) {
uri = NULL;
}
xmlChar *strattr = xmlGetNsProp(elemp, BAD_CAST name, BAD_CAST uri);
if (strattr != NULL) {
*should_free_result = true;
return strattr;
} else {
if (!follow_spec && xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
xmlNsPtr nsptr = dom_get_nsdecl(elemp, BAD_CAST name);
if (nsptr != NULL) {
return nsptr->href;
} else {
return NULL;
}
} else {
return NULL;
}
}
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAttrNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, getAttributeNS)
{
zval *id;
xmlNodePtr elemp;
dom_object *intern;
size_t uri_len = 0, name_len = 0;
char *uri, *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
bool should_free_result = false;
const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result);
if (result == NULL) {
if (php_dom_follow_spec_intern(intern)) {
RETURN_NULL();
}
RETURN_EMPTY_STRING();
} else {
RETVAL_STRING((const char *) result);
if (should_free_result) {
xmlFree(BAD_CAST result);
}
}
}
/* }}} end dom_element_get_attribute_ns */
static void dom_set_attribute_ns_legacy(dom_object *intern, xmlNodePtr elemp, char *uri, size_t uri_len, char *name, size_t name_len, const char *value)
{
if (name_len == 0) {
zend_argument_must_not_be_empty_error(2);
return;
}
xmlNodePtr nodep = NULL;
xmlNsPtr nsptr;
xmlAttr *attr;
char *localname = NULL, *prefix = NULL;
int is_xmlns = 0, name_valid;
bool stricterror = dom_get_strict_error(intern->document);
int errorcode = dom_check_qname(name, &localname, &prefix, uri_len, name_len);
if (errorcode == 0) {
dom_mark_ids_modified(intern->document);
if (uri_len > 0) {
nodep = (xmlNodePtr) xmlHasNsProp(elemp, BAD_CAST localname, BAD_CAST uri);
if (nodep != NULL && nodep->type != XML_ATTRIBUTE_DECL) {
node_list_unlink(nodep->children);
}
if ((xmlStrEqual(BAD_CAST prefix, BAD_CAST "xmlns") ||
(prefix == NULL && xmlStrEqual(BAD_CAST localname, BAD_CAST "xmlns"))) &&
xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
is_xmlns = 1;
if (prefix == NULL) {
nsptr = dom_get_nsdecl(elemp, NULL);
} else {
nsptr = dom_get_nsdecl(elemp, BAD_CAST localname);
}
} else {
nsptr = xmlSearchNsByHref(elemp->doc, elemp, BAD_CAST uri);
if (nsptr && nsptr->prefix == NULL) {
xmlNsPtr tmpnsptr;
tmpnsptr = nsptr->next;
while (tmpnsptr) {
if ((tmpnsptr->prefix != NULL) && (tmpnsptr->href != NULL) &&
(xmlStrEqual(tmpnsptr->href, BAD_CAST uri))) {
nsptr = tmpnsptr;
break;
}
tmpnsptr = tmpnsptr->next;
}
if (tmpnsptr == NULL) {
nsptr = dom_get_ns_resolve_prefix_conflict(elemp, (const char *) nsptr->href);
}
}
}
if (nsptr == NULL) {
if (is_xmlns == 1) {
xmlNewNs(elemp, BAD_CAST value, prefix == NULL ? NULL : BAD_CAST localname);
} else {
nsptr = dom_get_ns(elemp, uri, &errorcode, prefix);
}
xmlReconciliateNs(elemp->doc, elemp);
} else {
if (is_xmlns == 1) {
if (nsptr->href) {
xmlFree(BAD_CAST nsptr->href);
}
nsptr->href = xmlStrdup(BAD_CAST value);
}
}
if (errorcode == 0 && is_xmlns == 0) {
xmlSetNsProp(elemp, nsptr, BAD_CAST localname, BAD_CAST value);
}
} else {
name_valid = xmlValidateName(BAD_CAST localname, 0);
if (name_valid != 0) {
errorcode = INVALID_CHARACTER_ERR;
stricterror = 1;
} else {
attr = xmlHasProp(elemp, BAD_CAST localname);
if (attr != NULL && attr->type != XML_ATTRIBUTE_DECL) {
node_list_unlink(attr->children);
}
xmlSetProp(elemp, BAD_CAST localname, BAD_CAST value);
}
}
}
xmlFree(localname);
if (prefix != NULL) {
xmlFree(prefix);
}
if (errorcode != 0) {
php_dom_throw_error(errorcode, stricterror);
}
}
/* https://dom.spec.whatwg.org/#dom-element-setattributens */
static void dom_set_attribute_ns_modern(dom_object *intern, xmlNodePtr elemp, zend_string *uri, const zend_string *name, const char *value)
{
xmlChar *localname = NULL, *prefix = NULL;
int errorcode = dom_validate_and_extract(uri, name, &localname, &prefix);
if (errorcode == 0) {
php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri);
xmlAttrPtr attr = xmlSetNsProp(elemp, ns, localname, BAD_CAST value);
if (UNEXPECTED(attr == NULL)) {
php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true);
} else {
dom_check_register_attribute_id(attr, intern->document);
}
} else {
php_dom_throw_error(errorcode, /* strict */ true);
}
xmlFree(localname);
xmlFree(prefix);
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAttrNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, setAttributeNS)
{
zval *id;
xmlNodePtr elemp;
size_t value_len = 0;
char *value;
zend_string *uri;
zend_string *name = NULL;
dom_object *intern;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!Ss", &uri, &name, &value, &value_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
if (php_dom_follow_spec_intern(intern)) {
dom_set_attribute_ns_modern(intern, elemp, uri, name, value);
} else {
dom_set_attribute_ns_legacy(intern, elemp, uri ? ZSTR_VAL(uri) : NULL, uri ? ZSTR_LEN(uri) : 0, ZSTR_VAL(name), ZSTR_LEN(name), value);
}
}
/* }}} end dom_element_set_attribute_ns */
static void dom_remove_eliminated_ns_single_element(xmlNodePtr node, xmlNsPtr eliminatedNs)
{
ZEND_ASSERT(node->type == XML_ELEMENT_NODE);
if (node->ns == eliminatedNs) {
node->ns = NULL;
}
for (xmlAttrPtr attr = node->properties; attr != NULL; attr = attr->next) {
if (attr->ns == eliminatedNs) {
attr->ns = NULL;
}
}
}
static void dom_remove_eliminated_ns(xmlNodePtr node, xmlNsPtr eliminatedNs)
{
dom_remove_eliminated_ns_single_element(node, eliminatedNs);
xmlNodePtr base = node;
node = node->children;
while (node != NULL) {
ZEND_ASSERT(node != base);
if (node->type == XML_ELEMENT_NODE) {
dom_remove_eliminated_ns_single_element(node, eliminatedNs);
}
node = php_dom_next_in_tree_order(node, base);
}
}
static void dom_eliminate_ns(xmlNodePtr nodep, xmlNsPtr nsptr)
{
if (nsptr->href != NULL) {
xmlFree((char *) nsptr->href);
nsptr->href = NULL;
}
if (nsptr->prefix != NULL) {
xmlFree((char *) nsptr->prefix);
nsptr->prefix = NULL;
}
/* Remove it from the list and move it to the old ns list */
xmlNsPtr current_ns = nodep->nsDef;
if (current_ns == nsptr) {
nodep->nsDef = nsptr->next;
} else {
do {
if (current_ns->next == nsptr) {
current_ns->next = nsptr->next;
break;
}
current_ns = current_ns->next;
} while (current_ns != NULL);
}
nsptr->next = NULL;
php_libxml_set_old_ns(nodep->doc, nsptr);
dom_remove_eliminated_ns(nodep, nsptr);
}
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElRemAtNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-removeattributens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, removeAttributeNS)
{
zval *id;
xmlNode *nodep;
xmlAttr *attrp;
xmlNsPtr nsptr;
dom_object *intern;
size_t name_len, uri_len;
char *name, *uri;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
bool follow_spec = php_dom_follow_spec_intern(intern);
if (follow_spec && uri_len == 0) {
uri = NULL;
}
attrp = xmlHasNsProp(nodep, BAD_CAST name, BAD_CAST uri);
if (!follow_spec) {
nsptr = dom_get_nsdecl(nodep, BAD_CAST name);
if (nsptr != NULL) {
if (xmlStrEqual(BAD_CAST uri, nsptr->href)) {
dom_eliminate_ns(nodep, nsptr);
} else {
return;
}
}
}
if (attrp && attrp->type != XML_ATTRIBUTE_DECL) {
if (php_dom_object_get_data((xmlNodePtr) attrp) == NULL) {
node_list_unlink(attrp->children);
xmlUnlinkNode((xmlNodePtr) attrp);
xmlFreeProp(attrp);
} else {
xmlUnlinkNode((xmlNodePtr) attrp);
}
}
}
/* }}} end dom_element_remove_attribute_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElGetAtNodeNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-getattributenodens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, getAttributeNodeNS)
{
zval *id;
xmlNodePtr elemp;
xmlAttrPtr attrp;
dom_object *intern;
size_t uri_len, name_len;
char *uri, *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
bool follow_spec = php_dom_follow_spec_intern(intern);
if (follow_spec && uri_len == 0) {
uri = NULL;
}
attrp = xmlHasNsProp(elemp, BAD_CAST name, BAD_CAST uri);
if (attrp == NULL) {
if (!follow_spec && xmlStrEqual(BAD_CAST uri, BAD_CAST DOM_XMLNS_NS_URI)) {
xmlNsPtr nsptr;
nsptr = dom_get_nsdecl(elemp, BAD_CAST name);
if (nsptr != NULL) {
/* Keep parent alive, because we're a fake child. */
GC_ADDREF(&intern->std);
(void) php_dom_create_fake_namespace_decl(elemp, nsptr, return_value, intern);
} else {
RETURN_NULL();
}
} else {
RETURN_NULL();
}
} else {
DOM_RET_OBJ((xmlNodePtr) attrp, intern);
}
}
/* }}} end dom_element_get_attribute_node_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetAtNodeNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-setattributenodens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, setAttributeNodeNS)
{
dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ false);
}
PHP_METHOD(Dom_Element, setAttributeNodeNS)
{
dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAM_PASSTHRU, /* use_ns */ true, /* modern */ true);
}
/* }}} end dom_element_set_attribute_node_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-A6C90942
Modern spec URL: https://dom.spec.whatwg.org/#concept-getelementsbytagnamens
Since: DOM Level 2
*/
static void dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAMETERS, bool modern)
{
size_t uri_len, name_len;
dom_object *intern, *namednode;
char *uri, *name;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p!p", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
if (uri_len > INT_MAX) {
zend_argument_value_error(1, "is too long");
RETURN_THROWS();
}
if (name_len > INT_MAX) {
zend_argument_value_error(2, "is too long");
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
if (modern) {
php_dom_create_iterator(return_value, DOM_HTMLCOLLECTION, true);
} else {
php_dom_create_iterator(return_value, DOM_NODELIST, false);
}
namednode = Z_DOMOBJ_P(return_value);
dom_namednode_iter(intern, 0, namednode, NULL, name, name_len, uri ? uri : "", uri_len);
}
PHP_METHOD(DOMElement, getElementsByTagNameNS)
{
dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
}
PHP_METHOD(Dom_Element, getElementsByTagNameNS)
{
dom_element_get_elements_by_tag_name_ns(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
}
/* }}} end dom_element_get_elements_by_tag_name_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttr
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattribute
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, hasAttribute)
{
zval *id;
xmlNode *nodep;
dom_object *intern;
char *name;
size_t name_len;
xmlNodePtr attr;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
attr = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len);
if (attr == NULL) {
RETURN_FALSE;
} else {
RETURN_TRUE;
}
}
/* }}} end dom_element_has_attribute */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElHasAttrNS
Modern spec URL: https://dom.spec.whatwg.org/#dom-element-hasattributens
Since: DOM Level 2
*/
PHP_METHOD(DOMElement, hasAttributeNS)
{
zval *id;
xmlNodePtr elemp;
dom_object *intern;
size_t uri_len, name_len;
char *uri, *name;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!s", &uri, &uri_len, &name, &name_len) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
bool should_free_result = false;
const xmlChar *result = dom_get_attribute_ns(intern, elemp, uri, uri_len, name, &should_free_result);
if (result == NULL) {
RETURN_FALSE;
} else {
if (should_free_result) {
xmlFree(BAD_CAST result);
}
RETURN_TRUE;
}
}
/* }}} end dom_element_has_attribute_ns */
static void php_set_attribute_id(xmlAttrPtr attrp, bool is_id, php_libxml_ref_obj *document) /* {{{ */
{
if (is_id && attrp->atype != XML_ATTRIBUTE_ID) {
attrp->atype = XML_ATTRIBUTE_ID;
} else if (!is_id && attrp->atype == XML_ATTRIBUTE_ID) {
xmlRemoveID(attrp->doc, attrp);
attrp->atype = 0;
}
dom_mark_ids_modified(document);
}
/* }}} */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttr
Since: DOM Level 3
*/
PHP_METHOD(DOMElement, setIdAttribute)
{
zval *id;
xmlNode *nodep;
xmlAttrPtr attrp;
dom_object *intern;
char *name;
size_t name_len;
bool is_id;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "sb", &name, &name_len, &is_id) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
attrp = xmlHasNsProp(nodep, BAD_CAST name, NULL);
if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
} else {
php_set_attribute_id(attrp, is_id, intern->document);
}
}
/* }}} end dom_element_set_id_attribute */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNS
Since: DOM Level 3
*/
PHP_METHOD(DOMElement, setIdAttributeNS)
{
zval *id;
xmlNodePtr elemp;
xmlAttrPtr attrp;
dom_object *intern;
size_t uri_len, name_len;
char *uri, *name;
bool is_id;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssb", &uri, &uri_len, &name, &name_len, &is_id) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_OBJ(elemp, id, xmlNodePtr, intern);
attrp = xmlHasNsProp(elemp, BAD_CAST name, BAD_CAST uri);
if (attrp == NULL || attrp->type == XML_ATTRIBUTE_DECL) {
php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
} else {
php_set_attribute_id(attrp, is_id, intern->document);
}
}
/* }}} end dom_element_set_id_attribute_ns */
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-ElSetIdAttrNode
Since: DOM Level 3
*/
static void dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend_class_entry *attr_ce)
{
zval *id, *node;
xmlNode *nodep;
xmlAttrPtr attrp;
dom_object *intern, *attrobj;
bool is_id;
id = ZEND_THIS;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ob", &node, attr_ce, &is_id) != SUCCESS) {
RETURN_THROWS();
}
DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
DOM_GET_OBJ(attrp, node, xmlAttrPtr, attrobj);
if (attrp->parent != nodep) {
php_dom_throw_error(NOT_FOUND_ERR, dom_get_strict_error(intern->document));
} else {
php_set_attribute_id(attrp, is_id, intern->document);
}
}
PHP_METHOD(DOMElement, setIdAttributeNode)
{
dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_attr_class_entry);
}
PHP_METHOD(Dom_Element, setIdAttributeNode)
{
dom_element_set_id_attribute_node(INTERNAL_FUNCTION_PARAM_PASSTHRU, dom_modern_attr_class_entry);
}
/* }}} end dom_element_set_id_attribute_node */
/* {{{ URL:
Since:
*/
PHP_METHOD(DOMElement, remove)
{
dom_object *intern;
if (zend_parse_parameters_none() == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_INTERN(intern);
dom_child_node_remove(intern);
}
/* }}} end DOMElement::remove */
PHP_METHOD(DOMElement, after)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_parent_node_after(intern, args, argc);
}
PHP_METHOD(DOMElement, before)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_parent_node_before(intern, args, argc);
}
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-append
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(DOMElement, append)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_parent_node_append(intern, args, argc);
}
/* }}} end DOMElement::append */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-prepend
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(DOMElement, prepend)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_parent_node_prepend(intern, args, argc);
}
/* }}} end DOMElement::prepend */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
Since: DOM Living Standard (DOM4)
*/
PHP_METHOD(DOMElement, replaceWith)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_child_replace_with(intern, args, argc);
}
/* }}} end DOMElement::prepend */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
Since:
*/
PHP_METHOD(DOMElement, replaceChildren)
{
uint32_t argc = 0;
zval *args;
dom_object *intern;
ZEND_PARSE_PARAMETERS_START(0, -1)
Z_PARAM_VARIADIC('*', args, argc)
ZEND_PARSE_PARAMETERS_END();
DOM_GET_THIS_INTERN(intern);
dom_parent_node_replace_children(intern, args, argc);
}
/* }}} */
#define INSERT_ADJACENT_RES_ADOPT_FAILED ((void*) -1)
#define INSERT_ADJACENT_RES_SYNTAX_FAILED INSERT_ADJACENT_RES_ADOPT_FAILED
#define INSERT_ADJACENT_RES_PRE_INSERT_FAILED ((void*) -2)
static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp)
{
if (zend_string_equals_literal_ci(where, "beforebegin")) {
if (thisp->parent == NULL) {
return NULL;
}
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
return INSERT_ADJACENT_RES_ADOPT_FAILED;
}
if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp)) {
return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
}
} else if (zend_string_equals_literal_ci(where, "afterbegin")) {
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
return INSERT_ADJACENT_RES_ADOPT_FAILED;
}
if (!php_dom_pre_insert(this_intern->document, otherp, thisp, thisp->children)) {
return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
}
} else if (zend_string_equals_literal_ci(where, "beforeend")) {
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
return INSERT_ADJACENT_RES_ADOPT_FAILED;
}
if (!php_dom_pre_insert(this_intern->document, otherp, thisp, NULL)) {
return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
}
} else if (zend_string_equals_literal_ci(where, "afterend")) {
if (thisp->parent == NULL) {
return NULL;
}
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
return INSERT_ADJACENT_RES_ADOPT_FAILED;
}
if (!php_dom_pre_insert(this_intern->document, otherp, thisp->parent, thisp->next)) {
return INSERT_ADJACENT_RES_PRE_INSERT_FAILED;
}
} else {
php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document));
return INSERT_ADJACENT_RES_SYNTAX_FAILED;
}
return otherp;
}
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
Since:
*/
static void dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAMETERS, const zend_string *where, zval *element_zval)
{
zval *id;
xmlNodePtr thisp, otherp;
dom_object *this_intern, *other_intern;
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
DOM_GET_OBJ(otherp, element_zval, xmlNodePtr, other_intern);
xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
if (result == NULL) {
RETURN_NULL();
} else if (result != INSERT_ADJACENT_RES_ADOPT_FAILED && result != INSERT_ADJACENT_RES_PRE_INSERT_FAILED) {
DOM_RET_OBJ(otherp, other_intern);
} else {
RETURN_THROWS();
}
}
PHP_METHOD(DOMElement, insertAdjacentElement)
{
zend_string *where;
zval *element_zval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) != SUCCESS) {
RETURN_THROWS();
}
dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, element_zval);
}
PHP_METHOD(Dom_Element, insertAdjacentElement)
{
zval *element_zval, *where_zv;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
Z_PARAM_OBJECT_OF_CLASS(element_zval, dom_modern_element_class_entry)
ZEND_PARSE_PARAMETERS_END();
const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
dom_element_insert_adjacent_element(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, element_zval);
}
/* }}} end DOMElement::insertAdjacentElement */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
Since:
*/
static void dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAMETERS, const zend_string *where, const zend_string *data)
{
dom_object *this_intern;
zval *id;
xmlNodePtr thisp;
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(data)))) {
zend_argument_value_error(2, "is too long");
RETURN_THROWS();
}
xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data));
xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
if (result == NULL || result == INSERT_ADJACENT_RES_ADOPT_FAILED) {
xmlFreeNode(otherp);
}
}
PHP_METHOD(DOMElement, insertAdjacentText)
{
zend_string *where, *data;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &where, &data) == FAILURE) {
RETURN_THROWS();
}
dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, data);
}
PHP_METHOD(Dom_Element, insertAdjacentText)
{
zval *where_zv;
zend_string *data;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
Z_PARAM_STR(data)
ZEND_PARSE_PARAMETERS_END();
const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
dom_element_insert_adjacent_text(INTERNAL_FUNCTION_PARAM_PASSTHRU, where, data);
}
/* }}} end DOMElement::insertAdjacentText */
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
Since:
*/
PHP_METHOD(DOMElement, toggleAttribute)
{
char *qname, *qname_tmp = NULL;
size_t qname_length;
bool force, force_is_null = true;
xmlNodePtr thisp;
zval *id;
dom_object *intern;
bool retval;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|b!", &qname, &qname_length, &force, &force_is_null) == FAILURE) {
RETURN_THROWS();
}
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
/* Step 1 */
if (xmlValidateName(BAD_CAST qname, 0) != 0) {
php_dom_throw_error(INVALID_CHARACTER_ERR, true);
RETURN_THROWS();
}
bool follow_spec = php_dom_follow_spec_intern(intern);
/* Step 2 */
if (thisp->doc != NULL && thisp->doc->type == XML_HTML_DOCUMENT_NODE
&& ((!follow_spec && thisp->ns == NULL) || (thisp->ns != NULL && xmlStrEqual(thisp->ns->href, BAD_CAST DOM_XHTML_NS_URI)))) {
qname_tmp = zend_str_tolower_dup_ex(qname, qname_length);
if (qname_tmp != NULL) {
qname = qname_tmp;
}
}
/* Step 3 */
xmlNodePtr attribute = dom_get_attribute_or_nsdecl(intern, thisp, BAD_CAST qname, qname_length);
/* Step 4 */
if (attribute == NULL) {
/* Step 4.1 */
if (force_is_null || force) {
if (follow_spec) {
xmlSetNsProp(thisp, NULL, BAD_CAST qname, NULL);
} else {
/* The behaviour for namespaces isn't defined by spec, but this is based on observing browers behaviour.
* It follows the same rules when you'd manually add an attribute using the other APIs. */
int len;
const xmlChar *split = xmlSplitQName3((const xmlChar *) qname, &len);
if (split == NULL || strncmp(qname, "xmlns:", len + 1 /* +1 for matching ':' too */) != 0) {
/* unqualified name, or qualified name with no xml namespace declaration */
dom_create_attribute(thisp, qname, "");
} else {
/* qualified name with xml namespace declaration */
xmlNewNs(thisp, (const xmlChar *) "", (const xmlChar *) (qname + len + 1));
}
}
retval = true;
goto out;
}
/* Step 4.2 */
retval = false;
goto out;
}
/* Step 5 */
if (force_is_null || !force) {
dom_remove_attribute(thisp, attribute);
retval = false;
goto out;
}
/* Step 6 */
retval = true;
out:
if (qname_tmp) {
efree(qname_tmp);
}
RETURN_BOOL(retval);
}
/* }}} end DOMElement::prepend */
static void php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAMETERS, bool all)
{
zend_string *selectors_str;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(selectors_str)
ZEND_PARSE_PARAMETERS_END();
xmlNodePtr thisp;
dom_object *intern;
zval *id;
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
if (all) {
dom_parent_node_query_selector_all(thisp, intern, return_value, selectors_str);
} else {
dom_parent_node_query_selector(thisp, intern, return_value, selectors_str);
}
}
PHP_METHOD(Dom_Element, querySelector)
{
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
}
PHP_METHOD(Dom_Element, querySelectorAll)
{
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
}
PHP_METHOD(Dom_Element, matches)
{
zend_string *selectors_str;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(selectors_str)
ZEND_PARSE_PARAMETERS_END();
xmlNodePtr thisp;
dom_object *intern;
zval *id;
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
dom_element_matches(thisp, intern, return_value, selectors_str);
}
PHP_METHOD(Dom_Element, closest)
{
zend_string *selectors_str;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(selectors_str)
ZEND_PARSE_PARAMETERS_END();
xmlNodePtr thisp;
dom_object *intern;
zval *id;
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
dom_element_closest(thisp, intern, return_value, selectors_str);
}
zend_result dom_modern_element_substituted_node_value_read(dom_object *obj, zval *retval)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
xmlChar *content = xmlNodeGetContent(nodep);
if (UNEXPECTED(content == NULL)) {
php_dom_throw_error(INVALID_STATE_ERR, true);
return FAILURE;
} else {
ZVAL_STRING(retval, (const char *) content);
xmlFree(content);
}
return SUCCESS;
}
zend_result dom_modern_element_substituted_node_value_write(dom_object *obj, zval *newval)
{
DOM_PROP_NODE(xmlNodePtr, nodep, obj);
php_libxml_invalidate_node_list_cache(obj->document);
dom_remove_all_children(nodep);
xmlNodeSetContentLen(nodep, (xmlChar *) Z_STRVAL_P(newval), Z_STRLEN_P(newval));
return SUCCESS;
}
static void dom_element_get_in_scope_namespace_info(php_dom_libxml_ns_mapper *ns_mapper, HashTable *result, xmlNodePtr nodep, dom_object *intern)
{
HashTable prefix_to_ns_table;
zend_hash_init(&prefix_to_ns_table, 0, NULL, NULL, false);
zend_hash_real_init_mixed(&prefix_to_ns_table);
/* https://www.w3.org/TR/1999/REC-xpath-19991116/#namespace-nodes */
for (const xmlNode *cur = nodep; cur != NULL; cur = cur->parent) {
if (cur->type == XML_ELEMENT_NODE) {
/* Find the last attribute */
const xmlAttr *last = NULL;
for (const xmlAttr *attr = cur->properties; attr != NULL; attr = attr->next) {
last = attr;
}
/* Reversed loop because the parent traversal is reversed as well,
* this will keep the ordering consistent. */
for (const xmlAttr *attr = last; attr != NULL; attr = attr->prev) {
if (attr->ns != NULL && php_dom_ns_is_fast_ex(attr->ns, php_dom_ns_is_xmlns_magic_token)
&& attr->children != NULL && attr->children->content != NULL) {
const char *prefix = attr->ns->prefix == NULL ? NULL : (const char *) attr->name;
const char *key = prefix == NULL ? "" : prefix;
xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_strings_nullsafe(ns_mapper, prefix, (const char *) attr->children->content);
/* NULL is a valid value for the sentinel */
zval zv;
ZVAL_PTR(&zv, ns);
zend_hash_str_add(&prefix_to_ns_table, key, strlen(key), &zv);
}
}
}
}
xmlNsPtr ns;
zend_string *prefix;
ZEND_HASH_MAP_REVERSE_FOREACH_STR_KEY_PTR(&prefix_to_ns_table, prefix, ns) {
if (ZSTR_LEN(prefix) == 0 && (ns == NULL || ns->href == NULL || *ns->href == '\0')) {
/* Exception: "the value of the xmlns attribute for the nearest such element is non-empty" */
continue;
}
zval zv;
object_init_ex(&zv, dom_namespace_info_class_entry);
zend_object *obj = Z_OBJ(zv);
if (ZSTR_LEN(prefix) != 0) {
ZVAL_STR_COPY(OBJ_PROP_NUM(obj, 0), prefix);
} else {
ZVAL_NULL(OBJ_PROP_NUM(obj, 0));
}
if (ns != NULL && ns->href != NULL && *ns->href != '\0') {
ZVAL_STRING(OBJ_PROP_NUM(obj, 1), (const char *) ns->href);
} else {
ZVAL_NULL(OBJ_PROP_NUM(obj, 1));
}
php_dom_create_object(nodep, OBJ_PROP_NUM(obj, 2), intern);
zend_hash_next_index_insert_new(result, &zv);
} ZEND_HASH_FOREACH_END();
zend_hash_destroy(&prefix_to_ns_table);
}
PHP_METHOD(Dom_Element, getInScopeNamespaces)
{
zval *id;
xmlNode *nodep;
dom_object *intern;
ZEND_PARSE_PARAMETERS_NONE();
DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
array_init(return_value);
HashTable *result = Z_ARRVAL_P(return_value);
dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
}
PHP_METHOD(Dom_Element, getDescendantNamespaces)
{
zval *id;
xmlNode *nodep;
dom_object *intern;
ZEND_PARSE_PARAMETERS_NONE();
DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
array_init(return_value);
HashTable *result = Z_ARRVAL_P(return_value);
dom_element_get_in_scope_namespace_info(ns_mapper, result, nodep, intern);
xmlNodePtr cur = nodep->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
/* TODO: this could be more optimized by updating the same HashTable repeatedly
* instead of recreating it on every node. */
dom_element_get_in_scope_namespace_info(ns_mapper, result, cur, intern);
}
cur = php_dom_next_in_tree_order(cur, nodep);
}
}
PHP_METHOD(Dom_Element, rename)
{
zend_string *namespace_uri, *qualified_name;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_STR_OR_NULL(namespace_uri)
Z_PARAM_STR(qualified_name)
ZEND_PARSE_PARAMETERS_END();
zval *id;
dom_object *intern;
xmlNodePtr nodep;
DOM_GET_THIS_OBJ(nodep, id, xmlNodePtr, intern);
xmlChar *localname = NULL, *prefix = NULL;
int errorcode = dom_validate_and_extract(namespace_uri, qualified_name, &localname, &prefix);
if (UNEXPECTED(errorcode != 0)) {
php_dom_throw_error(errorcode, /* strict */ true);
goto cleanup;
}
if (nodep->type == XML_ATTRIBUTE_NODE) {
/* Check for duplicate attributes. */
xmlAttrPtr existing = xmlHasNsProp(nodep->parent, localname, namespace_uri && ZSTR_VAL(namespace_uri)[0] != '\0' ? BAD_CAST ZSTR_VAL(namespace_uri) : NULL);
if (existing != NULL && existing != (xmlAttrPtr) nodep) {
php_dom_throw_error_with_message(INVALID_MODIFICATION_ERR, "An attribute with the given name in the given namespace already exists", /* strict */ true);
goto cleanup;
}
} else {
ZEND_ASSERT(nodep->type == XML_ELEMENT_NODE);
/* Check for moving to or away from the HTML namespace. */
bool is_currently_html_ns = php_dom_ns_is_fast(nodep, php_dom_ns_is_html_magic_token);
bool will_be_html_ns = namespace_uri != NULL && zend_string_equals_literal(namespace_uri, DOM_XHTML_NS_URI);
if (is_currently_html_ns != will_be_html_ns) {
if (is_currently_html_ns) {
php_dom_throw_error_with_message(
INVALID_MODIFICATION_ERR,
"It is not possible to move an element out of the HTML namespace because the HTML namespace is tied to the HTMLElement class",
/* strict */ true
);
} else {
php_dom_throw_error_with_message(
INVALID_MODIFICATION_ERR,
"It is not possible to move an element into the HTML namespace because the HTML namespace is tied to the HTMLElement class",
/* strict */ true
);
}
goto cleanup;
}
/* If we currently have a template but the new element type won't be a template, then throw away the templated content. */
if (is_currently_html_ns && xmlStrEqual(nodep->name, BAD_CAST "template") && !xmlStrEqual(localname, BAD_CAST "template")) {
php_dom_throw_error_with_message(
INVALID_MODIFICATION_ERR,
"It is not possible to rename the template element because it hosts a document fragment",
/* strict */ true
);
goto cleanup;
}
}
php_libxml_invalidate_node_list_cache(intern->document);
php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern);
/* Update namespace uri + prefix by querying the namespace mapper */
/* prefix can be NULL here, but that is taken care of by the called APIs. */
nodep->ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), namespace_uri);
/* Change the local name */
if (xmlDictOwns(nodep->doc->dict, nodep->name) != 1) {
xmlFree((xmlChar *) nodep->name);
}
const xmlChar *copy = xmlDictLookup(nodep->doc->dict, localname, -1);
if (copy != NULL) {
nodep->name = copy;
} else {
nodep->name = localname;
localname = NULL;
}
cleanup:
xmlFree(localname);
xmlFree(prefix);
}
#endif