diff --git a/UPGRADING b/UPGRADING
index 15c1aad15db0..27a095288d33 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -500,6 +500,13 @@ PHP 8.6 UPGRADE NOTES
. Improved performance of indentation generation in json_encode()
when using PHP_JSON_PRETTY_PRINT.
+- Intl:
+ . Improved performance of IntlCalendar::getAvailableLocales() and
+ IntlDateFormatter::localtime() / datefmt_localtime() by pre-allocating
+ their returned arrays.
+ . Improved performance of transliterator_list_ids() and
+ resourcebundle_locales() by pre-allocating their returned arrays.
+
- Phar:
. Reduced temporary allocations when iterating Phar directories.
@@ -514,10 +521,14 @@ PHP 8.6 UPGRADE NOTES
. Improved performance of str_split().
- URI:
+ . Improved performance of Uri\WhatWg\Url::parse() when collecting
+ validation errors by pre-allocating the error array.
. Reduced allocations when reading IPv6/IPFuture hosts and paths with
Uri\Rfc3986\Uri.
. Improved performance and memory consumption when using normalizing
(non-raw) getters on already-normalized URIs with Uri\Rfc3986\Uri.
- Zip:
+ . Improved performance of ZipArchive::addGlob() and
+ ZipArchive::addPattern() by pre-allocating their returned arrays.
. Avoid string copies in ZipArchive::addFromString().
diff --git a/ext/dom/tests/xpath_php_function_removes_argument_node.phpt b/ext/dom/tests/xpath_php_function_removes_argument_node.phpt
new file mode 100644
index 000000000000..e6696617be25
--- /dev/null
+++ b/ext/dom/tests/xpath_php_function_removes_argument_node.phpt
@@ -0,0 +1,33 @@
+--TEST--
+DOMXPath: a php:function callback that removes its argument node must not free it mid-evaluation
+--EXTENSIONS--
+dom
+--FILE--
+loadXML('1234');
+$xp = new DOMXPath($doc);
+$xp->registerNamespace('php', 'http://php.net/xpath');
+$xp->registerPhpFunctions();
+
+function cb($nodes) {
+ foreach ($nodes as $n) {
+ if ($n->parentNode) {
+ $n->parentNode->removeChild($n);
+ }
+ }
+ return true;
+}
+
+$res = $xp->query('//a[php:function("cb", .)]');
+foreach ($res as $r) {
+ var_dump($r->nodeName);
+}
+echo "done\n";
+?>
+--EXPECT--
+string(1) "a"
+string(1) "a"
+string(1) "a"
+string(1) "a"
+done
diff --git a/ext/dom/xpath_callbacks.c b/ext/dom/xpath_callbacks.c
index 56d94a664092..622dd187dd84 100644
--- a/ext/dom/xpath_callbacks.c
+++ b/ext/dom/xpath_callbacks.c
@@ -379,11 +379,19 @@ static zval *php_dom_xpath_callback_fetch_args(xmlXPathParserContextPtr ctxt, ui
return params;
}
-static void php_dom_xpath_callback_cleanup_args(zval *params, uint32_t param_count)
+static void php_dom_xpath_callback_cleanup_args(php_dom_xpath_callbacks *xpath_callbacks, zval *params, uint32_t param_count)
{
if (params) {
for (uint32_t i = 0; i < param_count; i++) {
- zval_ptr_dtor(¶ms[i]);
+ zval *param = ¶ms[i];
+ if (Z_TYPE_P(param) == IS_OBJECT || Z_TYPE_P(param) == IS_ARRAY) {
+ if (xpath_callbacks->node_list == NULL) {
+ xpath_callbacks->node_list = zend_new_array(0);
+ }
+ zend_hash_next_index_insert_new(xpath_callbacks->node_list, param);
+ } else {
+ zval_ptr_dtor(param);
+ }
}
efree(params);
}
@@ -478,7 +486,7 @@ PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_php_ns(php_dom_xpath_cal
cleanup:
xmlXPathFreeObject(obj);
- php_dom_xpath_callback_cleanup_args(params, param_count);
+ php_dom_xpath_callback_cleanup_args(xpath_callbacks, params, param_count);
cleanup_no_obj:
if (UNEXPECTED(result != SUCCESS)) {
/* Push sentinel value */
@@ -506,7 +514,7 @@ PHP_DOM_EXPORT zend_result php_dom_xpath_callbacks_call_custom_ns(php_dom_xpath_
zend_result result = php_dom_xpath_callback_dispatch(xpath_callbacks, ns, ctxt, params, param_count, function_name, function_name_length);
- php_dom_xpath_callback_cleanup_args(params, param_count);
+ php_dom_xpath_callback_cleanup_args(xpath_callbacks, params, param_count);
if (UNEXPECTED(result != SUCCESS)) {
/* Push sentinel value */
valuePush(ctxt, xmlXPathNewString((const xmlChar *) ""));
diff --git a/ext/intl/resourcebundle/resourcebundle_class.cpp b/ext/intl/resourcebundle/resourcebundle_class.cpp
index f796a6ffc8aa..7e22e9e8c7df 100644
--- a/ext/intl/resourcebundle/resourcebundle_class.cpp
+++ b/ext/intl/resourcebundle/resourcebundle_class.cpp
@@ -339,6 +339,7 @@ U_CFUNC PHP_FUNCTION( resourcebundle_locales )
size_t bundlename_len = 0;
const char * entry;
int entry_len;
+ int32_t count;
UEnumeration *icuenum;
UErrorCode icuerror = U_ZERO_ERROR;
@@ -364,7 +365,13 @@ U_CFUNC PHP_FUNCTION( resourcebundle_locales )
uenum_reset( icuenum, &icuerror );
INTL_CHECK_STATUS(icuerror, "Cannot iterate locales list");
- array_init( return_value );
+ count = uenum_count( icuenum, &icuerror );
+ if (U_FAILURE(icuerror)) {
+ count = 0;
+ icuerror = U_ZERO_ERROR;
+ }
+
+ array_init_size( return_value, count );
while ((entry = uenum_next( icuenum, &entry_len, &icuerror ))) {
add_next_index_stringl( return_value, (char *) entry, entry_len);
}
diff --git a/ext/intl/transliterator/transliterator_methods.cpp b/ext/intl/transliterator/transliterator_methods.cpp
index 45dd00b42bcf..8efcff95b310 100644
--- a/ext/intl/transliterator/transliterator_methods.cpp
+++ b/ext/intl/transliterator/transliterator_methods.cpp
@@ -21,6 +21,8 @@
#include
#endif
+#include
+
extern "C" {
#include "php_intl.h"
#include "intl_data.h"
@@ -226,6 +228,7 @@ U_CFUNC PHP_FUNCTION( transliterator_list_ids )
UEnumeration *en;
const UChar *elem;
int32_t elem_len;
+ int32_t count;
UErrorCode status = U_ZERO_ERROR;
intl_error_reset( nullptr );
@@ -236,7 +239,14 @@ U_CFUNC PHP_FUNCTION( transliterator_list_ids )
INTL_CHECK_STATUS( status,
"Failed to obtain registered transliterators" );
- array_init( return_value );
+ count = uenum_count( en, &status );
+ if( U_FAILURE( status ) )
+ {
+ count = 0;
+ status = U_ZERO_ERROR;
+ }
+
+ array_init_size( return_value, count );
while( (elem = uenum_unext( en, &elem_len, &status )) )
{
zend_string *el = intl_convert_utf16_to_utf8(elem, elem_len, &status );
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 6c50619a35d8..a9f6b579977e 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -295,8 +295,8 @@ static zend_object *reflection_objects_new(zend_class_entry *class_type) /* {{{
/* }}} */
static void _const_string(smart_str *str, const char *name, zval *value, const char *indent);
-static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent);
-static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent);
+static void _function_string(smart_str *str, const zend_function *fptr, const zend_class_entry *scope, const char* indent);
+static void _property_string(smart_str *str, const zend_property_info *prop, const char *prop_name, const char* indent);
static void _class_const_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
static void _enum_case_string(smart_str *str, const zend_string *name, zend_class_constant *c, const char* indent);
static void _class_string(smart_str *str, zend_class_entry *ce, zval *obj, const char *indent);
@@ -699,7 +699,7 @@ static zend_op *get_recv_op(const zend_op_array *op_array, uint32_t offset)
return NULL;
}
-static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) {
+static zval *get_default_from_recv(const zend_op_array *op_array, uint32_t offset) {
zend_op *recv = get_recv_op(op_array, offset);
if (!recv || recv->opcode != ZEND_RECV_INIT) {
return NULL;
@@ -708,7 +708,7 @@ static zval *get_default_from_recv(zend_op_array *op_array, uint32_t offset) {
return RT_CONSTANT(recv, recv->op2);
}
-static void format_default_value(smart_str *str, zval *value) {
+static void format_default_value(smart_str *str, const zval *value) {
if (smart_str_append_zval(str, value, SIZE_MAX) == SUCCESS) {
/* Nothing to do. */
} else if (Z_TYPE_P(value) == IS_ARRAY) {
@@ -741,8 +741,8 @@ static void format_default_value(smart_str *str, zval *value) {
/* This branch is reached if the constant AST was already evaluated and
* resulted in an object; enums are already handled in smart_str_append_zval()
* (GH-15902) */
- zend_object *obj = Z_OBJ_P(value);
- zend_class_entry *class = obj->ce;
+ const zend_object *obj = Z_OBJ_P(value);
+ const zend_class_entry *class = obj->ce;
ZEND_ASSERT(!(class->ce_flags & ZEND_ACC_ENUM));
smart_str_appends(str, "object(");
smart_str_append(str, class->name);
@@ -756,7 +756,7 @@ static void format_default_value(smart_str *str, zval *value) {
}
/* {{{ _parameter_string */
-static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_arg_info *arg_info, uint32_t offset, bool required, char* indent)
+static void _parameter_string(smart_str *str, const zend_function *fptr, const struct _zend_arg_info *arg_info, uint32_t offset, bool required, char* indent)
{
smart_str_append_printf(str, "Parameter #%d [ ", offset);
if (!required) {
@@ -789,7 +789,7 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
smart_str_appends(str, "");
}
} else {
- zval *default_value = get_default_from_recv((zend_op_array*)fptr, offset);
+ const zval *default_value = get_default_from_recv((const zend_op_array*)fptr, offset);
if (default_value) {
smart_str_appends(str, " = ");
format_default_value(str, default_value);
@@ -801,7 +801,7 @@ static void _parameter_string(smart_str *str, zend_function *fptr, struct _zend_
/* }}} */
/* {{{ _function_parameter_string */
-static void _function_parameter_string(smart_str *str, zend_function *fptr, char* indent)
+static void _function_parameter_string(smart_str *str, const zend_function *fptr, char* indent)
{
struct _zend_arg_info *arg_info = fptr->common.arg_info;
uint32_t i, num_args, num_required = fptr->common.required_num_args;
@@ -855,7 +855,7 @@ static void _function_closure_string(smart_str *str, const zend_function *fptr,
/* }}} */
/* {{{ _function_string */
-static void _function_string(smart_str *str, zend_function *fptr, zend_class_entry *scope, const char* indent)
+static void _function_string(smart_str *str, const zend_function *fptr, const zend_class_entry *scope, const char* indent)
{
smart_str param_indent = {0};
zend_function *overwrites;
@@ -877,8 +877,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
if (fptr->common.fn_flags & ZEND_ACC_DEPRECATED) {
smart_str_appends(str, ", deprecated");
}
- if (fptr->type == ZEND_INTERNAL_FUNCTION && ((zend_internal_function*)fptr)->module) {
- smart_str_append_printf(str, ":%s", ((zend_internal_function*)fptr)->module->name);
+ if (fptr->type == ZEND_INTERNAL_FUNCTION && ((const zend_internal_function*)fptr)->module) {
+ smart_str_append_printf(str, ":%s", ((const zend_internal_function*)fptr)->module->name);
}
if (scope && fptr->common.scope) {
@@ -964,8 +964,8 @@ static void _function_string(smart_str *str, zend_function *fptr, zend_class_ent
}
/* }}} */
-static zval *property_get_default(zend_property_info *prop_info) {
- zend_class_entry *ce = prop_info->ce;
+static zval *property_get_default(const zend_property_info *prop_info) {
+ const zend_class_entry *ce = prop_info->ce;
if (prop_info->flags & ZEND_ACC_STATIC) {
zval *prop = &ce->default_static_members_table[prop_info->offset];
ZVAL_DEINDIRECT(prop);
@@ -978,7 +978,7 @@ static zval *property_get_default(zend_property_info *prop_info) {
}
/* {{{ _property_string */
-static void _property_string(smart_str *str, zend_property_info *prop, const char *prop_name, const char* indent)
+static void _property_string(smart_str *str, const zend_property_info *prop, const char *prop_name, const char* indent)
{
if (prop && prop->doc_comment) {
smart_str_append_printf(str, "%s%s\n", indent, ZSTR_VAL(prop->doc_comment));
@@ -1037,7 +1037,7 @@ static void _property_string(smart_str *str, zend_property_info *prop, const cha
}
smart_str_append_printf(str, "$%s", prop_name);
- zval *default_value = property_get_default(prop);
+ const zval *default_value = property_get_default(prop);
if (default_value && !Z_ISUNDEF_P(default_value)) {
smart_str_appends(str, " = ");
format_default_value(str, default_value);
@@ -1259,7 +1259,7 @@ static void reflection_attribute_factory(zval *object, HashTable *attributes, ze
/* }}} */
static zend_result read_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope,
- uint32_t offset, uint32_t target, zend_string *name, zend_class_entry *base, zend_string *filename) /* {{{ */
+ uint32_t offset, uint32_t target, zend_string *name, const zend_class_entry *base, zend_string *filename) /* {{{ */
{
ZEND_ASSERT(attributes != NULL);
@@ -1288,7 +1288,7 @@ static zend_result read_attributes(zval *ret, HashTable *attributes, zend_class_
if (base) {
// Base type filtering.
- zend_class_entry *ce = zend_lookup_class_ex(attr->name, attr->lcname, 0);
+ const zend_class_entry *ce = zend_lookup_class_ex(attr->name, attr->lcname, 0);
if (ce == NULL) {
// Bailout on error, otherwise ignore unavailable class.
@@ -1317,7 +1317,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
{
zend_string *name = NULL;
zend_long flags = 0;
- zend_class_entry *base = NULL;
+ const zend_class_entry *base = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!l", &name, &flags) == FAILURE) {
RETURN_THROWS();
@@ -1611,7 +1611,7 @@ static void reflection_class_constant_factory(zend_string *name_str, zend_class_
}
/* }}} */
-static void reflection_enum_case_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object)
+static void reflection_enum_case_factory(const zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object)
{
reflection_object *intern;
@@ -1628,7 +1628,7 @@ static void reflection_enum_case_factory(zend_class_entry *ce, zend_string *name
ZVAL_STR_COPY(reflection_prop_class(object), constant->ce->name);
}
-static zend_result get_parameter_default(zval *result, parameter_reference *param) {
+static zend_result get_parameter_default(zval *result, const parameter_reference *param) {
if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
if (param->fptr->common.fn_flags & ZEND_ACC_USER_ARG_INFO) {
/* We don't have a way to determine the default value for this case right now. */
@@ -1636,7 +1636,7 @@ static zend_result get_parameter_default(zval *result, parameter_reference *para
}
return zend_get_default_from_internal_arg_info(result, param->arg_info);
} else {
- zval *default_value = get_default_from_recv((zend_op_array *) param->fptr, param->offset);
+ zval *default_value = get_default_from_recv((const zend_op_array *) param->fptr, param->offset);
if (!default_value) {
return FAILURE;
}
@@ -2937,7 +2937,7 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueAvailable)
if (param->fptr->type == ZEND_INTERNAL_FUNCTION) {
RETURN_BOOL(param->arg_info->default_value);
} else {
- zval *default_value = get_default_from_recv((zend_op_array *)param->fptr, param->offset);
+ const zval *default_value = get_default_from_recv((const zend_op_array *)param->fptr, param->offset);
RETURN_BOOL(default_value != NULL);
}
}
@@ -2983,7 +2983,7 @@ ZEND_METHOD(ReflectionParameter, isDefaultValueConstant)
}
if (Z_TYPE(default_value) == IS_CONSTANT_AST) {
- zend_ast *ast = Z_ASTVAL(default_value);
+ const zend_ast *ast = Z_ASTVAL(default_value);
RETVAL_BOOL(ast->kind == ZEND_AST_CONSTANT
|| ast->kind == ZEND_AST_CONSTANT_CLASS
|| ast->kind == ZEND_AST_CLASS_CONST);
@@ -3023,8 +3023,8 @@ ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName)
} else if (ast->kind == ZEND_AST_CONSTANT_CLASS) {
RETVAL_STRINGL("__CLASS__", sizeof("__CLASS__")-1);
} else if (ast->kind == ZEND_AST_CLASS_CONST) {
- zend_string *class_name = zend_ast_get_str(ast->child[0]);
- zend_string *const_name = zend_ast_get_str(ast->child[1]);
+ const zend_string *class_name = zend_ast_get_str(ast->child[0]);
+ const zend_string *const_name = zend_ast_get_str(ast->child[1]);
RETVAL_NEW_STR(zend_string_concat3(
ZSTR_VAL(class_name), ZSTR_LEN(class_name),
"::", sizeof("::")-1,
@@ -3595,7 +3595,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, inNamespace)
RETURN_FALSE;
}
- zend_string *name = fptr->common.function_name;
+ const zend_string *name = fptr->common.function_name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
RETURN_BOOL(backslash);
}
@@ -3615,7 +3615,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getNamespaceName)
RETURN_EMPTY_STRING();
}
- zend_string *name = fptr->common.function_name;
+ const zend_string *name = fptr->common.function_name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
if (backslash) {
RETURN_STRINGL(ZSTR_VAL(name), backslash - ZSTR_VAL(name));
@@ -3986,7 +3986,7 @@ ZEND_METHOD(ReflectionClassConstant, getValue)
GET_REFLECTION_OBJECT_PTR(ref);
- zval *name = reflection_prop_name(ZEND_THIS);
+ const zval *name = reflection_prop_name(ZEND_THIS);
if (Z_ISUNDEF_P(name)) {
zend_throw_error(NULL,
"Typed property ReflectionClassConstant::$name "
@@ -5626,7 +5626,7 @@ ZEND_METHOD(ReflectionClass, inNamespace)
GET_REFLECTION_OBJECT_PTR(ce);
- zend_string *name = ce->name;
+ const zend_string *name = ce->name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
RETURN_BOOL(backslash);
}
@@ -5642,7 +5642,7 @@ ZEND_METHOD(ReflectionClass, getNamespaceName)
GET_REFLECTION_OBJECT_PTR(ce);
- zend_string *name = ce->name;
+ const zend_string *name = ce->name;
const char *backslash = zend_memrchr(ZSTR_VAL(name), '\\', ZSTR_LEN(name));
if (backslash) {
RETURN_STRINGL(ZSTR_VAL(name), backslash - ZSTR_VAL(name));
@@ -6017,7 +6017,7 @@ ZEND_METHOD(ReflectionProperty, setValue)
* The effective prop may add hooks or change flags. */
static zend_property_info *reflection_property_get_effective_prop(
zend_property_info *prop, zend_string *unmangled_name,
- zend_class_entry *scope, zend_object *object) {
+ const zend_class_entry *scope, const zend_object *object) {
if (scope != object->ce && !(prop && (prop->flags & ZEND_ACC_PRIVATE))) {
prop = zend_hash_find_ptr(&object->ce->properties_info, unmangled_name);
}
@@ -6052,7 +6052,7 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
}
}
- zend_property_info *prop = reflection_property_get_effective_prop(ref->prop,
+ const zend_property_info *prop = reflection_property_get_effective_prop(ref->prop,
ref->unmangled_name, intern->ce, Z_OBJ_P(object));
if (UNEXPECTED(prop && (prop->flags & ZEND_ACC_STATIC))) {
@@ -6085,7 +6085,7 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
static void zend_reflection_property_set_raw_value_ex(zend_property_info *prop,
zend_string *unmangled_name, void *cache_slot[3],
- zend_class_entry *scope, zend_object *object, zval *value)
+ const zend_class_entry *scope, zend_object *object, zval *value)
{
ZEND_ASSERT(!prop || !(prop->flags & ZEND_ACC_STATIC));
@@ -6102,7 +6102,7 @@ static void zend_reflection_property_set_raw_value_ex(zend_property_info *prop,
PHPAPI void zend_reflection_property_set_raw_value(zend_property_info *prop,
zend_string *unmangled_name, void *cache_slot[3],
- zend_class_entry *scope, zend_object *object, zval *value)
+ const zend_class_entry *scope, zend_object *object, zval *value)
{
prop = reflection_property_get_effective_prop(prop,
unmangled_name, scope, object);
@@ -6135,8 +6135,8 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
}
static zend_result reflection_property_check_lazy_compatible(
- zend_property_info *prop, zend_string *unmangled_name,
- zend_class_entry *scope, zend_object *object, const char *method)
+ const zend_property_info *prop, zend_string *unmangled_name,
+ const zend_class_entry *scope, const zend_object *object, const char *method)
{
if (!prop) {
zend_throw_exception_ex(reflection_exception_ptr, 0,
@@ -6178,7 +6178,7 @@ static zend_result reflection_property_check_lazy_compatible(
PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization(
zend_property_info *prop, zend_string *unmangled_name,
- void *cache_slot[3], zend_class_entry *scope,
+ void *cache_slot[3], const zend_class_entry *scope,
zend_object *object, zval *value)
{
while (zend_object_is_lazy_proxy(object)
@@ -6265,7 +6265,7 @@ ZEND_METHOD(ReflectionProperty, skipLazyInitialization)
object = zend_lazy_object_get_instance(object);
}
- zval *src = &object->ce->default_properties_table[OBJ_PROP_TO_NUM(ref->prop->offset)];
+ const zval *src = &object->ce->default_properties_table[OBJ_PROP_TO_NUM(ref->prop->offset)];
zval *dst = OBJ_PROP(object, ref->prop->offset);
if (!(Z_PROP_FLAG_P(dst) & IS_PROP_LAZY)) {
@@ -6316,7 +6316,6 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
reflection_object *intern;
property_reference *ref;
zval *object = NULL;
- zval *member_p = NULL;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
@@ -6326,7 +6325,7 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
GET_REFLECTION_OBJECT_PTR(ref);
if (prop_get_flags(ref) & ZEND_ACC_STATIC) {
- member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 1);
+ const zval *member_p = zend_read_static_property_ex(intern->ce, ref->unmangled_name, 1);
if (member_p) {
RETURN_BOOL(!Z_ISUNDEF_P(member_p));
}
@@ -6451,7 +6450,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType)
GET_REFLECTION_OBJECT_PTR(ref);
- zend_property_info *prop = ref->prop;
+ const zend_property_info *prop = ref->prop;
/* Dynamic property is untyped. */
if (!ref->prop) {
RETURN_NULL();
@@ -6466,7 +6465,7 @@ ZEND_METHOD(ReflectionProperty, getSettableType)
/* Extract set $value parameter type. */
if (prop->hooks && prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
- zend_arg_info *arg_info = &prop->hooks[ZEND_PROPERTY_HOOK_SET]->common.arg_info[0];
+ const zend_arg_info *arg_info = &prop->hooks[ZEND_PROPERTY_HOOK_SET]->common.arg_info[0];
if (!ZEND_TYPE_IS_SET(arg_info->type)) {
RETURN_NULL();
}
@@ -6500,20 +6499,18 @@ ZEND_METHOD(ReflectionProperty, hasDefaultValue)
{
reflection_object *intern;
property_reference *ref;
- zend_property_info *prop_info;
- zval *prop;
ZEND_PARSE_PARAMETERS_NONE();
GET_REFLECTION_OBJECT_PTR(ref);
- prop_info = ref->prop;
+ const zend_property_info *prop_info = ref->prop;
if (prop_info == NULL) {
RETURN_FALSE;
}
- prop = property_get_default(prop_info);
+ const zval *prop = property_get_default(prop_info);
RETURN_BOOL(prop && !Z_ISUNDEF_P(prop));
}
/* }}} */
@@ -6523,14 +6520,13 @@ ZEND_METHOD(ReflectionProperty, getDefaultValue)
{
reflection_object *intern;
property_reference *ref;
- zend_property_info *prop_info;
zval *prop;
ZEND_PARSE_PARAMETERS_NONE();
GET_REFLECTION_OBJECT_PTR(ref);
- prop_info = ref->prop;
+ const zend_property_info *prop_info = ref->prop;
if (prop_info == NULL) {
// Dynamic property
@@ -6694,7 +6690,7 @@ static zend_always_inline uint32_t set_visibility_to_visibility(uint32_t set_vis
}
}
-static bool check_visibility(uint32_t visibility, zend_class_entry *ce, zend_class_entry *scope)
+static bool check_visibility(uint32_t visibility, const zend_class_entry *ce, zend_class_entry *scope)
{
if (!(visibility & ZEND_ACC_PUBLIC) && (scope != ce)) {
if (!scope) {
@@ -6852,7 +6848,7 @@ ZEND_METHOD(ReflectionProperty, isWritable)
ref->unmangled_name, intern->ce, obj);
}
- zend_class_entry *ce = obj ? obj->ce : intern->ce;
+ const zend_class_entry *ce = obj ? obj->ce : intern->ce;
if (!prop) {
if (!(ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) {
RETURN_TRUE;
@@ -7026,7 +7022,7 @@ ZEND_METHOD(ReflectionExtension, getConstants)
/* }}} */
/* {{{ _addinientry */
-static void _addinientry(zend_ini_entry *ini_entry, zval *retval, int number)
+static void _addinientry(const zend_ini_entry *ini_entry, const zval *retval, int number)
{
if (number == ini_entry->module_number) {
zval zv;
@@ -7058,7 +7054,7 @@ ZEND_METHOD(ReflectionExtension, getINIEntries)
/* }}} */
/* {{{ add_extension_class */
-static void add_extension_class(zend_class_entry *ce, zend_string *key, zval *class_array, zend_module_entry *module, bool add_reflection_class)
+static void add_extension_class(zend_class_entry *ce, zend_string *key, zval *class_array, const zend_module_entry *module, bool add_reflection_class)
{
if (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module && !strcasecmp(ce->info.internal.module->name, module->name)) {
zend_string *name;
@@ -7352,7 +7348,7 @@ ZEND_METHOD(ReflectionReference, __construct)
}
/* }}} */
-static bool is_ignorable_reference(HashTable *ht, zval *ref) {
+static bool is_ignorable_reference(const HashTable *ht, const zval *ref) {
if (Z_REFCOUNT_P(ref) != 1) {
return false;
}
@@ -7676,7 +7672,7 @@ ZEND_METHOD(ReflectionEnum, hasCase)
GET_REFLECTION_OBJECT_PTR(ce);
- zend_class_constant *class_const = zend_hash_find_ptr(&ce->constants_table, name);
+ const zend_class_constant *class_const = zend_hash_find_ptr(&ce->constants_table, name);
if (class_const == NULL) {
RETURN_FALSE;
}
@@ -7771,7 +7767,7 @@ ZEND_METHOD(ReflectionEnumUnitCase, __construct)
GET_REFLECTION_OBJECT_PTR(ref);
if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE)) {
- zval *case_name = reflection_prop_name(ZEND_THIS);
+ const zval *case_name = reflection_prop_name(ZEND_THIS);
zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name));
RETURN_THROWS();
}
@@ -7801,7 +7797,7 @@ ZEND_METHOD(ReflectionEnumBackedCase, __construct)
GET_REFLECTION_OBJECT_PTR(ref);
if (ref->ce->enum_backing_type == IS_UNDEF) {
- zval *case_name = reflection_prop_name(ZEND_THIS);
+ const zval *case_name = reflection_prop_name(ZEND_THIS);
zend_throw_exception_ex(reflection_exception_ptr, 0, "Enum case %s::%s is not a backed case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name));
RETURN_THROWS();
}
@@ -7823,7 +7819,7 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue)
}
ZEND_ASSERT(intern->ce->enum_backing_type != IS_UNDEF);
- zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value));
+ const zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value));
ZVAL_COPY_OR_DUP(return_value, member_p);
}
@@ -7894,7 +7890,7 @@ ZEND_METHOD(ReflectionFiber, getTrace)
ZEND_METHOD(ReflectionFiber, getExecutingLine)
{
- zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
+ const zend_fiber *fiber = (const zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
zend_execute_data *prev_execute_data;
ZEND_PARSE_PARAMETERS_NONE();
@@ -7918,7 +7914,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingLine)
ZEND_METHOD(ReflectionFiber, getExecutingFile)
{
- zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
+ const zend_fiber *fiber = (const zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
zend_execute_data *prev_execute_data;
ZEND_PARSE_PARAMETERS_NONE();
@@ -7942,7 +7938,7 @@ ZEND_METHOD(ReflectionFiber, getExecutingFile)
ZEND_METHOD(ReflectionFiber, getCallable)
{
- zend_fiber *fiber = (zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
+ const zend_fiber *fiber = (const zend_fiber *) Z_OBJ(Z_REFLECTION_P(ZEND_THIS)->obj);
ZEND_PARSE_PARAMETERS_NONE();
diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h
index 51f48b8039cc..93b53de74133 100644
--- a/ext/reflection/php_reflection.h
+++ b/ext/reflection/php_reflection.h
@@ -62,13 +62,13 @@ PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object);
*/
PHPAPI void zend_reflection_property_set_raw_value(
zend_property_info *prop, zend_string *unmangled_name,
- void *cache_slot[3], zend_class_entry *scope,
+ void *cache_slot[3], const zend_class_entry *scope,
zend_object *object, zval *value);
/* Same as zend_reflection_property_set_raw_value(), but skips lazy object initialization. */
PHPAPI void zend_reflection_property_set_raw_value_without_lazy_initialization(
zend_property_info *prop, zend_string *unmangled_name,
- void *cache_slot[3], zend_class_entry *scope,
+ void *cache_slot[3], const zend_class_entry *scope,
zend_object *object, zval *value);
END_EXTERN_C()
diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c
index 86652ae4f5cc..6cbabff26a36 100644
--- a/ext/spl/spl_iterators.c
+++ b/ext/spl/spl_iterators.c
@@ -266,6 +266,7 @@ static void spl_recursive_it_move_forward_ex(spl_recursive_it_object *object, zv
zend_clear_exception();
}
}
+ iterator = object->iterators[object->level].iterator;
ZEND_FALLTHROUGH;
case RS_START:
if (iterator->funcs->valid(iterator) == FAILURE) {
diff --git a/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt b/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt
new file mode 100644
index 000000000000..3c9863e81a30
--- /dev/null
+++ b/ext/spl/tests/recursiveiteratoriterator_rewind_during_next.phpt
@@ -0,0 +1,41 @@
+--TEST--
+RecursiveIteratorIterator: rewind() re-entered from an inner next() must not use-after-free
+--FILE--
+data = $d; $this->depth = $depth; }
+ function current(): mixed { return $this->data[$this->pos] ?? null; }
+ function key(): mixed { return $this->pos; }
+ function next(): void {
+ $this->pos++;
+ if ($this->rii && $this->depth === 1 && $this->pos === 1 && !self::$fired) {
+ self::$fired = true;
+ $this->rii->rewind();
+ }
+ }
+ function rewind(): void { $this->pos = 0; }
+ function valid(): bool { return $this->pos < count($this->data); }
+ function hasChildren(): bool { return is_array($this->current()); }
+ function getChildren(): RecursiveIterator {
+ $c = new Reenter($this->current(), $this->depth + 1);
+ $c->rii = $this->rii;
+ return $c;
+ }
+}
+$root = new Reenter([[10, 11], [20, 21]]);
+$rii = new RecursiveIteratorIterator($root, RecursiveIteratorIterator::SELF_FIRST);
+$root->rii = $rii;
+$seen = [];
+foreach ($rii as $v) {
+ if (is_array($v)) { $v = '[' . implode(',', $v) . ']'; }
+ $seen[] = $v;
+ if (count($seen) > 20) { $seen[] = '...'; break; }
+}
+echo implode(' ', $seen), "\n";
+echo "done\n";
+?>
+--EXPECT--
+[10,11] 10 [10,11] 10 11 [20,21] 20 21
+done