Commit fbb2e1f23d6 for php.net

commit fbb2e1f23d624503e823b43137be17846f211079
Author: Gina Peter Banyard <girgias@php.net>
Date:   Thu Aug 13 14:16:31 2026 +0100

    Zend: pass $this as a zend_object*

    Instead of a zval*, this is in preparation for converting the this_ptr field of zend_closure to zend_object*

diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 91f690c73ed..d421bdbc8eb 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -45,7 +45,7 @@ ZEND_API zend_class_entry *zend_ce_closure;
 static zend_object_handlers closure_handlers;

 static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only);
-static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags);
+static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool is_fake, uint32_t flags);

 static inline uint32_t zend_closure_flags(const zend_closure *closure)
 {
@@ -90,25 +90,25 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
 /* }}} */

 static bool zend_valid_closure_binding(
-		zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
+		zend_closure *closure, zend_object *new_this, zend_class_entry *scope) /* {{{ */
 {
 	zend_function *func = &closure->func;
 	// TODO: rename variable
 	bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0
 		|| (closure->std.extra_flags & ZEND_PARTIAL);
-	if (newthis) {
+	if (new_this) {
 		if (func->common.fn_flags & ZEND_ACC_STATIC) {
 			zend_error(E_WARNING, "Cannot bind an instance to a static closure, this will be an error in PHP 9");
 			return false;
 		}

 		if (is_fake_closure && func->common.scope &&
-				!instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
+				!instanceof_function(new_this->ce, func->common.scope)) {
 			/* Binding incompatible $this to an internal method is not supported. */
 			zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s, this will be an error in PHP 9",
 					ZSTR_VAL(func->common.scope->name),
 					ZSTR_VAL(func->common.function_name),
-					ZSTR_VAL(Z_OBJCE_P(newthis)->name));
+					ZSTR_VAL(new_this->ce->name));
 			return false;
 		}
 	} else if (is_fake_closure && func->common.scope
@@ -144,32 +144,31 @@ static bool zend_valid_closure_binding(
 /* {{{ Call closure, binding to a given object with its class as the scope */
 ZEND_METHOD(Closure, call)
 {
-	zval *newthis, closure_result;
+	zval closure_result;
 	zend_closure *closure;
 	zend_fcall_info fci;
 	zend_fcall_info_cache fci_cache;
-	zend_object *newobj;
+	zend_object *new_this;
 	zend_class_entry *newclass;

 	fci.param_count = 0;
 	fci.params = NULL;

 	ZEND_PARSE_PARAMETERS_START(1, -1)
-		Z_PARAM_OBJECT(newthis)
+		Z_PARAM_OBJ(new_this)
 		Z_PARAM_VARIADIC_WITH_NAMED(fci.params, fci.param_count, fci.named_params)
 	ZEND_PARSE_PARAMETERS_END();

 	closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);

-	newobj = Z_OBJ_P(newthis);
-	newclass = newobj->ce;
+	newclass = new_this->ce;

-	if (!zend_valid_closure_binding(closure, newthis, newclass)) {
+	if (!zend_valid_closure_binding(closure, new_this, newclass)) {
 		return;
 	}

 	fci_cache.called_scope = newclass;
-	fci_cache.object = fci.object = newobj;
+	fci_cache.object = fci.object = new_this;

 	fci.size = sizeof(fci);
 	fci.consumed_args = 0;
@@ -180,7 +179,7 @@ ZEND_METHOD(Closure, call)
 	if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
 		zval new_closure;
 		zend_create_closure_ex(&new_closure, &closure->func, newclass,
-				closure->called_scope, newthis,
+				closure->called_scope, new_this,
 				zend_closure_is_fake(closure), zend_closure_flags(closure));
 		closure = (zend_closure *) Z_OBJ(new_closure);
 		fci_cache.function_handler = &closure->func;
@@ -244,7 +243,7 @@ ZEND_METHOD(Closure, call)
 }
 /* }}} */

-static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str)
+static zend_result do_closure_bind(zval *return_value, zval *zclosure, zend_object *new_this, zend_object *scope_obj, zend_string *scope_str)
 {
 	zend_class_entry *ce, *called_scope;
 	zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure);
@@ -263,17 +262,17 @@ static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *new
 		ce = NULL;
 	}

-	if (!zend_valid_closure_binding(closure, newthis, ce)) {
+	if (!zend_valid_closure_binding(closure, new_this, ce)) {
 		return FAILURE;
 	}

-	if (newthis) {
-		called_scope = Z_OBJCE_P(newthis);
+	if (new_this) {
+		called_scope = new_this->ce;
 	} else {
 		called_scope = ce;
 	}

-	zend_create_closure_ex(return_value, &closure->func, ce, called_scope, newthis,
+	zend_create_closure_ex(return_value, &closure->func, ce, called_scope, new_this,
 		zend_closure_is_fake(closure), zend_closure_flags(closure));

 	if (zend_closure_flags(closure) & ZEND_PARTIAL_OF_CLOSURE) {
@@ -286,7 +285,7 @@ static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *new
 		ZEND_ASSERT(Z_TYPE_P(inner) == IS_OBJECT && Z_OBJCE_P(inner) == zend_ce_closure);

 		zval new_inner;
-		if (do_closure_bind(&new_inner, inner, newthis, scope_obj, scope_str) != SUCCESS) {
+		if (do_closure_bind(&new_inner, inner, new_this, scope_obj, scope_str) != SUCCESS) {
 			/* Should not happen, as we have already validated arguments and the
 			 * inner closure should have the same constraints. */
 			ZEND_UNREACHABLE();
@@ -306,34 +305,35 @@ static zend_result do_closure_bind(zval *return_value, zval *zclosure, zval *new
 /* {{{ Create a closure from another one and bind to another object and scope */
 ZEND_METHOD(Closure, bind)
 {
-	zval *zclosure, *newthis;
+	zval *zclosure;
+	zend_object *new_this;
 	zend_object *scope_obj = NULL;
 	zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);

 	ZEND_PARSE_PARAMETERS_START(2, 3)
 		Z_PARAM_OBJECT_OF_CLASS(zclosure, zend_ce_closure)
-		Z_PARAM_OBJECT_OR_NULL(newthis)
+		Z_PARAM_OBJ_OR_NULL(new_this)
 		Z_PARAM_OPTIONAL
 		Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
 	ZEND_PARSE_PARAMETERS_END();

-	do_closure_bind(return_value, zclosure, newthis, scope_obj, scope_str);
+	do_closure_bind(return_value, zclosure, new_this, scope_obj, scope_str);
 }

 /* {{{ Create a closure from another one and bind to another object and scope */
 ZEND_METHOD(Closure, bindTo)
 {
-	zval *newthis;
+	zend_object *new_this;
 	zend_object *scope_obj = NULL;
 	zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);

 	ZEND_PARSE_PARAMETERS_START(1, 2)
-		Z_PARAM_OBJECT_OR_NULL(newthis)
+		Z_PARAM_OBJ_OR_NULL(new_this)
 		Z_PARAM_OPTIONAL
 		Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
 	ZEND_PARSE_PARAMETERS_END();

-	do_closure_bind(return_value, ZEND_THIS, newthis, scope_obj, scope_str);
+	do_closure_bind(return_value, ZEND_THIS, new_this, scope_obj, scope_str);
 }

 static void zend_copy_parameters_array(const uint32_t param_count, HashTable *argument_array) /* {{{ */
@@ -396,7 +396,6 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
 static zend_result zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ {
 	zend_fcall_info_cache fcc;
 	zend_function *mptr;
-	zval instance;
 	zend_internal_function call;

 	if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) {
@@ -439,12 +438,7 @@ static zend_result zend_create_closure_from_callable(zval *return_value, zval *c
 		mptr = (zend_function *) &call;
 	}

-	if (fcc.object) {
-		ZVAL_OBJ(&instance, fcc.object);
-		zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, &instance);
-	} else {
-		zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, NULL);
-	}
+	zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, fcc.object);

 	if (&mptr->internal_function == &call) {
 		zend_string_release(mptr->common.function_name);
@@ -638,7 +632,8 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
 	zval result;

 	zend_create_closure_ex(&result, &closure->func,
-		closure->func.common.scope, closure->called_scope, &closure->this_ptr,
+		closure->func.common.scope, closure->called_scope,
+		Z_ISUNDEF(closure->this_ptr) ? NULL : Z_OBJ(closure->this_ptr),
 		zend_closure_is_fake(closure), zend_closure_flags(closure));
 	return Z_OBJ(result);
 }
@@ -807,7 +802,10 @@ static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
 }
 /* }}} */

-static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake, uint32_t flags) /* {{{ */
+static void zend_create_closure_ex(
+	zval *res, zend_function *func,
+	zend_class_entry *scope, zend_class_entry *called_scope,
+	zend_object *this_ptr, bool is_fake, uint32_t flags) /* {{{ */
 {
 	zend_closure *closure;
 	void *ptr;
@@ -817,7 +815,7 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
 	closure = (zend_closure *)Z_OBJ_P(res);
 	closure->std.extra_flags = flags;

-	if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
+	if ((scope == NULL) && this_ptr) {
 		/* use dummy scope if we're binding an object without specifying a scope */
 		/* maybe it would be better to create one for this purpose */
 		scope = zend_ce_closure;
@@ -903,21 +901,21 @@ static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_en
 	closure->called_scope = called_scope;
 	if (scope) {
 		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
-		if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
-			ZVAL_OBJ_COPY(&closure->this_ptr, Z_OBJ_P(this_ptr));
+		if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
+			ZVAL_OBJ_COPY(&closure->this_ptr, this_ptr);
 		}
 	}
 }
 /* }}} */

-ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
+ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr)
 {
 	zend_create_closure_ex(res, func, scope, called_scope, this_ptr,
 		/* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0,
 		/* flags */ 0);
 }

-ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
+ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr) /* {{{ */
 {
 	zend_closure *closure;

@@ -932,7 +930,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas
 }
 /* }}} */

-ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure)
+ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool partial_of_closure)
 {
 	uint32_t flags = ZEND_PARTIAL;
 	if (partial_of_closure) {
@@ -943,7 +941,6 @@ ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_c
 }

 void zend_closure_from_frame(zval *return_value, const zend_execute_data *call) { /* {{{ */
-	zval instance;
 	zend_internal_function trampoline;
 	zend_function *mptr = call->func;

@@ -974,9 +971,7 @@ void zend_closure_from_frame(zval *return_value, const zend_execute_data *call)
 	}

 	if (ZEND_CALL_INFO(call) & ZEND_CALL_HAS_THIS) {
-		ZVAL_OBJ(&instance, Z_OBJ(call->This));
-
-		zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE(instance), &instance);
+		zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE(call->This), Z_OBJ(call->This));
 	} else {
 		zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_CE(call->This), NULL);
 	}
diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h
index 305d82e5015..969594e900e 100644
--- a/Zend/zend_closures.h
+++ b/Zend/zend_closures.h
@@ -34,9 +34,9 @@ void zend_closure_from_frame(zval *closure_zv, const zend_execute_data *frame);

 extern ZEND_API zend_class_entry *zend_ce_closure;

-ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr);
-ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr);
-ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool partial_of_closure);
+ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr);
+ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr);
+ZEND_API void zend_create_partial_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zend_object *this_ptr, bool partial_of_closure);
 ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *obj);
 ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj);
 ZEND_API zval* zend_get_closure_this_ptr(zval *obj);
