Commit 7a5e452f14c for php.net

commit 7a5e452f14ce2e4719d6e86586652a2fad8c3697
Author: Gina Peter Banyard <girgias@php.net>
Date:   Thu Aug 13 14:34:46 2026 +0100

    Zend: use zend_object* for this_ptr

    Instead of a zval

diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index d421bdbc8eb..4aa46731590 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -35,7 +35,7 @@
 typedef struct _zend_closure {
 	zend_object       std;
 	zend_function     func;
-	zval              this_ptr;
+	zend_object      *this_ptr;
 	zend_class_entry *called_scope;
 	zif_handler       orig_internal_handler;
 } zend_closure;
@@ -115,7 +115,7 @@ static bool zend_valid_closure_binding(
 			&& !(func->common.fn_flags & ZEND_ACC_STATIC)) {
 		zend_error(E_WARNING, "Cannot unbind $this of method, this will be an error in PHP 9");
 		return false;
-	} else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr)
+	} else if (!is_fake_closure && closure->this_ptr
 			&& (func->common.fn_flags & ZEND_ACC_USES_THIS)) {
 		zend_error(E_WARNING, "Cannot unbind $this of closure using $this, this will be an error in PHP 9");
 		return false;
@@ -197,7 +197,7 @@ ZEND_METHOD(Closure, call)
 		fake_closure->std.gc.refcount = 1;
 		fake_closure->std.gc.u.type_info = GC_NULL;
 		fake_closure->std.extra_flags = zend_closure_flags(closure);
-		ZVAL_UNDEF(&fake_closure->this_ptr);
+		fake_closure->this_ptr = NULL;
 		fake_closure->called_scope = NULL;
 		my_function = &fake_closure->func;
 		if (ZEND_USER_CODE(closure->func.type)) {
@@ -510,11 +510,7 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
 		return ZEND_UNCOMPARABLE;
 	}

-	if (Z_TYPE(lhs->this_ptr) != Z_TYPE(rhs->this_ptr)) {
-		return ZEND_UNCOMPARABLE;
-	}
-
-	if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) {
+	if (lhs->this_ptr != rhs->this_ptr) {
 		return ZEND_UNCOMPARABLE;
 	}

@@ -574,10 +570,10 @@ ZEND_API const zend_function *zend_get_closure_method_def(zend_object *obj) /* {
 }
 /* }}} */

-ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */
+ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj) /* {{{ */
 {
 	zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
-	return &closure->this_ptr;
+	return closure->this_ptr;
 }
 /* }}} */

@@ -607,8 +603,8 @@ static void zend_closure_free_storage(zend_object *object) /* {{{ */
 		zend_string_release(closure->func.common.function_name);
 	}

-	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
-		zval_ptr_dtor(&closure->this_ptr);
+	if (closure->this_ptr) {
+		OBJ_RELEASE(closure->this_ptr);
 	}
 }
 /* }}} */
@@ -633,7 +629,7 @@ static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */

 	zend_create_closure_ex(&result, &closure->func,
 		closure->func.common.scope, closure->called_scope,
-		Z_ISUNDEF(closure->this_ptr) ? NULL : Z_OBJ(closure->this_ptr),
+		closure->this_ptr,
 		zend_closure_is_fake(closure), zend_closure_flags(closure));
 	return Z_OBJ(result);
 }
@@ -645,12 +641,7 @@ static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry *

 	*fptr_ptr = &closure->func;
 	*ce_ptr = closure->called_scope;
-
-	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
-		*obj_ptr = Z_OBJ(closure->this_ptr);
-	} else {
-		*obj_ptr = NULL;
-	}
+	*obj_ptr = closure->this_ptr;

 	return SUCCESS;
 }
@@ -718,9 +709,10 @@ static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp)
 		}
 	}

-	if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
-		Z_ADDREF(closure->this_ptr);
-		zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr);
+	if (closure->this_ptr) {
+		zval tmp;
+		ZVAL_OBJ_COPY(&tmp, closure->this_ptr);
+		zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &tmp);
 	}

 	if (arg_info &&
@@ -757,8 +749,15 @@ static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /*
 {
 	zend_closure *closure = (zend_closure *)obj;

-	*table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
-	*n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
+	if (closure->this_ptr) {
+		zend_get_gc_buffer *gc_buffer = zend_get_gc_buffer_create();
+		zend_get_gc_buffer_add_obj(gc_buffer, closure->this_ptr);
+		zend_get_gc_buffer_use(gc_buffer, table, n);
+	} else {
+		*table = NULL;
+		*n = 0;
+	}
+
 	/* Fake closures don't own the static variables they reference. */
 	return (closure->func.type == ZEND_USER_FUNCTION
 			&& !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ?
@@ -894,7 +893,7 @@ static void zend_create_closure_ex(
 		}
 	}

-	ZVAL_UNDEF(&closure->this_ptr);
+	closure->this_ptr = NULL;
 	/* Invariant:
 	 * If the closure is unscoped or static, it has no bound object. */
 	closure->func.common.scope = scope;
@@ -902,7 +901,8 @@ static void zend_create_closure_ex(
 	if (scope) {
 		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
 		if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
-			ZVAL_OBJ_COPY(&closure->this_ptr, this_ptr);
+			closure->this_ptr = this_ptr;
+			GC_ADDREF(this_ptr);
 		}
 	}
 }
