Commit cf9bb87ec24 for php.net

commit cf9bb87ec24e928012c97231f836150fafd4338a
Merge: a80fffa00d3 1666d90b157
Author: ndossche <7771979+ndossche@users.noreply.github.com>
Date:   Thu Jun 25 08:31:03 2026 +0200

    Merge branch 'PHP-8.4' into PHP-8.5

    * PHP-8.4:
      [ci skip] Update heading
      soap: do not overwrite the parsed host on a protocol-relative redirect

diff --cc ext/dom/obj_map.c
index 84fae3bad57,00000000000..6e65143648d
mode 100644,000000..100644
--- a/ext/dom/obj_map.c
+++ b/ext/dom/obj_map.c
@@@ -1,640 -1,0 +1,640 @@@
 +/*
 +   +----------------------------------------------------------------------+
 +   | 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>                            |
-    |          Niels Dossche <nielsdos@php.net>                            |
++   |          Nora Dossche  <ndossche@php.net>                            |
 +   +----------------------------------------------------------------------+
 +*/
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "php.h"
 +#if defined(HAVE_LIBXML) && defined(HAVE_DOM)
 +#include "php_dom.h"
 +#include "obj_map.h"
 +#include "token_list.h"
 +
 +static zend_always_inline void objmap_cache_release_cached_obj(dom_nnodemap_object *objmap)
 +{
 +	if (objmap->cached_obj) {
 +		OBJ_RELEASE(&objmap->cached_obj->std);
 +		objmap->cached_obj = NULL;
 +		objmap->cached_obj_index = 0;
 +	}
 +}
 +
 +static zend_always_inline void reset_objmap_cache(dom_nnodemap_object *objmap)
 +{
 +	objmap_cache_release_cached_obj(objmap);
 +	objmap->cached_length = -1;
 +}
 +
 +static bool dom_matches_class_name(const dom_nnodemap_object *map, const xmlNode *nodep)
 +{
 +	bool ret = false;
 +
 +	if (nodep->type == XML_ELEMENT_NODE) {
 +		xmlAttrPtr classes = xmlHasNsProp(nodep, BAD_CAST "class", NULL);
 +		if (classes != NULL) {
 +			bool should_free;
 +			xmlChar *value = php_libxml_attr_value(classes, &should_free);
 +
 +			bool quirks = map->baseobj->document->quirks_mode == PHP_LIBXML_QUIRKS;
 +			if (dom_ordered_set_all_contained(map->array, (const char *) value, quirks)) {
 +				ret = true;
 +			}
 +
 +			if (should_free) {
 +				xmlFree(value);
 +			}
 +		}
 +	}
 +
 +	return ret;
 +}
 +
 +/**************************
 + * === Length methods === *
 + **************************/
 +
 +static zend_long dom_map_get_xmlht_length(dom_nnodemap_object *map)
 +{
 +	/* Note: if there are, for example, no entities or notations then the hash table can be NULL. */
 +	return map->ht ? xmlHashSize(map->ht) : 0;
 +}
 +
 +static zend_long dom_map_get_nodeset_length(dom_nnodemap_object *map)
 +{
 +	return zend_hash_num_elements(map->array);
 +}
 +
 +static zend_long dom_map_get_prop_length(dom_nnodemap_object *map)
 +{
 +	zend_long count = 0;
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	if (nodep) {
 +		for (xmlAttrPtr curnode = nodep->properties; curnode; curnode = curnode->next) {
 +			count++;
 +		}
 +	}
 +	return count;
 +}
 +
 +static zend_long dom_map_get_nodes_length(dom_nnodemap_object *map)
 +{
 +	zend_long count = 0;
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	if (nodep) {
 +		for (xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); curnode; curnode = curnode->next) {
 +			count++;
 +		}
 +	}
 +	return count;
 +}
 +
 +static zend_long dom_map_get_elements_length(dom_nnodemap_object *map)
 +{
 +	zend_long count = 0;
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	if (nodep) {
 +		for (xmlNodePtr curnode = dom_nodelist_iter_start_first_child(nodep); curnode; curnode = curnode->next) {
 +			if (curnode->type == XML_ELEMENT_NODE) {
 +				count++;
 +			}
 +		}
 +	}
 +	return count;
 +}
 +
 +static zend_long dom_map_get_by_tag_name_length(dom_nnodemap_object *map)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	zend_long count = 0;
 +	if (nodep) {
 +		xmlNodePtr basep = nodep;
 +		nodep = php_dom_first_child_of_container_node(basep);
 +		dom_get_elements_by_tag_name_ns_raw(
 +			basep, nodep, map->ns, map->local, map->local_lower, &count, ZEND_LONG_MAX - 1 /* because of <= */);
 +	}
 +	return count;
 +}
 +
 +static zend_long dom_map_get_by_class_name_length(dom_nnodemap_object *map)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	zend_long count = 0;
 +	if (nodep) {
 +		xmlNodePtr basep = nodep;
 +		nodep = php_dom_first_child_of_container_node(basep);
 +
 +		while (nodep != NULL) {
 +			if (dom_matches_class_name(map, nodep)) {
 +				count++;
 +			}
 +			nodep = php_dom_next_in_tree_order(nodep, basep);
 +		}
 +	}
 +	return count;
 +}
 +
 +static zend_long dom_map_get_zero_length(dom_nnodemap_object *map)
 +{
 +	return 0;
 +}
 +
 +/************************
 + * === Item lookups === *
 + ************************/
 +
 +static void dom_ret_node_to_zobj(dom_nnodemap_object *map, xmlNodePtr node, zval *return_value)
 +{
 +	if (node) {
 +		DOM_RET_OBJ(node, map->baseobj);
 +	} else {
 +		RETURN_NULL();
 +	}
 +}
 +
 +static void dom_map_get_entity_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr node = map->ht ? php_dom_libxml_hash_iter(map->ht, index) : NULL;
 +	dom_ret_node_to_zobj(map, node, return_value);
 +}
 +
 +static void dom_map_get_notation_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr node = map->ht ? php_dom_libxml_hash_iter(map->ht, index) : NULL;
 +	if (node) {
 +		xmlNotation *notation = (xmlNotation *) node;
 +		node = create_notation(notation->name, notation->PublicID, notation->SystemID);
 +	}
 +	dom_ret_node_to_zobj(map, node, return_value);
 +}
 +
 +static void dom_map_get_nodeset_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	zval *entry = zend_hash_index_find(map->array, index);
 +	if (entry) {
 +		RETURN_COPY(entry);
 +	} else {
 +		RETURN_NULL();
 +	}
 +}
 +
 +typedef struct dom_node_idx_pair {
 +	xmlNodePtr node;
 +	zend_long index;
 +} dom_node_idx_pair;
 +
 +static dom_node_idx_pair dom_obj_map_get_start_point(dom_nnodemap_object *map, xmlNodePtr basep, zend_long index)
 +{
 +	dom_node_idx_pair ret;
 +	ZEND_ASSERT(basep != NULL);
 +	/* For now we're only able to use cache for forward search.
 +	 * TODO: in the future we could extend the logic of the node list such that backwards searches
 +	 *       are also possible. */
 +	bool restart = true;
 +	zend_long relative_index = index;
 +	if (index >= map->cached_obj_index && map->cached_obj && !php_dom_is_cache_tag_stale_from_node(&map->cache_tag, basep)) {
 +		xmlNodePtr cached_obj_xml_node = dom_object_get_node(map->cached_obj);
 +
 +		/* The node cannot be NULL if the cache is valid. If it is NULL, then it means we
 +		 * forgot an invalidation somewhere. Take the defensive programming approach and invalidate
 +		 * it here if it's NULL (except in debug mode where we would want to catch this). */
 +		if (UNEXPECTED(cached_obj_xml_node == NULL)) {
 +#if ZEND_DEBUG
 +			ZEND_UNREACHABLE();
 +#endif
 +			reset_objmap_cache(map);
 +		} else {
 +			restart = false;
 +			relative_index -= map->cached_obj_index;
 +			basep = cached_obj_xml_node;
 +		}
 +	}
 +	ret.node = restart ? NULL : basep;
 +	ret.index = relative_index;
 +	return ret;
 +}
 +
 +static void dom_map_cache_obj(dom_nnodemap_object *map, xmlNodePtr itemnode, zend_long index, zval *return_value)
 +{
 +	/* Hold additional reference for the cache, must happen before releasing the cache
 +	 * because we might be the last reference holder.
 +	 * Instead of storing and copying zvals, we store the object pointer directly.
 +	 * This saves us some bytes because a pointer is smaller than a zval.
 +	 * This also means we have to manually refcount the objects here, and remove the reference count
 +	 * in reset_objmap_cache() and the destructor. */
 +	dom_object *cached_obj = Z_DOMOBJ_P(return_value);
 +	GC_ADDREF(&cached_obj->std);
 +	/* If the tag is stale, all cached data is useless. Otherwise only the cached object is useless. */
 +	if (php_dom_is_cache_tag_stale_from_node(&map->cache_tag, itemnode)) {
 +		php_dom_mark_cache_tag_up_to_date_from_node(&map->cache_tag, itemnode);
 +		reset_objmap_cache(map);
 +	} else {
 +		objmap_cache_release_cached_obj(map);
 +	}
 +	map->cached_obj_index = index;
 +	map->cached_obj = cached_obj;
 +}
 +
 +static void dom_map_get_attributes_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	xmlNodePtr itemnode = NULL;
 +	if (nodep && index >= 0) {
 +		ZEND_ASSERT(nodep->type == XML_ELEMENT_NODE);
 +		itemnode = (xmlNodePtr) nodep->properties;
 +		for (; index > 0 && itemnode; itemnode = itemnode->next, index--);
 +	}
 +	dom_ret_node_to_zobj(map, itemnode, return_value);
 +}
 +
 +static void dom_map_get_nodes_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	xmlNodePtr itemnode = NULL;
 +	if (nodep && index >= 0) {
 +		dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index);
 +		itemnode = start_point.node ? start_point.node : dom_nodelist_iter_start_first_child(nodep);
 +		for (; start_point.index > 0 && itemnode; itemnode = itemnode->next, start_point.index--);
 +	}
 +	dom_ret_node_to_zobj(map, itemnode, return_value);
 +	if (itemnode) {
 +		dom_map_cache_obj(map, itemnode, index, return_value);
 +	}
 +}
 +
 +static void dom_map_get_elements_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	xmlNodePtr itemnode = NULL;
 +	if (nodep && index >= 0) {
 +		dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index);
 +		if (start_point.node) {
 +			/* Guaranteed to be an element */
 +			itemnode = start_point.node;
 +		} else {
 +			/* Fetch first element child */
 +			itemnode = nodep->children;
 +			while (itemnode && itemnode->type != XML_ELEMENT_NODE) {
 +				itemnode = itemnode->next;
 +			}
 +		}
 +
 +		for (; start_point.index > 0 && itemnode; --start_point.index) {
 +			do {
 +				itemnode = itemnode->next;
 +			} while (itemnode && itemnode->type != XML_ELEMENT_NODE);
 +		}
 +		if (itemnode && itemnode->type != XML_ELEMENT_NODE) {
 +			itemnode = NULL;
 +		}
 +	}
 +	dom_ret_node_to_zobj(map, itemnode, return_value);
 +	if (itemnode) {
 +		dom_map_cache_obj(map, itemnode, index, return_value);
 +	}
 +}
 +
 +static void dom_map_collection_named_item_elements_iter(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
 +{
 +	if (iter->candidate != iter->basep->children) {
 +		iter->candidate = iter->candidate->next;
 +	}
 +	while (iter->candidate && iter->candidate->type != XML_ELEMENT_NODE) {
 +		iter->candidate = iter->candidate->next;
 +	}
 +}
 +
 +static void dom_map_collection_named_item_null(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
 +{
 +}
 +
 +static void dom_map_get_by_tag_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	xmlNodePtr itemnode = NULL;
 +	if (nodep && index >= 0) {
 +		zend_long count = 0;
 +		dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index);
 +		itemnode = start_point.node ? start_point.node : php_dom_first_child_of_container_node(nodep);
 +		itemnode = dom_get_elements_by_tag_name_ns_raw(nodep, itemnode, map->ns, map->local, map->local_lower, &count, start_point.index);
 +	}
 +	dom_ret_node_to_zobj(map, itemnode, return_value);
 +	if (itemnode) {
 +		dom_map_cache_obj(map, itemnode, index, return_value);
 +	}
 +}
 +
 +static void dom_map_get_by_class_name_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	xmlNodePtr itemnode = NULL;
 +	if (nodep && index >= 0) {
 +		dom_node_idx_pair start_point = dom_obj_map_get_start_point(map, nodep, index);
 +		if (start_point.node) {
 +			if (start_point.index > 0) {
 +				/* Only start iteration at next point if we actually have an index to seek to. */
 +				itemnode = php_dom_next_in_tree_order(start_point.node, nodep);
 +			} else {
 +				itemnode = start_point.node;
 +			}
 +		} else {
 +			itemnode = php_dom_first_child_of_container_node(nodep);
 +		}
 +
 +		do {
 +			--start_point.index;
 +			while (itemnode != NULL && !dom_matches_class_name(map, itemnode)) {
 +				itemnode = php_dom_next_in_tree_order(itemnode, nodep);
 +			}
 +		} while (start_point.index > 0 && itemnode);
 +	}
 +	dom_ret_node_to_zobj(map, itemnode, return_value);
 +	if (itemnode) {
 +		dom_map_cache_obj(map, itemnode, index, return_value);
 +	}
 +}
 +
 +static void dom_map_collection_named_item_by_tag_name_iter(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
 +{
 +	iter->candidate = dom_get_elements_by_tag_name_ns_raw(iter->basep, iter->candidate, map->ns, map->local, map->local_lower, &iter->cur, iter->next);
 +	iter->next = iter->cur + 1;
 +}
 +
 +static void dom_map_collection_named_item_by_class_name_iter(dom_nnodemap_object *map, php_dom_obj_map_collection_iter *iter)
 +{
 +	xmlNodePtr basep = iter->basep;
 +	xmlNodePtr nodep = iter->candidate ? php_dom_next_in_tree_order(iter->candidate, basep) : php_dom_first_child_of_container_node(basep);
 +
 +	while (nodep != NULL && !dom_matches_class_name(map, nodep)) {
 +		nodep = php_dom_next_in_tree_order(nodep, basep);
 +	}
 +
 +	iter->candidate = nodep;
 +}
 +
 +static void dom_map_get_null_item(dom_nnodemap_object *map, zend_long index, zval *return_value)
 +{
 +	RETURN_NULL();
 +}
 +
 +/***********************
 + * === Common APIs === *
 + ***********************/
 +
 +zend_long php_dom_get_nodelist_length(dom_object *obj)
 +{
 +	dom_nnodemap_object *objmap = obj->ptr;
 +
 +	if (objmap->handler->use_cache) {
 +		xmlNodePtr nodep = dom_object_get_node(objmap->baseobj);
 +		if (!nodep) {
 +			return 0;
 +		}
 +
 +		if (!php_dom_is_cache_tag_stale_from_node(&objmap->cache_tag, nodep)) {
 +			if (objmap->cached_length >= 0) {
 +				return objmap->cached_length;
 +			}
 +			/* Only the length is out-of-date, the cache tag is still valid.
 +			 * Therefore, only overwrite the length and keep the currently cached object. */
 +		} else {
 +			php_dom_mark_cache_tag_up_to_date_from_node(&objmap->cache_tag, nodep);
 +			reset_objmap_cache(objmap);
 +		}
 +	}
 +
 +	zend_long count = objmap->handler->length(objmap);
 +
 +	if (objmap->handler->use_cache) {
 +		objmap->cached_length = count;
 +	}
 +
 +	return count;
 +}
 +
 +void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler)
 +{
 +	dom_nnodemap_object *mapptr = intern->ptr;
 +
 +	ZEND_ASSERT(basenode != NULL);
 +
 +	GC_ADDREF(&basenode->std);
 +
 +	xmlDocPtr doc = basenode->document ? basenode->document->ptr : NULL;
 +
 +	mapptr->handler = handler;
 +	mapptr->baseobj = basenode;
 +	mapptr->ht = ht;
 +	if (EXPECTED(doc != NULL)) {
 +		mapptr->dict = doc->dict;
 +		xmlDictReference(doc->dict);
 +	}
 +
 +	const xmlChar* tmp;
 +
 +	if (local) {
 +		int len = (int) ZSTR_LEN(local);
 +		if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(local), len)) != NULL) {
 +			mapptr->local = BAD_CAST tmp;
 +		} else {
 +			mapptr->local = BAD_CAST ZSTR_VAL(zend_string_copy(local));
 +			mapptr->release_local = true;
 +		}
 +		mapptr->local_lower = zend_string_tolower(local);
 +	}
 +
 +	if (ns) {
 +		int len = (int) ZSTR_LEN(ns);
 +		if (doc != NULL && (tmp = xmlDictExists(doc->dict, (const xmlChar *)ZSTR_VAL(ns), len)) != NULL) {
 +			mapptr->ns = BAD_CAST tmp;
 +		} else {
 +			mapptr->ns = BAD_CAST ZSTR_VAL(zend_string_copy(ns));
 +			mapptr->release_ns = true;
 +		}
 +	}
 +}
 +
 +void php_dom_obj_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value)
 +{
 +	if (EXPECTED(objmap)) {
 +		objmap->handler->get_item(objmap, index, return_value);
 +	} else {
 +		RETURN_NULL();
 +	}
 +}
 +
 +void php_dom_obj_map_get_ns_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value)
 +{
 +	xmlNodePtr itemnode = objmap->handler->get_ns_named_item(objmap, named, ns);
 +	if (itemnode) {
 +		DOM_RET_OBJ(itemnode, objmap->baseobj);
 +	} else {
 +		RETURN_NULL();
 +	}
 +}
 +
 +/**********************
 + * === Named item === *
 + **********************/
 +
 +static xmlNodePtr dom_map_get_ns_named_item_entity(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	return xmlHashLookup(map->ht, BAD_CAST ZSTR_VAL(named));
 +}
 +
 +static bool dom_map_has_ns_named_item_xmlht(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	return dom_map_get_ns_named_item_entity(map, named, ns) != NULL;
 +}
 +
 +static xmlNodePtr dom_map_get_ns_named_item_notation(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	xmlNotationPtr notation = xmlHashLookup(map->ht, BAD_CAST ZSTR_VAL(named));
 +	if (notation) {
 +		return create_notation(notation->name, notation->PublicID, notation->SystemID);
 +	}
 +	return NULL;
 +}
 +
 +static xmlNodePtr dom_map_get_ns_named_item_prop(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	xmlNodePtr nodep = dom_object_get_node(map->baseobj);
 +	if (nodep) {
 +		if (ns) {
 +			return (xmlNodePtr) xmlHasNsProp(nodep, BAD_CAST ZSTR_VAL(named), BAD_CAST ns);
 +		} else {
 +			if (php_dom_follow_spec_intern(map->baseobj)) {
 +				return (xmlNodePtr) php_dom_get_attribute_node(nodep, BAD_CAST ZSTR_VAL(named), ZSTR_LEN(named));
 +			} else {
 +				return (xmlNodePtr) xmlHasProp(nodep, BAD_CAST ZSTR_VAL(named));
 +			}
 +		}
 +	}
 +	return NULL;
 +}
 +
 +static bool dom_map_has_ns_named_item_prop(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	return dom_map_get_ns_named_item_prop(map, named, ns) != NULL;
 +}
 +
 +static xmlNodePtr dom_map_get_ns_named_item_null(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	return NULL;
 +}
 +
 +static bool dom_map_has_ns_named_item_null(dom_nnodemap_object *map, const zend_string *named, const char *ns)
 +{
 +	return false;
 +}
 +
 +/**************************
 + * === Handler tables === *
 + **************************/
 +
 +const php_dom_obj_map_handler php_dom_obj_map_attributes = {
 +	.length = dom_map_get_prop_length,
 +	.get_item = dom_map_get_attributes_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_prop,
 +	.has_ns_named_item = dom_map_has_ns_named_item_prop,
 +	.collection_named_item_iter = NULL,
 +	.use_cache = false,
 +	.nameless = false,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_by_tag_name = {
 +	.length = dom_map_get_by_tag_name_length,
 +	.get_item = dom_map_get_by_tag_name_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = dom_map_collection_named_item_by_tag_name_iter,
 +	.use_cache = true,
 +	.nameless = true,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_by_class_name = {
 +	.length = dom_map_get_by_class_name_length,
 +	.get_item = dom_map_get_by_class_name_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = dom_map_collection_named_item_by_class_name_iter,
 +	.use_cache = true,
 +	.nameless = true,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_child_nodes = {
 +	.length = dom_map_get_nodes_length,
 +	.get_item = dom_map_get_nodes_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = NULL,
 +	.use_cache = true,
 +	.nameless = true,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_nodeset = {
 +	.length = dom_map_get_nodeset_length,
 +	.get_item = dom_map_get_nodeset_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = NULL,
 +	.use_cache = false,
 +	.nameless = true,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_entities = {
 +	.length = dom_map_get_xmlht_length,
 +	.get_item = dom_map_get_entity_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_entity,
 +	.has_ns_named_item = dom_map_has_ns_named_item_xmlht,
 +	.collection_named_item_iter = NULL,
 +	.use_cache = false,
 +	.nameless = false,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_notations = {
 +	.length = dom_map_get_xmlht_length,
 +	.get_item = dom_map_get_notation_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_notation,
 +	.has_ns_named_item = dom_map_has_ns_named_item_xmlht,
 +	.collection_named_item_iter = NULL,
 +	.use_cache = false,
 +	.nameless = false,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_child_elements = {
 +	.length = dom_map_get_elements_length,
 +	.get_item = dom_map_get_elements_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = dom_map_collection_named_item_elements_iter,
 +	.use_cache = true,
 +	.nameless = true,
 +};
 +
 +const php_dom_obj_map_handler php_dom_obj_map_noop = {
 +	.length = dom_map_get_zero_length,
 +	.get_item = dom_map_get_null_item,
 +	.get_ns_named_item = dom_map_get_ns_named_item_null,
 +	.has_ns_named_item = dom_map_has_ns_named_item_null,
 +	.collection_named_item_iter = dom_map_collection_named_item_null,
 +	.use_cache = false,
 +	.nameless = true,
 +};
 +
 +#endif
diff --cc ext/dom/obj_map.h
index e7231eb7b10,00000000000..beed08bbb16
mode 100644,000000..100644
--- a/ext/dom/obj_map.h
+++ b/ext/dom/obj_map.h
@@@ -1,74 -1,0 +1,74 @@@
 +/*
 +   +----------------------------------------------------------------------+
 +   | 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: Niels Dossche <nielsdos@php.net>                            |
++   | Authors: Nora Dossche  <ndossche@php.net>                            |
 +   +----------------------------------------------------------------------+
 +*/
 +
 +#ifndef PHP_DOM_OBJ_MAP_H
 +#define PHP_DOM_OBJ_MAP_H
 +
 +typedef struct dom_nnodemap_object dom_nnodemap_object;
 +
 +typedef struct php_dom_obj_map_collection_iter {
 +	zend_long cur, next;
 +	xmlNodePtr candidate, basep;
 +} php_dom_obj_map_collection_iter;
 +
 +typedef struct php_dom_obj_map_handler {
 +	zend_long (*length)(dom_nnodemap_object *);
 +	void (*get_item)(dom_nnodemap_object *, zend_long, zval *);
 +	xmlNodePtr (*get_ns_named_item)(dom_nnodemap_object *, const zend_string *, const char *);
 +	bool (*has_ns_named_item)(dom_nnodemap_object *, const zend_string *, const char *);
 +	void (*collection_named_item_iter)(dom_nnodemap_object *, php_dom_obj_map_collection_iter *);
 +	bool use_cache;
 +	bool nameless;
 +} php_dom_obj_map_handler;
 +
 +typedef struct dom_nnodemap_object {
 +	dom_object *baseobj;
 +	zend_long cached_length;
 +	union {
 +		xmlHashTable *ht;
 +		HashTable *array;
 +		struct {
 +			xmlChar *local;
 +			zend_string *local_lower;
 +			xmlChar *ns;
 +		};
 +	};
 +	php_libxml_cache_tag cache_tag;
 +	dom_object *cached_obj;
 +	zend_long cached_obj_index;
 +	xmlDictPtr dict;
 +	const php_dom_obj_map_handler *handler;
 +	bool release_local;
 +	bool release_ns;
 +	bool release_array;
 +} dom_nnodemap_object;
 +
 +void php_dom_create_obj_map(dom_object *basenode, dom_object *intern, xmlHashTablePtr ht, zend_string *local, zend_string *ns, const php_dom_obj_map_handler *handler);
 +void php_dom_obj_map_get_ns_named_item_into_zval(dom_nnodemap_object *objmap, const zend_string *named, const char *ns, zval *return_value);
 +void php_dom_obj_map_get_item_into_zval(dom_nnodemap_object *objmap, zend_long index, zval *return_value);
 +zend_long php_dom_get_nodelist_length(dom_object *obj);
 +
 +extern const php_dom_obj_map_handler php_dom_obj_map_attributes;
 +extern const php_dom_obj_map_handler php_dom_obj_map_by_tag_name;
 +extern const php_dom_obj_map_handler php_dom_obj_map_by_class_name;
 +extern const php_dom_obj_map_handler php_dom_obj_map_child_elements;
 +extern const php_dom_obj_map_handler php_dom_obj_map_child_nodes;
 +extern const php_dom_obj_map_handler php_dom_obj_map_nodeset;
 +extern const php_dom_obj_map_handler php_dom_obj_map_entities;
 +extern const php_dom_obj_map_handler php_dom_obj_map_notations;
 +extern const php_dom_obj_map_handler php_dom_obj_map_noop;
 +
 +#endif
diff --cc ext/lexbor/php_lexbor.c
index af61f90291d,00000000000..f18a09a5c4f
mode 100644,000000..100644
--- a/ext/lexbor/php_lexbor.c
+++ b/ext/lexbor/php_lexbor.c
@@@ -1,80 -1,0 +1,80 @@@
 +/*
 +   +----------------------------------------------------------------------+
 +   | 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: Niels Dossche <nielsdos@php.net>                            |
++   | Authors: Nora Dossche  <ndossche@php.net>                            |
 +   |          Mate Kocsis <kocsismate@php.net>                            |
 +   +----------------------------------------------------------------------+
 +*/
 +
 +#ifdef HAVE_CONFIG_H
 +#include <config.h>
 +#endif
 +
 +#include "php.h"
 +#include "zend_globals.h"
 +#include "ext/standard/info.h"
 +#include "lexbor/core/base.h"
 +#include "lexbor/core/types.h"
 +#include "lexbor/core/lexbor.h"
 +
 +#ifdef HAVE_LEXBOR
 +
 +#include "php_lexbor.h"
 +
 +static void *php_lexbor_malloc(size_t size)
 +{
 +	return emalloc(size);
 +}
 +
 +static void *php_lexbor_realloc(void *dst, size_t size)
 +{
 +	return erealloc(dst, size);
 +}
 +
 +static void *php_lexbor_calloc(size_t num, size_t size)
 +{
 +	return ecalloc(num, size);
 +}
 +
 +static void php_lexbor_free(void *ptr)
 +{
 +	efree(ptr);
 +}
 +
 +static PHP_MINFO_FUNCTION(lexbor)
 +{
 +	php_info_print_table_start();
 +	php_info_print_table_row(2, "Lexbor support", "active");
 +	php_info_print_table_row(2, "Lexbor version", LEXBOR_VERSION_STRING);
 +	php_info_print_table_end();
 +}
 +
 +static PHP_MINIT_FUNCTION(lexbor)
 +{
 +	lexbor_memory_setup(php_lexbor_malloc, php_lexbor_realloc, php_lexbor_calloc, php_lexbor_free);
 +	return SUCCESS;
 +}
 +
 +zend_module_entry lexbor_module_entry = {
 +	STANDARD_MODULE_HEADER,
 +	"lexbor",                   /* extension name */
 +	NULL,                       /* extension function list */
 +	PHP_MINIT(lexbor),          /* extension-wide startup function */
 +	NULL,                       /* extension-wide shutdown function */
 +	NULL,                       /* per-request startup function */
 +	NULL,                       /* per-request shutdown function */
 +	PHP_MINFO(lexbor),          /* information function */
 +	PHP_VERSION,
 +	STANDARD_MODULE_PROPERTIES
 +};
 +
 +#endif
diff --cc ext/lexbor/php_lexbor.h
index fbdc71746dc,00000000000..cf89c12f4d2
mode 100644,000000..100644
--- a/ext/lexbor/php_lexbor.h
+++ b/ext/lexbor/php_lexbor.h
@@@ -1,30 -1,0 +1,30 @@@
 +/*
 +   +----------------------------------------------------------------------+
 +   | 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: Niels Dossche <nielsdos@php.net>                            |
++   | Authors: Nora Dossche  <ndossche@php.net>                            |
 +   |          Mate Kocsis <kocsismate@php.net>                            |
 +   +----------------------------------------------------------------------+
 +*/
 +
 +#ifndef PHP_LEXBOR_H
 +#define PHP_LEXBOR_H
 +
 +#ifdef HAVE_LEXBOR
 +extern zend_module_entry lexbor_module_entry;
 +#define phpext_lexbor_ptr &lexbor_module_entry
 +
 +#if defined(ZTS) && defined(COMPILE_DL_LEXBOR)
 +ZEND_TSRMLS_CACHE_EXTERN()
 +#endif
 +
 +#endif
 +#endif /* PHP_LEXBOR_H */
diff --cc ext/libxml/image_svg.c
index 091f7e28316,00000000000..e92b3ffca7e
mode 100644,000000..100644
--- a/ext/libxml/image_svg.c
+++ b/ext/libxml/image_svg.c
@@@ -1,178 -1,0 +1,178 @@@
 +/*
 +   +----------------------------------------------------------------------+
 +   | 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: Niels Dossche <nielsdos@php.net>                            |
++   | Authors: Nora Dossche  <ndossche@php.net>                            |
 +   +----------------------------------------------------------------------+
 + */
 +
 +#ifdef HAVE_CONFIG_H
 +#include "config.h"
 +#endif
 +
 +#include "php.h"
 +#include "image_svg.h"
 +#include "php_libxml.h"
 +
 +#include "ext/standard/php_image.h"
 +
 +#include <libxml/xmlreader.h>
 +
 +#ifdef HAVE_LIBXML
 +
 +static int svg_image_type_id;
 +
 +static int php_libxml_svg_stream_read(void *context, char *buffer, int len)
 +{
 +	return php_stream_read(context, buffer, len);
 +}
 +
 +/* Sanity check that the input only contains characters valid for a dimension (numbers with units, e.g. 5cm).
 + * This also protects the user against injecting XSS.
 + * Only accept [0-9]+[a-zA-Z]* */
 +static bool php_libxml_parse_dimension(const xmlChar *input, const xmlChar **unit_position)
 +{
 +	if (!(*input >= '0' && *input <= '9')) {
 +		return false;
 +	}
 +
 +	input++;
 +
 +	while (*input) {
 +		if (!(*input >= '0' && *input <= '9')) {
 +			if ((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z')) {
 +				break;
 +			}
 +			return false;
 +		}
 +		input++;
 +	}
 +
 +	*unit_position = input;
 +
 +	while (*input) {
 +		if (!((*input >= 'a' && *input <= 'z') || (*input >= 'A' && *input <= 'Z'))) {
 +			return false;
 +		}
 +		input++;
 +	}
 +
 +	return true;
 +}
 +
 +zend_result php_libxml_svg_image_handle(php_stream *stream, struct php_gfxinfo **result)
 +{
 +	if (php_stream_rewind(stream)) {
 +		return FAILURE;
 +	}
 +
 +	/* Early check before doing more expensive work */
 +	if (php_stream_getc(stream) != '<') {
 +		return FAILURE;
 +	}
 +
 +	if (php_stream_rewind(stream)) {
 +		return FAILURE;
 +	}
 +
 +	PHP_LIBXML_SANITIZE_GLOBALS(reader_for_stream);
 +	xmlTextReaderPtr reader = xmlReaderForIO(
 +		php_libxml_svg_stream_read,
 +		NULL,
 +		stream,
 +		NULL,
 +		NULL,
 +		XML_PARSE_NOWARNING | XML_PARSE_NOERROR | XML_PARSE_NONET
 +	);
 +	PHP_LIBXML_RESTORE_GLOBALS(reader_for_stream);
 +
 +	if (!reader) {
 +		return FAILURE;
 +	}
 +
 +	bool is_svg = false;
 +	while (xmlTextReaderRead(reader) == 1) {
 +		if (xmlTextReaderNodeType(reader) == XML_READER_TYPE_ELEMENT) {
 +			/* Root must be an svg element */
 +			const xmlChar *name = xmlTextReaderConstLocalName(reader);
 +			if (!name || strcasecmp((const char *) name, "svg") != 0) {
 +				break;
 +			}
 +
 +			xmlChar *width = xmlTextReaderGetAttribute(reader, BAD_CAST "width");
 +			xmlChar *height = xmlTextReaderGetAttribute(reader, BAD_CAST "height");
 +			const xmlChar *width_unit_position, *height_unit_position;
 +			if (!width || !height
 +				|| !php_libxml_parse_dimension(width, &width_unit_position)
 +				|| !php_libxml_parse_dimension(height, &height_unit_position)) {
 +				xmlFree(width);
 +				xmlFree(height);
 +				break;
 +			}
 +
 +			is_svg = true;
 +			if (result) {
 +				*result = ecalloc(1, sizeof(**result));
 +				(*result)->width = ZEND_STRTOL((const char *) width, NULL, 10);
 +				(*result)->height = ZEND_STRTOL((const char *) height, NULL, 10);
 +				if (*width_unit_position) {
 +					(*result)->width_unit = zend_string_init((const char*) width_unit_position,
 +															 xmlStrlen(width_unit_position), false);
 +				}
 +				if (*height_unit_position) {
 +					(*result)->height_unit = zend_string_init((const char*) height_unit_position,
 +															  xmlStrlen(height_unit_position), false);
 +				}
 +			}
 +
 +			xmlFree(width);
 +			xmlFree(height);
 +			break;
 +		}
 +	}
 +
 +	xmlFreeTextReader(reader);
 +
 +	return is_svg ? SUCCESS : FAILURE;
 +}
 +
 +zend_result php_libxml_svg_image_identify(php_stream *stream)
 +{
 +	return php_libxml_svg_image_handle(stream, NULL);
 +}
 +
 +struct php_gfxinfo *php_libxml_svg_image_get_info(php_stream *stream)
 +{
 +	struct php_gfxinfo *result = NULL;
 +	zend_result status = php_libxml_svg_image_handle(stream, &result);
 +	ZEND_ASSERT((status == SUCCESS) == (result != NULL));
 +	return result;
 +}
 +
 +static const struct php_image_handler svg_image_handler = {
 +	"image/svg+xml",
 +	".svg",
 +	PHP_IMAGE_CONST_NAME("SVG"),
 +	php_libxml_svg_image_identify,
 +	php_libxml_svg_image_get_info,
 +};
 +
 +void php_libxml_register_image_svg_handler(void)
 +{
 +	svg_image_type_id = php_image_register_handler(&svg_image_handler);
 +}
 +
 +zend_result php_libxml_unregister_image_svg_handler(void)
 +{
 +	return php_image_unregister_handler(svg_image_type_id);
 +}
 +
 +#endif
diff --cc ext/libxml/image_svg.h
index d023334af36,0013775d18f..6a346e2b9b8
--- a/ext/libxml/image_svg.h
+++ b/ext/libxml/image_svg.h
@@@ -10,7 -10,8 +10,7 @@@
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
-    | Authors: Niels Dossche <nielsdos@php.net>                             |
 -   | Author: Sascha Schumann <sascha@schumann.cx>                         |
 -   |         Xinchen Hui <laruence@php.net>                               |
++   | Authors: Nora Dossche  <ndossche@php.net>                             |
     +----------------------------------------------------------------------+
   */

diff --cc ext/soap/php_http.c
index c7f3732f298,e9fd68007d2..f3df860bb08
--- a/ext/soap/php_http.c
+++ b/ext/soap/php_http.c
@@@ -1148,42 -1143,37 +1148,44 @@@ try_again
  		char *loc;

  		if ((loc = get_http_header_value(ZSTR_VAL(http_headers), "Location:")) != NULL) {
 -			php_url *new_url  = php_url_parse(loc);
 +			const php_uri_parser *uri_parser = php_uri_get_parser(uri_parser_class);
 +			if (uri_parser == NULL) {
 +				efree(loc);
 +				zend_argument_value_error(6, "must be a valid URI parser name");
 +				return FALSE;
 +			}
 +
 +			php_uri *new_uri = php_uri_parse_to_struct(uri_parser, loc, strlen(loc), PHP_URI_COMPONENT_READ_MODE_RAW, true);
  			efree(loc);

 -			if (new_url != NULL) {
 +			if (new_uri != NULL) {
  				zend_string_release_ex(http_headers, 0);
  				zend_string_release_ex(http_body, 0);
 -				if (new_url->scheme == NULL && new_url->path != NULL) {
 -					new_url->scheme = phpurl->scheme ? zend_string_copy(phpurl->scheme) : NULL;
 -					if (new_url->host == NULL) {
 -						new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL;
 -						new_url->port = phpurl->port;
 +				if (new_uri->scheme == NULL && new_uri->path != NULL) {
 +					new_uri->scheme = uri->scheme ? zend_string_copy(uri->scheme) : NULL;
- 					new_uri->host = uri->host ? zend_string_copy(uri->host) : NULL;
- 					new_uri->port = uri->port;
++					if (new_uri->host == NULL) {
++						new_uri->host = uri->host ? zend_string_copy(uri->host) : NULL;
++						new_uri->port = uri->port;
+ 					}
 -					if (new_url->path && ZSTR_VAL(new_url->path)[0] != '/') {
 -						if (phpurl->path) {
 -							char *t = ZSTR_VAL(phpurl->path);
 +					if (new_uri->path && ZSTR_VAL(new_uri->path)[0] != '/') {
 +						if (uri->path) {
 +							char *t = ZSTR_VAL(uri->path);
  							char *p = strrchr(t, '/');
  							if (p) {
 -								zend_string *s = zend_string_alloc((p - t) + ZSTR_LEN(new_url->path) + 2, 0);
 +								zend_string *s = zend_string_alloc((p - t) + ZSTR_LEN(new_uri->path) + 2, 0);
  								strncpy(ZSTR_VAL(s), t, (p - t) + 1);
  								ZSTR_VAL(s)[(p - t) + 1] = 0;
 -								strcat(ZSTR_VAL(s), ZSTR_VAL(new_url->path));
 -								zend_string_release_ex(new_url->path, 0);
 -								new_url->path = s;
 +								strcat(ZSTR_VAL(s), ZSTR_VAL(new_uri->path));
 +								zend_string_release_ex(new_uri->path, 0);
 +								new_uri->path = s;
  							}
  						} else {
 -							zend_string *s = zend_string_alloc(ZSTR_LEN(new_url->path) + 2, 0);
 +							zend_string *s = zend_string_alloc(ZSTR_LEN(new_uri->path) + 2, 0);
  							ZSTR_VAL(s)[0] = '/';
  							ZSTR_VAL(s)[1] = 0;
 -							strcat(ZSTR_VAL(s), ZSTR_VAL(new_url->path));
 -							zend_string_release_ex(new_url->path, 0);
 -							new_url->path = s;
 +							strcat(ZSTR_VAL(s), ZSTR_VAL(new_uri->path));
 +							zend_string_release_ex(new_uri->path, 0);
 +							new_uri->path = s;
  						}
  					}
  				}