Commit f2f63ce5d17 for php.net

commit f2f63ce5d17df45f14e3960d7a79485337806fc9
Author: Gina Peter Banyard <girgias@php.net>
Date:   Sat Sep 19 16:03:14 2026 +0100

    tree: Drop FCI.object assignment if we have an FCC (#23739)

    If we have a valid FCC there is no need to assign to the FCI.object field as it is only used for callability checks in conjunction with the FCI.function_namefield.
    Moreover, in many cases we can use an engine API to set up the structs for us instead of doing it manually.

    This in preparation for the removal of the FCI.object field in the future.

diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index 4aa46731590..788957d1c3a 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -168,7 +168,7 @@ ZEND_METHOD(Closure, call)
 	}

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

 	fci.size = sizeof(fci);
 	fci.consumed_args = 0;
@@ -384,7 +384,7 @@ static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
 		ZVAL_EMPTY_ARRAY(&fci.params[1]);
 	}

-	fcc.object = fci.object = Z_OBJ_P(ZEND_THIS);
+	fcc.object = Z_OBJ_P(ZEND_THIS);
 	fcc.called_scope = zend_get_called_scope(EG(current_execute_data));

 	zend_call_function(&fci, &fcc);
diff --git a/ext/ffi/ffi.c b/ext/ffi/ffi.c
index c3c08327fbe..518a9d9c47f 100644
--- a/ext/ffi/ffi.c
+++ b/ext/ffi/ffi.c
@@ -943,7 +943,6 @@ static void zend_ffi_callback_trampoline(ffi_cif* cif, void* ret, void** args, v
 	ZVAL_UNDEF(&fci.function_name);
 	fci.retval = &retval;
 	fci.params = do_alloca(sizeof(zval) *callback_data->arg_count, use_heap);
-	fci.object = NULL;
 	fci.param_count = callback_data->arg_count;
 	fci.named_params = NULL;
 	fci.consumed_args = 0;
diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c
index 43a1a8008d7..a7a41168757 100644
--- a/ext/pdo/pdo_stmt.c
+++ b/ext/pdo/pdo_stmt.c
@@ -634,23 +634,12 @@ static bool pdo_do_key_pair_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation o
 }

 /* Return value MUST be an initialized object */
-static bool pdo_call_fetch_object_constructor(zend_function *constructor, HashTable *ctor_args, zval *return_value)
+static bool pdo_call_fetch_object_constructor(zend_function *constructor, HashTable *ctor_args, zend_object *this_ptr)
 {
 	zval retval_constructor_call;
-	zend_fcall_info fci = { 0 };
-	fci.size = sizeof(zend_fcall_info);
-	fci.object = Z_OBJ_P(return_value);
-	fci.retval = &retval_constructor_call;
-	fci.named_params = ctor_args;
-	zend_fcall_info_cache fcc = {
-		.function_handler = constructor,
-		.object = Z_OBJ_P(return_value),
-		.called_scope = Z_OBJCE_P(return_value),
-		.calling_scope = NULL,
-		.closure = NULL,
-	};
-
-	zend_call_function(&fci, &fcc);
+
+	zend_call_known_function(constructor, this_ptr, this_ptr->ce, &retval_constructor_call, 0, NULL, ctor_args);
+
 	bool failed = Z_ISUNDEF(retval_constructor_call);
 	zval_ptr_dtor(&retval_constructor_call);

@@ -784,7 +773,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h
 					goto in_fetch_error;
 				}
 				if (ce->constructor && (flags & PDO_FETCH_PROPS_LATE)) {
-					bool failed = pdo_call_fetch_object_constructor(ce->constructor, ctor_arguments, return_value);
+					bool failed = pdo_call_fetch_object_constructor(ce->constructor, ctor_arguments, Z_OBJ_P(return_value));
 					if (UNEXPECTED(failed)) {
 						zval_ptr_dtor(return_value);
 						goto in_fetch_error;
@@ -922,7 +911,7 @@ static bool do_fetch(pdo_stmt_t *stmt, zval *return_value, enum pdo_fetch_type h

 	/* Run constructor for objects if not already run and not unserialized */
 	if (how == PDO_FETCH_CLASS && ce->constructor && !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
-		bool failed = pdo_call_fetch_object_constructor(ce->constructor, ctor_arguments, return_value);
+		bool failed = pdo_call_fetch_object_constructor(ce->constructor, ctor_arguments, Z_OBJ_P(return_value));
 		if (UNEXPECTED(failed)) {
 			zval_ptr_dtor(return_value);
 			goto in_fetch_error;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 1fb8793c2bb..0138471aa63 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -265,27 +265,15 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
 			if (Z_EXTRA_P(zv) == VAR_WAKEUP_FLAG) {
 				/* Perform delayed __wakeup calls */
 				if (!delayed_call_failed) {
-					zval retval;
-					zend_fcall_info fci;
-					zend_fcall_info_cache fci_cache;
-
 					ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT);

-					fci.size = sizeof(fci);
-					fci.object = Z_OBJ_P(zv);
-					fci.retval = &retval;
-					fci.param_count = 0;
-					fci.params = NULL;
-					fci.named_params = NULL;
-					ZVAL_UNDEF(&fci.function_name);
-
-					fci_cache.function_handler = zend_hash_find_ptr(
-						&fci.object->ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP));
-					fci_cache.object = fci.object;
-					fci_cache.called_scope = fci.object->ce;
+					zend_function *fn = zend_hash_find_ptr(&Z_OBJCE_P(zv)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP));
+					zval retval;

 					BG(serialize_lock)++;
-					if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) {
+					zend_call_known_instance_method(fn, Z_OBJ_P(zv), &retval, 0, NULL);
+					/* Exception thrown */
+					if (Z_ISUNDEF(retval)) {
 						delayed_call_failed = 1;
 						GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED);
 					}