@@ -924,7 +924,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_clas

 	closure = (zend_closure *)Z_OBJ_P(res);
 	closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
-	if (Z_TYPE(closure->this_ptr) != IS_OBJECT) {
+	if (!closure->this_ptr) {
 		GC_ADD_FLAGS(&closure->std, GC_NOT_COLLECTABLE);
 	}
 }
diff --git a/Zend/zend_closures.h b/Zend/zend_closures.h
index 969594e900e..c421c100833 100644
--- a/Zend/zend_closures.h
+++ b/Zend/zend_closures.h
@@ -39,7 +39,7 @@ ZEND_API void zend_create_fake_closure(zval *res, zend_function *op_array, zend_
 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);
+ZEND_API zend_object* zend_get_closure_this_ptr(zval *obj);

 END_EXTERN_C()

diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c
index 2bbd7b0e3f4..9849bda4cd2 100644
--- a/ext/opcache/jit/zend_jit_ir.c
+++ b/ext/opcache/jit/zend_jit_ir.c
@@ -8453,7 +8453,7 @@ static int zend_jit_isset_isempty_cv(zend_jit_ctx *jit, const zend_op *opline, u
 typedef struct _zend_closure {
 	zend_object       std;
 	zend_function     func;
-	zval              this_ptr;
+	zend_object      *this_ptr;
 	zend_class_entry *called_scope;
 	zif_handler       orig_internal_handler;
 } zend_closure;
@@ -8708,17 +8708,17 @@ static int zend_jit_push_call_frame(zend_jit_ctx *jit, const zend_op *opline, co
 			ir_AND_U32(
 				ir_LOAD_U32(ir_ADD_OFFSET(func_ref, offsetof(zend_closure, func.common.fn_flags))),
 				ir_CONST_U32(ZEND_ACC_FAKE_CLOSURE)),
-			ir_CONST_U32(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE));
-		// JIT: if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
-		if_cond = ir_IF(ir_LOAD_U8(ir_ADD_OFFSET(func_ref, offsetof(zend_closure, this_ptr.u1.v.type))));
+				ir_CONST_U32(ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_CLOSURE));
+
+		// JIT: object_or_called_scope = closure->this_ptr;
+		object = ir_LOAD_A(ir_ADD_OFFSET(func_ref, offsetof(zend_closure, this_ptr)));
+		// JIT: if (closure->this_ptr != NULL) {
+		if_cond = ir_IF(object);
 		ir_IF_TRUE(if_cond);

 		// JIT: call_info |= ZEND_CALL_HAS_THIS;
 		call_info2 = ir_OR_U32(call_info, ir_CONST_U32(ZEND_CALL_HAS_THIS));

-		// JIT: object_or_called_scope = Z_OBJ(closure->this_ptr);
-		object = ir_LOAD_A(ir_ADD_OFFSET(func_ref, offsetof(zend_closure, this_ptr.value.ptr)));
-
 		ir_MERGE_WITH_EMPTY_FALSE(if_cond);
 		call_info = ir_PHI_2(IR_U32, call_info2, call_info);
 		object_or_called_scope = ir_PHI_2(IR_ADDR, object, object_or_called_scope);
diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c
index 3f65acb68c1..1accd39e257 100644
--- a/ext/reflection/php_reflection.c
+++ b/ext/reflection/php_reflection.c
@@ -1772,9 +1772,9 @@ ZEND_METHOD(ReflectionFunctionAbstract, getClosureThis)

 	GET_REFLECTION_OBJECT();
 	if (!Z_ISUNDEF(intern->obj)) {
-		zval *closure_this = zend_get_closure_this_ptr(&intern->obj);
-		if (!Z_ISUNDEF_P(closure_this)) {
-			RETURN_OBJ_COPY(Z_OBJ_P(closure_this));
+		zend_object *closure_this = zend_get_closure_this_ptr(&intern->obj);
+		if (closure_this) {
+			RETURN_OBJ_COPY(closure_this);
 		}
 	}
 }