diff --git a/Zend/zend_partial.c b/Zend/zend_partial.c
index ec60383a2bc..b50512c238f 100644
--- a/Zend/zend_partial.c
+++ b/Zend/zend_partial.c
@@ -1125,7 +1125,7 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
 	}

 	zend_class_entry *called_scope;
-	zval object;
+	zend_object *object;

 	if (Z_TYPE_P(this_ptr) == IS_OBJECT) {
 		called_scope = Z_OBJCE_P(this_ptr);
@@ -1134,13 +1134,13 @@ void zend_partial_create(zval *result, zval *this_ptr, zend_function *function,
 	}

 	if (Z_TYPE_P(this_ptr) == IS_OBJECT && !zp_is_static_closure(function)) {
-		ZVAL_COPY_VALUE(&object, this_ptr);
+		object = Z_OBJ_P(this_ptr);
 	} else {
-		ZVAL_UNDEF(&object);
+		object = NULL;
 	}

 	zend_create_partial_closure(result, (zend_function*)op_array,
-			function->common.scope, called_scope, &object,
+			function->common.scope, called_scope, object,
 			(function->common.fn_flags & ZEND_ACC_CLOSURE) != 0);

 	zp_bind(result, function, argc, argv, extra_named_params, const_args);
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index cf8072646ee..01131b5d3ae 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -8413,7 +8413,7 @@ ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT)
 {
 	USE_OPLINE
 	zend_function *func;
-	zval *object;
+	zend_object *object;
 	zend_class_entry *called_scope;

 	if (opline->extended_value != (uint32_t)-1) {
@@ -8431,7 +8431,7 @@ ZEND_VM_HANDLER(142, ZEND_DECLARE_LAMBDA_FUNCTION, UNUSED, NUM, NUM|CACHE_SLOT)
 				(EX(func)->common.fn_flags & ZEND_ACC_STATIC))) {
 			object = NULL;
 		} else {
-			object = &EX(This);
+			object = Z_OBJ(EX(This));
 		}
 	} else {
 		called_scope = Z_CE(EX(This));
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 58cd0ddf916..5061d772ee8 100644
Binary files a/Zend/zend_vm_execute.h and b/Zend/zend_vm_execute.h differ
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 07a671739f3..3f65acb68c1 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -3334,7 +3334,7 @@ ZEND_METHOD(ReflectionMethod, getClosure)
 	{
 		RETURN_OBJ_COPY(Z_OBJ_P(obj));
 	}
-	zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE_P(obj), obj);
+	zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE_P(obj), Z_OBJ_P(obj));
 }
 /* }}} */